[
  {
    "path": ".editorconfig",
    "content": "# http://editorconfig.org\nroot = true\n\n# 空格替代Tab缩进在各种编辑工具下效果一致\n[*]\nindent_style = space\nindent_size = 4\ncharset = utf-8\nend_of_line = lf\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.{json,yml,yaml}]\nindent_size = 2\n\n[*.md]\ninsert_final_newline = false\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".gitee/ISSUE_TEMPLATE.zh-CN.md",
    "content": "### 使用版本(未按照模板填写直接删除)\n\n- jdk版本(带上尾号): 例如 1.8.0_202\n- 框架版本(项目启动时输出的版本号): 例如 4.4.0\n- 其他依赖版本(你觉得有必要的):\n\n### 问题前提\n\n> 功能不好用 不会用 是否已经看过项目文档\n> 项目运行报错 是否已经拿着报错信息去百度 常见报错百度百度足以\n> 是否搜索过其他issue 一些已经解决的问题 会在issue内留下解决方法\n> 无法线上解决或者与框架无关的问题的欢迎加VIP群跟作者一对一谈\n\n### 异常模块\n\n> 此报错都涉及到那些系统模块\n\n例如 ruoyi-system ruoyi-auth 等等\n\n### 问题描述\n\n> 越详细越容易直击问题所在\n\n已知: XXX功能不好用 或 XXX数据不正常 等等\n\n### 希望结果\n\n> 想知道你觉得怎么样是正常或者合理的\n\n希望功能可以有XXX结果 或者 XXX现象\n\n### 重现步骤\n\n> 作者并不知道这个问题是如何出现的\n\n- 1\n- 2\n- 3\n\n### 相关代码与报错信息(请勿发混乱格式)\n\n> 代码可按照如下形式提供或者截图均可 越详细越好\n> 大多数问题都是 代码编写错误问题 逻辑问题 或者用法错误等问题\n\n```java\npublic class XXX {\n\n}\n```"
  },
  {
    "path": ".gitee/PULL_REQUEST_TEMPLATE.zh-CN.md",
    "content": "### 更改目的 解决了什么问题(请提交到dev分支)\n\n\n### 改动逻辑 这么写的思路(让作者更好的理解你的意图)\n\n\n### 测试 都做了哪些测试(未经过测试不采纳)"
  },
  {
    "path": ".gitignore",
    "content": "######################################################################\n# Build Tools\n\n.gradle\n/build/\n!gradle/wrapper/gradle-wrapper.jar\n\ntarget/\n!.mvn/wrapper/maven-wrapper.jar\n\n######################################################################\n# IDE\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\n\n### JRebel ###\nrebel.xml\n\n### NetBeans ###\nnbproject/private/\nbuild/*\nnbbuild/\nnbdist/\n.nb-gradle/\n\n######################################################################\n# Others\n*.log\n*.xml.versionsBackup\n*.swp\n\n!*/build/*.java\n!*/build/*.html\n!*/build/*.xml\n"
  },
  {
    "path": ".run/ruoyi-monitor-admin.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"ruoyi-monitor-admin\" type=\"docker-deploy\" factoryName=\"dockerfile\" server-name=\"Docker\">\n    <deployment type=\"dockerfile\">\n      <settings>\n        <option name=\"imageTag\" value=\"ruoyi/ruoyi-monitor-admin:4.7.0\" />\n        <option name=\"buildOnly\" value=\"true\" />\n        <option name=\"sourceFilePath\" value=\"ruoyi-extend/ruoyi-monitor-admin/Dockerfile\" />\n      </settings>\n    </deployment>\n    <method v=\"2\" />\n  </configuration>\n</component>\n"
  },
  {
    "path": ".run/ruoyi-server.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"ruoyi-server\" type=\"docker-deploy\" factoryName=\"dockerfile\" server-name=\"Docker\">\n    <deployment type=\"dockerfile\">\n      <settings>\n        <option name=\"imageTag\" value=\"ruoyi/ruoyi-server:4.7.0\" />\n        <option name=\"buildOnly\" value=\"true\" />\n        <option name=\"sourceFilePath\" value=\"ruoyi-admin/Dockerfile\" />\n      </settings>\n    </deployment>\n    <method v=\"2\" />\n  </configuration>\n</component>\n"
  },
  {
    "path": ".run/ruoyi-xxl-job-admin.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"ruoyi-xxl-job-admin\" type=\"docker-deploy\" factoryName=\"dockerfile\" server-name=\"Docker\">\n    <deployment type=\"dockerfile\">\n      <settings>\n        <option name=\"imageTag\" value=\"ruoyi/ruoyi-xxl-job-admin:4.7.0\" />\n        <option name=\"buildOnly\" value=\"true\" />\n        <option name=\"sourceFilePath\" value=\"ruoyi-extend/ruoyi-xxl-job-admin/Dockerfile\" />\n      </settings>\n    </deployment>\n    <method v=\"2\" />\n  </configuration>\n</component>\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"java.compile.nullAnalysis.mode\": \"automatic\"\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<http://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<http://www.gnu.org/philosophy/why-not-lgpl.html>.\n"
  },
  {
    "path": "PaiZhiCheng/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>ruoyi-vue-plus</artifactId>\n        <groupId>com.ruoyi</groupId>\n        <version>4.7.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>pai-zhi-cheng</artifactId>\n\n\n    <dependencies>\n\n        <dependency> <!-- 表达式计算 -->\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-jexl3</artifactId>\n            <version>3.2.1</version>\n        </dependency>\n\n        <dependency> <!-- 表达式计算 选项2 -->\n\n            <groupId>com.github.ismail-mekni</groupId>\n            <artifactId>mxreflection</artifactId>\n            <version>1.0.1</version>\n\n        </dependency>\n\n        <dependency>\n            <groupId>com.corundumstudio.socketio</groupId>\n            <artifactId>netty-socketio</artifactId>\n            <version>1.7.16</version>\n        </dependency>\n\n        <!-- 通用工具-->\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>ruoyi-common</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>ruoyi-system</artifactId>\n        </dependency>\n\n\n        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <version>2.0.10</version>\n        </dependency>\n        <dependency>\n            <groupId>io.undertow</groupId>\n            <artifactId>undertow-core</artifactId>\n        </dependency>\n\n        <!-- xxl-job-core -->\n        <dependency>\n            <groupId>com.xuxueli</groupId>\n            <artifactId>xxl-job-core</artifactId>\n        </dependency>\n\n\n        <dependency>\n            <groupId>com.github.wechatpay-apiv3</groupId>\n            <artifactId>wechatpay-apache-httpclient</artifactId>\n            <version>0.4.7</version>\n        </dependency>\n\n\n\n        <dependency>\n            <groupId>com.squareup.okhttp3</groupId>\n            <artifactId>okhttp</artifactId>\n            <version>4.4.1</version>\n        </dependency>\n\n\n\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/Main.java",
    "content": "import lombok.SneakyThrows;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.Semaphore;\n\nimport static org.apache.xmlbeans.impl.schema.StscState.start;\n\npublic class Main {\n\n\n    public static final int max = 100;\n\n    public static int count = 0;\n\n    public static Semaphore semaphoreA =new Semaphore(1);\n    public static Semaphore semaphoreB =new Semaphore(0);\n    @SneakyThrows\n    public static void main(String[] args) throws InterruptedException {\n\n\n        CompletableFuture.runAsync(()->{\n            for (int i = 0; i < max/2; i++) {\n                try {\n                    semaphoreB.acquire();\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n                System.out.println(\"B\");\n                count++;\n                semaphoreA.release();\n            }\n        });\n\n\n        CompletableFuture.runAsync(()->{\n            for (int i = 0; i < max/2; i++) {\n                try {\n                    semaphoreA.acquire();\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n                System.out.println(\"A\");\n                count++;\n                semaphoreB.release();\n            }\n        });\n\n\n        Thread.sleep(1000);\n\n        System.out.println(\"count = \" + count);\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/common/BatchUtils.java",
    "content": "package top.flya.system.common;\n\n\nimport cn.hutool.json.JSONUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\nimport top.flya.system.domain.vo.*;\nimport top.flya.system.service.ISysOssService;\n\nimport javax.annotation.Resource;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\n@Component\n@Slf4j\npublic class BatchUtils {\n    @Resource\n    private ISysOssService iSysOssService;\n\n\n\n    // 假设这里有一个方法可以批量查询新的 imageUrl\n    public  Map<Long, String> getNewImageUrls(List<String> imageUrls) {\n//        List<Long> ossIds = imageUrls.stream().map(Long::parseLong).collect(Collectors.toList());\n\n        List<Long> ossIds = imageUrls.stream()\n            .map(url -> {\n                try {\n                    return Long.parseLong(url);\n                } catch (NumberFormatException e) {\n                    // Handle the exception, e.g. logging or skipping the invalid value\n                    // You can also return a default value in case of invalid format\n                    return null;\n                }\n            })\n            .filter(Objects::nonNull)\n            .collect(Collectors.toList());\n        return iSysOssService.listByIds(ossIds).stream().collect(Collectors.toMap(SysOssVo::getOssId, SysOssVo::getUrl));\n    }\n\n    public List<PzcArtistVo> transformToPzcArtistVo(List<PzcArtistVo> artistList) {\n        log.info(\"transform artistList start: {}\", artistList);\n        // 获取所有旧的 imageUrl\n        List<String> oldImageUrls = artistList.stream()\n            .map(PzcArtistVo::getImageUrl)\n            .collect(Collectors.toList());\n\n        // 批量查询新的 imageUrl\n        Map<Long, String> newImageUrls = getNewImageUrls(oldImageUrls);\n\n        // 使用 Stream API 进行处理\n        return artistList.stream()\n            // 对列表中的每个元素进行处理\n            .map(artist -> {\n                // 从 Map 中获取新的 imageUrl\n                String newImageUrl = artist.getImageUrl().contains(\"http\")?artist.getImageUrl():newImageUrls.get(Long.parseLong(artist.getImageUrl()));\n                // 创建一个新的 PzcArtistVo 对象，使用查询到的新 imageUrl\n                return new PzcArtistVo(\n                    artist.getArtistId(),\n                    artist.getName(),\n                    newImageUrl,\n                    artist.getDescription()\n                );\n            })\n            // 将处理后的元素收集到一个新的 List 中\n            .collect(Collectors.toList());\n    }\n\n    public  List<PzcOrganizerVo> transformToPzcOrganizerVo(List<PzcOrganizerVo> organizerList) {\n        log.info(\"transform organizerList start: {}\", organizerList);\n        // 获取所有旧的 imageUrl\n        List<String> oldImageUrls = organizerList.stream()\n            .map(PzcOrganizerVo::getLogo)\n            .collect(Collectors.toList());\n\n        // 批量查询新的 imageUrl\n        Map<Long, String> newImageUrls = getNewImageUrls(oldImageUrls);\n\n        // 使用 Stream API 进行处理\n        return organizerList.stream()\n            // 对列表中的每个元素进行处理\n            .map(organizer -> {\n                if(organizer.getLogo()==null){\n                    return organizer;\n                }\n                // 从 Map 中获取新的 imageUrl\n                String newImageUrl = organizer.getLogo().contains(\"http\")?organizer.getLogo():newImageUrls.get(Long.parseLong(organizer.getLogo()));\n                // 创建一个新的 PzcArtistVo 对象，使用查询到的新 imageUrl\n                return new PzcOrganizerVo(\n                    organizer.getOrganizerId(),\n                    organizer.getPhone(),\n                    organizer.getName(),\n                    newImageUrl,\n                    organizer.getContent(),\n                    organizer.getCreateTime(),\n                    organizer.getUpdateTime()\n                );\n            })\n            // 将处理后的元素收集到一个新的 List 中\n            .collect(Collectors.toList());\n    }\n\n    public  List<PzcTagVo> transformToPzcTagVo(List<PzcTagVo> tagList) {\n        log.info(\"transform tagList start: {}\", tagList);\n        // 获取所有旧的 imageUrl\n        List<String> oldImageUrls = tagList.stream()\n            .map(PzcTagVo::getImageUrl)\n            .collect(Collectors.toList());\n\n        // 批量查询新的 imageUrl\n        Map<Long, String> newImageUrls = getNewImageUrls(oldImageUrls);\n\n        // 使用 Stream API 进行处理\n        return tagList.stream()\n            // 对列表中的每个元素进行处理\n            .map(tag -> {\n                // 从 Map 中获取新的 imageUrl\n                String newImageUrl = tag.getImageUrl().contains(\"http\")?tag.getImageUrl():newImageUrls.get(Long.parseLong(tag.getImageUrl()));\n                // 创建一个新的 PzcArtistVo 对象，使用查询到的新 imageUrl\n                return new PzcTagVo(\n                    tag.getTagId(),\n                    tag.getName(),\n                    newImageUrl,\n                    tag.getCreateTime(),\n                    tag.getUpdateTime()\n                );\n            })\n            // 将处理后的元素收集到一个新的 List 中\n            .collect(Collectors.toList());\n    }\n\n    public List<PzcIntroVo> transformToPzcIntroVo(List<PzcIntroVo> introList){\n        log.info(\"transform introList start: {}\", JSONUtil.toJsonPrettyStr(introList));\n        // 获取所有旧的 imageUrl\n        List<String> oldImageUrls = introList.stream()\n            .map(PzcIntroVo::getImageFullUrl)\n            .collect(Collectors.toList());\n        // 批量查询新的 imageUrl\n        Map<Long, String> newImageUrls = getNewImageUrls(oldImageUrls);\n        // 使用 Stream API 进行处理\n        return introList.stream()\n            // 对列表中的每个元素进行处理\n            .map(intro -> {\n                // 从 Map 中获取新的 imageUrl\n                String newImageUrl = intro.getImageFullUrl().contains(\"http\")?intro.getImageFullUrl():newImageUrls.get(Long.parseLong(intro.getImageFullUrl()));\n                // 创建一个新的 PzcArtistVo 对象，使用查询到的新 imageUrl\n                return new PzcIntroVo(\n                    intro.getIntroId(),\n                    intro.getTitle(),\n                    intro.getContent(),\n                    intro.getType(),\n                    newImageUrl,\n                    intro.getCreateTime(),\n                    intro.getUpdateTime()\n\n                );\n            })\n            // 将处理后的元素收集到一个新的 List 中\n            .collect(Collectors.toList());\n\n    }\n\n    public List<PzcUserVo> transformToPzcUserVo(List<PzcUserVo> userList){\n        log.info(\"transform userList start: {}\", JSONUtil.toJsonPrettyStr(userList));\n        // 获取所有旧的 imageUrl\n        List<String> oldImageUrls = userList.stream()\n            .filter(user -> user.getAvatar() != null&&!user.getAvatar().contains(\"http\"))\n            .map(PzcUserVo::getAvatar)\n            .collect(Collectors.toList());\n        // 批量查询新的 imageUrl\n        Map<Long, String> newImageUrls = getNewImageUrls(oldImageUrls);\n        // 使用 Stream API 进行处理\n        return userList.stream()\n            // 对列表中的每个元素进行处理\n            .map(user -> {\n                // 从 Map 中获取新的 imageUrl\n                if(user.getAvatar()!=null&&!user.getAvatar().contains(\"http\")) //如果是http开头的就不用转换了\n                {\n                    String newImageUrl = newImageUrls.get(Long.parseLong(user.getAvatar()));\n                    // 创建一个新的 PzcArtistVo 对象，使用查询到的新 imageUrl\n                    return new PzcUserVo(\n                        user.getUserId(),\n                        user.getOpenid(),\n                        user.getMoney(),\n                        user.getUserLevel(),\n                        user.getIntegration(),\n                        user.getIntegrationNow(),\n                        user.getRealname(),\n                        user.getNickname(),\n                        user.getSex(),\n                        user.getPhone(),\n                        newImageUrl,\n                        user.getAddress(),\n                        user.getIntro(),\n                        user.getAge(),\n                        user.getConstellation(),\n                        user.getMbti(),\n                        user.getHobby(),\n                        user.getSchool(),\n                        user.getOccupation(),\n                        user.getCreateTime(),\n                        user.getUpdateTime(),\n                        user.getMusicStyle(),\n                        user.getState(),\n                        user.getExemptCancel()\n                    );\n                }\n                return user;\n\n            })\n            // 将处理后的元素收集到一个新的 List 中\n            .collect(Collectors.toList());\n\n    }\n\n    public List<PzcViewPagerVo> transformToPzcViewPagerVo(List<PzcViewPagerVo> viewPagerList)\n    {\n//        log.info(\"transform viewPagerList start: {}\", JSONUtil.toJsonPrettyStr(viewPagerList));\n        // 获取所有旧的 imageUrl\n        List<String> oldImageUrls = viewPagerList.stream()\n            .map(PzcViewPagerVo::getImageUrl)\n            .collect(Collectors.toList());\n        // 批量查询新的 imageUrl\n        Map<Long, String> newImageUrls = getNewImageUrls(oldImageUrls);\n        // 使用 Stream API 进行处理\n        return viewPagerList.stream()\n            // 对列表中的每个元素进行处理\n            .map(viewPager -> {\n                // 从 Map 中获取新的 imageUrl\n                String newImageUrl = viewPager.getImageUrl().contains(\"http\")?viewPager.getImageUrl():newImageUrls.get(Long.parseLong(viewPager.getImageUrl()));\n                // 创建一个新的 PzcArtistVo 对象，使用查询到的新 imageUrl\n                return new PzcViewPagerVo(\n                    viewPager.getViewPagerId(),\n                    viewPager.getName(),\n                    newImageUrl,\n                    viewPager.getLinkUrl(),\n                    viewPager.getState(),\n                    viewPager.getActivityId()\n                );\n            })\n            // 将处理后的元素收集到一个新的 List 中\n            .collect(Collectors.toList());\n\n    }\n\n\n    public List<PzcActivityVo> transformToPzcActivityVo(List<PzcActivityVo> records) {\n        log.info(\"transform activityList start: {}\", JSONUtil.toJsonPrettyStr(records));\n        // 获取所有旧的 imageUrl\n        List<String> oldImageUrls = records.stream()\n            .map(PzcActivityVo::getCoverImage)\n            .collect(Collectors.toList());\n\n        List<String> innerImageUrls = records.stream()\n            .map(PzcActivityVo::getInnerImage)\n            .collect(Collectors.toList());\n\n        List<String> shareImageUrls = records.stream()\n            .filter(activity -> activity.getShareImage() != null)\n            .map(PzcActivityVo::getShareImage)\n            .collect(Collectors.toList());\n        // 批量查询新的 imageUrl\n        Map<Long, String> newImageUrls = getNewImageUrls(oldImageUrls);\n        Map<Long, String> newInnerImageUrls = getNewImageUrls(innerImageUrls);\n        Map<Long, String> newShareImageUrls = getNewImageUrls(shareImageUrls);\n        // 使用 Stream API 进行处理\n        return records.stream()\n            .map(\n                r->{\n                    String newImageUrl = r.getCoverImage().contains(\"http\")?r.getCoverImage():newImageUrls.get(Long.parseLong(r.getCoverImage()));\n                    String innerImage = r.getInnerImage().contains(\"http\")?r.getInnerImage():newInnerImageUrls.get(Long.parseLong(r.getInnerImage()));\n                    String shareImage = r.getShareImage()==null?null:r.getShareImage().contains(\"http\")?r.getShareImage():newShareImageUrls.get(Long.parseLong(r.getShareImage()));\n                    return  new PzcActivityVo(\n                        r.getActivityId(),\n                        r.getAddress(),\n                        r.getRegionId(),\n                        r.getTitle(),\n                        r.getStartTime(),\n                        r.getEndDate(),\n                        innerImage,\n                        r.getShowTime(),\n                        newImageUrl,\n                        r.getCreateTime(),\n                        r.getUpdateTime(),\n                        r.getState(),\n                        r.getOrganizerId(),\n                        r.getClassify(),\n                        r.getRegion(),\n                        null,\n                        null,\n                        null,\n                        null,\n                        null,\n                        shareImage\n                    );\n                }\n            ).collect(Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/common/CheckUtils.java",
    "content": "package top.flya.system.common;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.system.domain.PzcActivity;\nimport top.flya.system.mapper.PzcActivityMapper;\n\nimport javax.annotation.Resource;\n\n@Slf4j\n@Component\npublic class CheckUtils {\n    @Resource\n    private PzcActivityMapper pzcActivityMapper;\n\n    /**\n     * 0 是创建活动 1是修改活动\n     * @param activity\n     * @param type\n     * @return\n     */\n    public R<Void> checkCreateActivity(PzcActivity activity, Integer type) {\n\n        log.info(\"checkActivity check init\");\n        if(type==1)\n        {\n\n            if(activity.getActivityId()==null)\n            {\n                return R.fail(\"活动id不能为空\");\n            }\n            PzcActivity checkActivity = pzcActivityMapper.selectById(activity.getActivityId());\n            if(checkActivity==null)\n            {\n                return R.fail(\"活动不存在\");\n            }\n\n        }\n        if(StringUtils.isEmpty(activity.getTitle()))\n        {\n            return R.fail(\"活动标题不能为空\");\n        }\n        if(StringUtils.isEmpty(activity.getStartTime()))\n        {\n            return R.fail(\"活动开始时间不能为空\");\n        }\n        if(StringUtils.isEmpty(activity.getEndDate()))\n        {\n            return R.fail(\"活动结束时间不能为空\");\n        }\n        if(StringUtils.isEmpty(activity.getCoverImage()))\n        {\n            return R.fail(\"活动封面不能为空\");\n        }\n\n\n        return R.ok();\n    }\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/config/ClientCache.java",
    "content": "package top.flya.system.config;\n\nimport com.corundumstudio.socketio.SocketIOClient;\nimport lombok.Data;\nimport org.springframework.stereotype.Component;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * @author litong\n * @date 2019/11/6 16:01\n */\n@Component\npublic class ClientCache {\n\n    /**\n     * 本地缓存\n     */\n    public static Map<String, HashMap<UUID, SocketIOClient>> concurrentHashMap = new ConcurrentHashMap<>();\n\n\n    public Map<String, HashMap<UUID, SocketIOClient>> getConcurrentHashMap()\n    {\n        return concurrentHashMap;\n    }\n\n\n    /**\n     * 存入本地缓存\n     *\n     * @param userId         用户ID\n     * @param sessionId      页面sessionID\n     * @param socketIOClient 页面对应的通道连接信息\n     */\n    public void saveClient(String userId, UUID sessionId, SocketIOClient socketIOClient) {\n        HashMap<UUID, SocketIOClient> sessionIdClientCache = concurrentHashMap.get(userId);\n        if (sessionIdClientCache == null) {\n            sessionIdClientCache = new HashMap<>();\n        }\n        sessionIdClientCache.put(sessionId, socketIOClient);\n        concurrentHashMap.put(userId, sessionIdClientCache);\n    }\n\n    /**\n     * 根据用户ID获取所有通道信息\n     *\n     * @param userId\n     * @return\n     */\n    public HashMap<UUID, SocketIOClient> getUserClient(String userId) {\n        return concurrentHashMap.get(userId);\n    }\n\n    /**\n     * 根据用户ID及页面sessionID删除页面链接信息\n     *\n     * @param userId\n     * @param sessionId\n     */\n    public void deleteSessionClient(String userId, UUID sessionId) {\n        concurrentHashMap.remove(userId);\n//        concurrentHashMap.get(userId).remove(sessionId);\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/config/DbTemplate.java",
    "content": "package top.flya.system.config;\n\nimport cn.hutool.core.collection.CollUtil;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.UUID;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * <p>\n * 模拟数据库\n * </p>\n *\n * @author yangkai.shen\n * @date Created in 2018-12-18 19:12\n */\n@Component\npublic class DbTemplate {\n    /**\n     * 模拟数据库存储 user_id <-> session_id 的关系\n     */\n    public static final ConcurrentHashMap<String, UUID> DB = new ConcurrentHashMap<>();\n\n    /**\n     * 获取所有SessionId\n     *\n     * @return SessionId列表\n     */\n    public List<UUID> findAll() {\n        return CollUtil.newArrayList(DB.values());\n    }\n\n    /**\n     * 根据UserId查询SessionId\n     *\n     * @param userId 用户id\n     * @return SessionId\n     */\n    public Optional<UUID> findByUserId(String userId) {\n        return Optional.ofNullable(DB.get(userId));\n    }\n\n    /**\n     * 保存/更新 user_id <-> session_id 的关系\n     *\n     * @param userId    用户id\n     * @param sessionId SessionId\n     */\n    public void save(String userId, UUID sessionId) {\n        DB.put(userId, sessionId);\n    }\n\n    /**\n     * 删除 user_id <-> session_id 的关系\n     *\n     * @param userId 用户id\n     */\n    public void deleteByUserId(String userId) {\n        DB.remove(userId);\n    }\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/config/Event.java",
    "content": "package top.flya.system.config;\n\n/**\n * <p>\n * 事件常量\n * </p>\n *\n * @author yangkai.shen\n * @date Created in 2018-12-18 19:36\n */\npublic interface Event {\n    /**\n     * 聊天事件\n     */\n    String CHAT = \"chat\";\n\n    /**\n     * 官方消息事件\n     */\n    String OFFICIAL = \"official\";\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/config/ServerConfig.java",
    "content": "package top.flya.system.config;\n\nimport com.corundumstudio.socketio.SocketConfig;\nimport com.corundumstudio.socketio.SocketIOServer;\nimport com.corundumstudio.socketio.Transport;\nimport com.corundumstudio.socketio.annotation.SpringAnnotationScanner;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport top.flya.common.utils.JsonUtils;\n\n/**\n * <p>\n * websocket服务器配置\n * </p>\n *\n * @author yangkai.shen\n * @date Created in 2018-12-18 16:42\n */\n@Configuration\n@Slf4j\npublic class ServerConfig {\n\n    @Bean\n    public SocketIOServer server(WsConfig wsConfig) {\n        log.info(\"init wsConfig: {}\", JsonUtils.toJsonString(wsConfig));\n        com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();\n        config.setHostname(wsConfig.getHost());\n        config.setPort(wsConfig.getPort());\n        config.setTransports(Transport.WEBSOCKET,Transport.POLLING); //test\n        SocketConfig socketConfig = config.getSocketConfig();\n        socketConfig.setReuseAddress(true); //地址复用\n\n\n        //这个listener可以用来进行身份验证\n//        config.setAuthorizationListener(data -> {\n//            // http://localhost:8081?token=xxxxxxx\n//            // 例如果使用上面的链接进行connect，可以使用如下代码获取用户密码信息，本文不做身份验证\n//            String token = data.getSingleUrlParam(\"token\");\n//            // 校验token的合法性，实际业务需要校验token是否过期等等，参考 spring-boot-demo-rbac-security 里的 JwtUtil\n//            // 如果认证不通过会返回一个 Socket.EVENT_CONNECT_ERROR 事件\n//            return StrUtil.isNotBlank(token);\n//        });\n\n        return new SocketIOServer(config);\n    }\n\n    /**\n     * Spring 扫描自定义注解\n     */\n    @Bean\n    public SpringAnnotationScanner springAnnotationScanner(SocketIOServer server) {\n        return new SpringAnnotationScanner(server);\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/config/ServerRunner.java",
    "content": "package top.flya.system.config;\n\nimport com.corundumstudio.socketio.SocketIOServer;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.stereotype.Component;\n\n/**\n * <p>\n * websocket服务器启动\n * </p>\n *\n * @author yangkai.shen\n * @date Created in 2018-12-18 17:07\n */\n@Component\n@Slf4j\npublic class ServerRunner implements CommandLineRunner {\n    @Autowired\n    private SocketIOServer server;\n\n    @Override\n    public void run(String... args) {\n        server.start();\n        log.info(\"websocket 服务器启动成功\");\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/config/WsConfig.java",
    "content": "package top.flya.system.config;\n\nimport lombok.Data;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\nimport javax.validation.Valid;\n\n/**\n * <p>\n * WebSocket配置类\n * </p>\n *\n * @author yangkai.shen\n * @date Created in 2018-12-18 16:41\n */\n\n@Data\n@Component\npublic class WsConfig {\n    /**\n     * 端口号\n     */\n    @Value(\"${wx.server.port}\")\n    private Integer port ;\n\n    /**\n     * host\n     */\n    @Value(\"${wx.server.host}\")\n    private String host ;\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/GaoDeMapController.java",
    "content": "package top.flya.system.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.*;\nimport top.flya.common.core.domain.R;\nimport top.flya.system.utils.gaode.GaoDeMapUtil;\n\nimport javax.annotation.Resource;\n\n\n/**\n * @Description: 地图控制层\n * @Author: isymikasan\n * @Date: 2022-01-26 09:36:55\n */\n@RestController\n\n@RequestMapping(\"/point\")\npublic class GaoDeMapController {\n\n    @Resource\n    private GaoDeMapUtil gaoDeMapUtil;\n\n    public static final Logger log = LoggerFactory.getLogger(GaoDeMapController.class);\n\n\n    @GetMapping(\"/getAddress\")\n    public R getAddress(@RequestParam(\"longitude\") String longitude, @RequestParam(\"latitude\") String latitude) {\n        try {\n            return gaoDeMapUtil.getAddress(longitude, latitude);\n        } catch (Exception e) {\n            return R.fail(e.toString());\n        }\n\n    }\n\n    @GetMapping(\"/getLonLat\")\n    public R getLonLat(@RequestParam(\"address\") String address) {\n        return gaoDeMapUtil.getLonLat(address);\n    }\n\n    @GetMapping(\"/getDistance\")\n    public R getDistance(@RequestParam(\"start\") String startLonLat,@RequestParam(\"end\") String endLonLat) {\n        return gaoDeMapUtil.getDistance(startLonLat, endLonLat);\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcActivityConnArtistController.java",
    "content": "package top.flya.system.controller;\n\nimport java.util.List;\nimport java.util.Arrays;\n\nimport lombok.RequiredArgsConstructor;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.*;\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.validation.annotation.Validated;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.vo.PzcActivityConnArtistVo;\nimport top.flya.system.domain.bo.PzcActivityConnArtistBo;\nimport top.flya.system.service.IPzcActivityConnArtistService;\nimport top.flya.common.core.page.TableDataInfo;\n\n/**\n * 活动关联艺人\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/activityConnArtist\")\npublic class PzcActivityConnArtistController extends BaseController {\n\n    private final IPzcActivityConnArtistService iPzcActivityConnArtistService;\n\n    /**\n     * 查询活动关联艺人列表\n     */\n    @SaCheckPermission(\"system:activityConnArtist:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcActivityConnArtistVo> list(PzcActivityConnArtistBo bo, PageQuery pageQuery) {\n        return iPzcActivityConnArtistService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 导出活动关联艺人列表\n     */\n    @SaCheckPermission(\"system:activityConnArtist:export\")\n    @Log(title = \"活动关联艺人\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcActivityConnArtistBo bo, HttpServletResponse response) {\n        List<PzcActivityConnArtistVo> list = iPzcActivityConnArtistService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"活动关联艺人\", PzcActivityConnArtistVo.class, response);\n    }\n\n    /**\n     * 获取活动关联艺人详细信息\n     *\n     * @param activityConnArtistId 主键\n     */\n    @SaCheckPermission(\"system:activityConnArtist:query\")\n    @GetMapping(\"/{activityConnArtistId}\")\n    public R<PzcActivityConnArtistVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Integer activityConnArtistId) {\n        return R.ok(iPzcActivityConnArtistService.queryById(activityConnArtistId));\n    }\n\n    /**\n     * 新增活动关联艺人\n     */\n    @SaCheckPermission(\"system:activityConnArtist:add\")\n    @Log(title = \"活动关联艺人\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcActivityConnArtistBo bo) {\n        return toAjax(iPzcActivityConnArtistService.insertByBo(bo));\n    }\n\n    /**\n     * 修改活动关联艺人\n     */\n    @SaCheckPermission(\"system:activityConnArtist:edit\")\n    @Log(title = \"活动关联艺人\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcActivityConnArtistBo bo) {\n        return toAjax(iPzcActivityConnArtistService.updateByBo(bo));\n    }\n\n    /**\n     * 删除活动关联艺人\n     *\n     * @param activityConnArtistIds 主键串\n     */\n    @SaCheckPermission(\"system:activityConnArtist:remove\")\n    @Log(title = \"活动关联艺人\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{activityConnArtistIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Integer[] activityConnArtistIds) {\n        return toAjax(iPzcActivityConnArtistService.deleteWithValidByIds(Arrays.asList(activityConnArtistIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcActivityConnIntroController.java",
    "content": "package top.flya.system.controller;\n\nimport java.util.List;\nimport java.util.Arrays;\n\nimport lombok.RequiredArgsConstructor;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.*;\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.validation.annotation.Validated;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.vo.PzcActivityConnIntroVo;\nimport top.flya.system.domain.bo.PzcActivityConnIntroBo;\nimport top.flya.system.service.IPzcActivityConnIntroService;\nimport top.flya.common.core.page.TableDataInfo;\n\n/**\n * 活动介绍与活动关联\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/activityConnIntro\")\npublic class PzcActivityConnIntroController extends BaseController {\n\n    private final IPzcActivityConnIntroService iPzcActivityConnIntroService;\n\n    /**\n     * 查询活动介绍与活动关联列表\n     */\n    @SaCheckPermission(\"system:activityConnIntro:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcActivityConnIntroVo> list(PzcActivityConnIntroBo bo, PageQuery pageQuery) {\n        return iPzcActivityConnIntroService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 导出活动介绍与活动关联列表\n     */\n    @SaCheckPermission(\"system:activityConnIntro:export\")\n    @Log(title = \"活动介绍与活动关联\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcActivityConnIntroBo bo, HttpServletResponse response) {\n        List<PzcActivityConnIntroVo> list = iPzcActivityConnIntroService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"活动介绍与活动关联\", PzcActivityConnIntroVo.class, response);\n    }\n\n    /**\n     * 获取活动介绍与活动关联详细信息\n     *\n     * @param activityConnIntroId 主键\n     */\n    @SaCheckPermission(\"system:activityConnIntro:query\")\n    @GetMapping(\"/{activityConnIntroId}\")\n    public R<PzcActivityConnIntroVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Integer activityConnIntroId) {\n        return R.ok(iPzcActivityConnIntroService.queryById(activityConnIntroId));\n    }\n\n    /**\n     * 新增活动介绍与活动关联\n     */\n    @SaCheckPermission(\"system:activityConnIntro:add\")\n    @Log(title = \"活动介绍与活动关联\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcActivityConnIntroBo bo) {\n        return toAjax(iPzcActivityConnIntroService.insertByBo(bo));\n    }\n\n    /**\n     * 修改活动介绍与活动关联\n     */\n    @SaCheckPermission(\"system:activityConnIntro:edit\")\n    @Log(title = \"活动介绍与活动关联\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcActivityConnIntroBo bo) {\n        return toAjax(iPzcActivityConnIntroService.updateByBo(bo));\n    }\n\n    /**\n     * 删除活动介绍与活动关联\n     *\n     * @param activityConnIntroIds 主键串\n     */\n    @SaCheckPermission(\"system:activityConnIntro:remove\")\n    @Log(title = \"活动介绍与活动关联\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{activityConnIntroIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Integer[] activityConnIntroIds) {\n        return toAjax(iPzcActivityConnIntroService.deleteWithValidByIds(Arrays.asList(activityConnIntroIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcActivityConnTagController.java",
    "content": "package top.flya.system.controller;\n\nimport java.util.List;\nimport java.util.Arrays;\n\nimport lombok.RequiredArgsConstructor;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.*;\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.validation.annotation.Validated;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.vo.PzcActivityConnTagVo;\nimport top.flya.system.domain.bo.PzcActivityConnTagBo;\nimport top.flya.system.service.IPzcActivityConnTagService;\nimport top.flya.common.core.page.TableDataInfo;\n\n/**\n * 活动标签与活动关联\n *\n * @author ruoyi\n * @date 2023-06-03\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/activityConnTag\")\npublic class PzcActivityConnTagController extends BaseController {\n\n    private final IPzcActivityConnTagService iPzcActivityConnTagService;\n\n    /**\n     * 查询活动标签与活动关联列表\n     */\n    @SaCheckPermission(\"system:activityConnTag:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcActivityConnTagVo> list(PzcActivityConnTagBo bo, PageQuery pageQuery) {\n        return iPzcActivityConnTagService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 导出活动标签与活动关联列表\n     */\n    @SaCheckPermission(\"system:activityConnTag:export\")\n    @Log(title = \"活动标签与活动关联\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcActivityConnTagBo bo, HttpServletResponse response) {\n        List<PzcActivityConnTagVo> list = iPzcActivityConnTagService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"活动标签与活动关联\", PzcActivityConnTagVo.class, response);\n    }\n\n    /**\n     * 获取活动标签与活动关联详细信息\n     *\n     * @param activityConnTagId 主键\n     */\n    @SaCheckPermission(\"system:activityConnTag:query\")\n    @GetMapping(\"/{activityConnTagId}\")\n    public R<PzcActivityConnTagVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Integer activityConnTagId) {\n        return R.ok(iPzcActivityConnTagService.queryById(activityConnTagId));\n    }\n\n    /**\n     * 新增活动标签与活动关联\n     */\n    @SaCheckPermission(\"system:activityConnTag:add\")\n    @Log(title = \"活动标签与活动关联\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcActivityConnTagBo bo) {\n        return toAjax(iPzcActivityConnTagService.insertByBo(bo));\n    }\n\n    /**\n     * 修改活动标签与活动关联\n     */\n    @SaCheckPermission(\"system:activityConnTag:edit\")\n    @Log(title = \"活动标签与活动关联\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcActivityConnTagBo bo) {\n        return toAjax(iPzcActivityConnTagService.updateByBo(bo));\n    }\n\n    /**\n     * 删除活动标签与活动关联\n     *\n     * @param activityConnTagIds 主键串\n     */\n    @SaCheckPermission(\"system:activityConnTag:remove\")\n    @Log(title = \"活动标签与活动关联\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{activityConnTagIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Integer[] activityConnTagIds) {\n        return toAjax(iPzcActivityConnTagService.deleteWithValidByIds(Arrays.asList(activityConnTagIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcActivityController.java",
    "content": "package top.flya.system.controller;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.bo.PzcActivityBo;\nimport top.flya.system.domain.vo.PzcActivityVo;\nimport top.flya.system.service.IPzcActivityService;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 活动\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/activity\")\n@Slf4j\npublic class PzcActivityController extends BaseController {\n\n    private final IPzcActivityService iPzcActivityService;\n\n    /**\n     * 新增活动\n     */\n    @SaCheckPermission(\"system:activity:add\")\n    @Log(title = \"活动\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcActivityBo bo) {\n        return toAjax(iPzcActivityService.insertByBo(bo));\n    }\n\n    /**\n     * 修改活动\n     */\n    @SaCheckPermission(\"system:activity:edit\")\n    @Log(title = \"活动\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcActivityBo bo) {\n        return toAjax(iPzcActivityService.updateByBo(bo));\n    }\n\n\n\n\n    /**\n     * 查询活动列表\n     */\n    @SaCheckPermission(\"system:activity:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcActivityVo> list(PzcActivityBo bo, PageQuery pageQuery) {\n        return iPzcActivityService.queryPageList(bo, pageQuery);\n    }\n\n\n    /**\n     * 查询活动列表 小程序端\n     */\n    @GetMapping(\"/listWx\")\n    public TableDataInfo<PzcActivityVo> Wx(PzcActivityBo bo, PageQuery pageQuery) {\n        pageQuery.setIsAsc(\"desc\");\n        pageQuery.setOrderByColumn(\"start_time\");\n        return iPzcActivityService.queryPageListWx(bo, pageQuery);\n    }\n\n    /**\n     * 导出活动列表\n     */\n    @SaCheckPermission(\"system:activity:export\")\n    @Log(title = \"活动\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcActivityBo bo, HttpServletResponse response) {\n        List<PzcActivityVo> list = iPzcActivityService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"活动\", PzcActivityVo.class, response);\n    }\n\n    /**\n     * 获取活动详细信息\n     *\n     * @param activityId 主键\n     */\n    @GetMapping(\"/{activityId}\")\n    public R<PzcActivityVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Integer activityId) {\n        return R.ok(iPzcActivityService.queryById(activityId));\n    }\n\n\n\n\n\n    /**\n     * 删除活动\n     *\n     * @param activityIds 主键串\n     */\n    @SaCheckPermission(\"system:activity:remove\")\n    @Log(title = \"活动\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{activityIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Integer[] activityIds) {\n        return toAjax(iPzcActivityService.deleteWithValidByIds(Arrays.asList(activityIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcActivityGroupApplyController.java",
    "content": "package top.flya.system.controller;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.PzcActivityGroup;\nimport top.flya.system.domain.PzcActivityGroupApply;\nimport top.flya.system.domain.PzcUser;\nimport top.flya.system.domain.bo.PzcActivityGroupApplyBo;\nimport top.flya.system.domain.bo.WxzApplyBo;\nimport top.flya.system.domain.vo.PzcActivityGroupApplyVo;\nimport top.flya.system.mapper.PzcActivityGroupApplyMapper;\nimport top.flya.system.mapper.PzcActivityGroupMapper;\nimport top.flya.system.mapper.PzcUserMapper;\nimport top.flya.system.service.IPzcActivityGroupApplyService;\nimport top.flya.system.utils.ActivityUtils;\nimport top.flya.system.utils.WxUtils;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.math.BigDecimal;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\n/**\n * 活动组队申请列表\n *\n * @author ruoyi\n * @date 2023-07-10\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/activityGroupApply\")\n@Slf4j\npublic class PzcActivityGroupApplyController extends BaseController {\n\n    private final IPzcActivityGroupApplyService iPzcActivityGroupApplyService;\n\n    private final ActivityUtils activityUtils;\n\n    private final PzcActivityGroupApplyMapper pzcActivityGroupApplyMapper;\n\n    private final PzcUserMapper pzcUserMapper;\n\n    private final PzcActivityGroupMapper pzcActivityGroupMapper;\n\n\n    private final StringRedisTemplate stringRedisTemplate;\n\n    private final WxUtils wxUtils;\n\n    private final ExecutorService newSingleThreadExecutor = new ThreadPoolExecutor(10, 20, 200L,\n        TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(100));\n\n\n    @PostMapping(\"/wxzApply\")\n    @Transactional\n    public R wxzApply(@RequestParam(\"applyId\") Integer applyId, @RequestParam(\"wxz\") Integer wxz) {  //wxz 0 未选择 1 选择\n        log.info(\"applyId:{},wxz:{}\", applyId, wxz);\n        if (wxz != 0 && wxz != 1) {\n            return R.fail(\"参数错误\");\n        }\n        PzcActivityGroupApply pzcActivityGroupApply = pzcActivityGroupApplyMapper.selectById(applyId);\n        if (pzcActivityGroupApply == null) {\n            return R.fail(\"申请不存在\");\n        }\n\n        if (pzcActivityGroupApply.getApplyStatus() != 2 && pzcActivityGroupApply.getApplyStatus() != 9 && pzcActivityGroupApply.getApplyStatus() != 10 && pzcActivityGroupApply.getApplyStatus() != 11 && pzcActivityGroupApply.getApplyStatus() != 12) {\n            return R.fail(\"当前状态为【\" + wxUtils.applyStatus(pzcActivityGroupApply.getApplyStatus()) + \"】，不能进行此操作\");\n        }\n        //这里取消redis缓存\n        Long userId = LoginHelper.getUserId();\n        String result = stringRedisTemplate.opsForValue().get(\"officialMessage:\" + userId);\n        log.info(\"result:{}\", result);\n        if (result != null) {\n            WxzApplyBo wxzApplyBo = JsonUtils.parseObject(result, WxzApplyBo.class);\n            if (wxzApplyBo == null) {\n                return R.fail(\"转换JSON异常\");\n            }\n            if (!wxzApplyBo.getApplyId().equals(applyId)) {\n                return R.fail(\"申请方请求不存在\");\n            }\n            stringRedisTemplate.delete(\"officialMessage:\" + userId);\n        } else {\n            return R.fail(\"申请方请求不存在\");\n        }\n\n        if(wxz==0)\n        {\n            return R.ok();\n        }\n\n        pzcActivityGroupApply.setWxz(wxz);\n        pzcActivityGroupApply.setApplyStatus(3); //直接是组队结束状态\n        pzcActivityGroupApplyMapper.updateById(pzcActivityGroupApply);\n\n        //退还双方保证金\n\n        newSingleThreadExecutor.execute(() -> {\n            // 更新 组队状态为已结束\n            PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupApply.getGroupId());\n\n            PzcUser applyUser = pzcUserMapper.selectById(pzcActivityGroupApply.getUserId());\n            PzcUser groupUser = pzcUserMapper.selectById(pzcActivityGroup.getUserId());\n            applyUser.setMoney(applyUser.getMoney().add(pzcActivityGroupApply.getMoney().subtract(new BigDecimal(\"10\"))));\n            groupUser.setMoney(groupUser.getMoney().add(pzcActivityGroup.getMoney().subtract(new BigDecimal(\"10\")))); //退还发起人的保证金\n            //无限制确认到达加3分\n            applyUser.setIntegration(applyUser.getIntegration() + 3);\n            applyUser.setIntegrationNow(applyUser.getIntegrationNow() + 3);\n            groupUser.setIntegration(groupUser.getIntegration() + 3);\n            groupUser.setIntegrationNow(groupUser.getIntegrationNow() + 3);\n            pzcUserMapper.updateById(applyUser);\n            pzcUserMapper.updateById(groupUser);\n            //更新余额变动\n            wxUtils.insertUserHistory(applyUser.getUserId(), pzcActivityGroup.getActivityId(),2L, \"组队结束，退还保证金 并收取活动费用 10派币\", pzcActivityGroupApply.getMoney().subtract(new BigDecimal(\"10\")));\n            wxUtils.insertUserHistory(groupUser.getUserId(), pzcActivityGroup.getActivityId(),2L, \"组队结束，退还保证金 并收取活动费用 10派币\", pzcActivityGroupApply.getMoney().subtract(new BigDecimal(\"10\")));\n\n\n\n            pzcActivityGroup.setStatus(1); //已结束\n            pzcActivityGroupMapper.updateById(pzcActivityGroup);\n        });\n\n\n        return R.ok();\n    }\n\n    @GetMapping(\"/myHistory\") //我的历史活动\n    public R<List<PzcActivityGroupApply>> myHistory() {\n        //我申请 并处于进行中的活动\n        Long userId = LoginHelper.getUserId();\n        List<PzcActivityGroupApply> step1 = pzcActivityGroupApplyMapper.selectList(\n            new QueryWrapper<PzcActivityGroupApply>()\n                .eq(\"user_id\", userId).in(\"apply_status\", 3,13,14,15));\n        step1.forEach(\n            p -> {\n                PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(p.getGroupId());\n                PzcUser my = pzcUserMapper.selectById(p.getUserId());\n                PzcUser other = pzcUserMapper.selectById(pzcActivityGroup.getUserId());\n                p.setOtherMoney(pzcActivityGroup.getMoney());\n                p.setOtherName(other.getNickname());\n                p.setOtherAvatar(other.getAvatar());\n                p.setOtherUserId(String.valueOf(other.getUserId()));\n                p.setOtherLevel(Math.toIntExact(other.getUserLevel()));\n                p.setMyAvatar(my.getAvatar());\n                p.setTitle(pzcActivityGroup.getTitle());\n            }\n        );\n        List<PzcActivityGroupApply> result = new java.util.ArrayList<>();\n\n        //申请我的 并处于进行中的活动\n        //1 找出所有我创建的组\n        List<PzcActivityGroup> pzcActivityGroups = pzcActivityGroupMapper.selectList(new QueryWrapper<PzcActivityGroup>().eq(\"user_id\", userId));\n        List<Long> groupIds = pzcActivityGroups.stream().map(PzcActivityGroup::getGroupId).collect(java.util.stream.Collectors.toList());\n        if (groupIds.size() != 0) {\n            List<PzcActivityGroupApply> step2 = pzcActivityGroupApplyMapper.selectList(new QueryWrapper<>(new PzcActivityGroupApply()).in(\"group_id\", groupIds).in(\"apply_status\", 3,14,13,15));\n            step2.forEach(\n                p -> {\n                    PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(p.getGroupId());\n                    PzcUser other = pzcUserMapper.selectById(p.getUserId());\n                    PzcUser my = pzcUserMapper.selectById(pzcActivityGroup.getUserId());\n                    p.setOtherMoney(pzcActivityGroup.getMoney());\n                    p.setOtherName(other.getNickname());\n                    p.setOtherAvatar(other.getAvatar());\n                    p.setOtherUserId(String.valueOf(other.getUserId()));\n                    p.setOtherLevel(Math.toIntExact(other.getUserLevel()));\n                    p.setMyAvatar(my.getAvatar());\n                    p.setTitle(pzcActivityGroup.getTitle());\n                }\n            );\n            result.addAll(step2);\n        }\n        result.addAll(step1);\n\n        //按照更新时间倒序排列\n        List<PzcActivityGroupApply> collect = result.stream().sorted((o1, o2) -> o2.getUpdateTime().compareTo(o1.getUpdateTime())).collect(Collectors.toList());\n\n        return R.ok(collect);\n    }\n\n    /**\n     * -1 已取消\n     * 0 位于申请列表中\n     * 1 申请通过待确认时\n     * 2 确认通过进行中\n     * 3 组队结束\n     * 9发起方已确认\n     * 10申请方已确认\n     * 11 发起方已打卡\n     * 12 申请方已打卡\n     * 13 发起方已评价\n     * 14 申请方已评价\n     * 15 双方已评价\n     *\n     * @return\n     */\n    @GetMapping(\"/myTrips\") //我的行程\n    public R<List<PzcActivityGroupApply>> myTrips() {\n        //我申请 并处于进行中的活动\n        Long userId = LoginHelper.getUserId();\n        List<PzcActivityGroupApply> step1 = pzcActivityGroupApplyMapper.selectList(\n            new QueryWrapper<PzcActivityGroupApply>()\n                .eq(\"user_id\", userId).in(\"apply_status\", 1, 2, 3, 9, 10, 11, 12, 14)); //, 14 我评价过了就从行程中移除\n        step1.forEach(\n            p -> {\n                PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(p.getGroupId());\n                PzcUser my = pzcUserMapper.selectById(p.getUserId());\n                PzcUser other = pzcUserMapper.selectById(pzcActivityGroup.getUserId());\n                p.setOtherMoney(pzcActivityGroup.getMoney());\n                p.setOtherName(other.getNickname());\n                p.setOtherAvatar(other.getAvatar());\n                p.setOtherUserId(String.valueOf(other.getUserId()));\n                p.setOtherLevel(Math.toIntExact(other.getUserLevel()));\n                p.setMyAvatar(my.getAvatar());\n                p.setTitle(pzcActivityGroup.getTitle());\n            }\n        );\n        List<PzcActivityGroupApply> result = new java.util.ArrayList<>();\n\n        //申请我的 并处于进行中的活动\n        //1 找出所有我创建的组\n        List<PzcActivityGroup> pzcActivityGroups = pzcActivityGroupMapper.selectList(new QueryWrapper<PzcActivityGroup>().eq(\"user_id\", userId));\n        List<Long> groupIds = pzcActivityGroups.stream().map(PzcActivityGroup::getGroupId).collect(java.util.stream.Collectors.toList());\n        if (groupIds.size() != 0) {\n            List<PzcActivityGroupApply> step2 = pzcActivityGroupApplyMapper.selectList(new QueryWrapper<>(new PzcActivityGroupApply()).in(\"group_id\", groupIds).in(\"apply_status\", 1, 2, 3, 9, 10, 11, 12,  13));//13, 评价了就移除\n            step2.forEach(\n                p -> {\n                    PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(p.getGroupId());\n                    PzcUser other = pzcUserMapper.selectById(p.getUserId());\n                    PzcUser my = pzcUserMapper.selectById(pzcActivityGroup.getUserId());\n                    p.setOtherMoney(pzcActivityGroup.getMoney());\n                    p.setOtherName(other.getNickname());\n                    p.setOtherAvatar(other.getAvatar());\n                    p.setOtherUserId(String.valueOf(other.getUserId()));\n                    p.setOtherLevel(Math.toIntExact(other.getUserLevel()));\n                    p.setMyAvatar(my.getAvatar());\n                    p.setTitle(pzcActivityGroup.getTitle());\n                }\n            );\n            result.addAll(step2);\n        }\n        result.addAll(step1);\n\n        //按照更新时间倒序排列\n        List<PzcActivityGroupApply> collect = result.stream().sorted((o1, o2) -> o2.getUpdateTime().compareTo(o1.getUpdateTime())).collect(Collectors.toList());\n\n        return R.ok(collect);\n    }\n\n    /**\n     * 查询活动组队申请列表列表\n     */\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcActivityGroupApplyVo> list(PzcActivityGroupApplyBo bo, PageQuery pageQuery) {\n        bo.setUserId(LoginHelper.getUserId());\n        return iPzcActivityGroupApplyService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 导出活动组队申请列表列表\n     */\n    @Log(title = \"活动组队申请列表\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcActivityGroupApplyBo bo, HttpServletResponse response) {\n        List<PzcActivityGroupApplyVo> list = iPzcActivityGroupApplyService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"活动组队申请列表\", PzcActivityGroupApplyVo.class, response);\n    }\n\n    /**\n     * 获取活动组队申请列表详细信息\n     *\n     * @param applyId 主键\n     */\n    @GetMapping(\"/{applyId}\")\n    public R<PzcActivityGroupApplyVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                              @PathVariable Long applyId) {\n        return R.ok(iPzcActivityGroupApplyService.queryById(applyId));\n    }\n\n    /**\n     * 申请参与组队\n     * <p>\n     * 1 做校验\n     * 1.1 活动是否存在 1.2 活动是否已经开始 1.3 活动是否已经结束 1.4 活动是否已经满员\n     * 2 组是否还存在\n     * <p>\n     * <p>\n     * ====================\n     * 用户申请活动的时候 判断 是否足够缴纳保证金\n     */\n    @Log(title = \"活动组队申请列表\", businessType = BusinessType.INSERT) //\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcActivityGroupApplyBo bo) {\n        log.info(\"申请参与组队:{}\", JsonUtils.toJsonString(bo));\n        if(bo.getMoney().compareTo(new BigDecimal(100))<0)\n        {\n            return R.fail(\"申请失败，最低保障金为99派币~\");\n        }\n        if (!activityUtils.allCheck(Math.toIntExact(bo.getActivityId()), bo.getGroupId())) {\n            return R.fail(\"申请失败，活动不存在或者已经结束或者组不存在\");\n        }\n        bo.setUserId(LoginHelper.getUserId());\n        if (iPzcActivityGroupApplyService.queryByUserIdAndGroupId(bo.getUserId(), bo.getGroupId()) != null) {\n            return R.fail(\"申请失败，您已经申请过了\");\n        }\n\n        //======================================================\n        PzcUser applyUser = pzcUserMapper.selectById(bo.getUserId()); //我有2个币  申请需要 两个币  我则需要 根据当前他的余额来判断\n        log.info(\"申请参与组队 我目前的余额是： {} 申请需要的余额是：{}\", applyUser.getMoney(), bo.getMoney());\n        if (applyUser.getMoney().compareTo(bo.getMoney()) < 0 || applyUser.getMoney().compareTo(new BigDecimal(100)) < 0) //100块钱 也没有就需要充值了\n        {\n            return R.fail(\"申请失败，最低保障金为99派币~\");\n        }\n\n        return toAjax(iPzcActivityGroupApplyService.insertByBo(bo));\n    }\n\n    /**\n     * 修改活动组队申请列表\n     */\n    @Log(title = \"活动组队申请列表\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcActivityGroupApplyBo bo) {\n        bo.setUserId(LoginHelper.getUserId());\n        if (!activityUtils.allCheck(Math.toIntExact(bo.getActivityId()), bo.getGroupId())) {\n            return R.fail(\"修改失败，活动不存在或者已经结束或者组不存在\");\n        }\n        if (!iPzcActivityGroupApplyService.queryByUserIdAndActivityId(bo.getUserId(), bo.getActivityId())) {\n            return R.fail(\"修改失败，您还没有申请过该活动组\");\n        }\n        return toAjax(iPzcActivityGroupApplyService.updateByBo(bo));\n    }\n\n    /**\n     * 取消活动组队申请列表\n     *\n     * @param applyIds 主键串\n     */\n    @Log(title = \"活动组队申请列表\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{applyIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Long[] applyIds) {\n        return toAjax(iPzcActivityGroupApplyService.deleteWithValidByIds(Arrays.asList(applyIds), true));\n    }\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcActivityGroupController.java",
    "content": "package top.flya.system.controller;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport cn.hutool.core.map.MapBuilder;\nimport cn.hutool.json.JSONUtil;\nimport com.alibaba.excel.util.DateUtils;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.*;\nimport top.flya.system.domain.bo.PzcActivityGroupBo;\nimport top.flya.system.domain.bo.RefurbishBo;\nimport top.flya.system.domain.vo.PzcActivityGroupApplyVo;\nimport top.flya.system.domain.vo.PzcActivityGroupVo;\nimport top.flya.system.domain.vo.RefurbishVO;\nimport top.flya.system.mapper.*;\nimport top.flya.system.service.IPzcActivityGroupApplyService;\nimport top.flya.system.service.IPzcActivityGroupService;\nimport top.flya.system.utils.WxUtils;\nimport top.flya.system.utils.gaode.GaoDeMapUtil;\nimport top.flya.system.xxlJob.ScheduledExecutorUtils;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\nimport java.math.BigDecimal;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.concurrent.*;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport static top.flya.system.config.ClientCache.concurrentHashMap;\n\n/**\n * 活动组队\n *\n * @author ruoyi\n * @date 2023-07-10\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@Slf4j\n@RequestMapping(\"/system/activityGroup\")\npublic class PzcActivityGroupController extends BaseController {\n\n    private final IPzcActivityGroupService iPzcActivityGroupService;\n\n    private final IPzcActivityGroupApplyService iPzcActivityGroupApplyService;\n\n    private final PzcUserMapper pzcUserMapper;\n\n    private final PzcActivityMapper pzcActivityMapper;\n\n    private final PzcUserPhotoMapper pzcUserPhotoMapper;\n\n    private final WxUtils wxUtils;\n\n    private final PzcActivityGroupMapper pzcActivityGroupMapper;\n\n    private final PzcActivityGroupApplyMapper pzcActivityGroupApplyMapper;\n\n    private final PzcOfficialMapper pzcOfficialMapper;\n\n    private final PzcUserTalkMapper pzcUserTalkMapper;\n\n    private final GaoDeMapUtil gaoDeMapUtil;\n    private final ExecutorService newSingleThreadExecutor = new ThreadPoolExecutor(10, 20, 200L,\n        TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(100),\n        Executors.defaultThreadFactory(),\n        new ThreadPoolExecutor.CallerRunsPolicy() //指定拒绝策略 防止任务不执行\n    );\n\n    @PostMapping(\"/cancelIssueGroup\") //取消组队的发布\n    public R cancelIssueGroup(@RequestParam(\"groupId\") Long groupId) {\n        log.info(\"取消组队的发布: {}\", groupId);\n        Long userId = LoginHelper.getUserId();\n        //首先看看组队是否是我发布的 并且是否有人申请 如果有人申请则不能取消\n        PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(groupId);\n        if (pzcActivityGroup == null || !pzcActivityGroup.getUserId().equals(userId)) {\n            return R.fail(\"组队不存在~\");\n        }\n        // 查询申请列表 如果有人申请则不能取消\n        QueryWrapper<PzcActivityGroupApply> queryWrapper = new QueryWrapper<>();\n        queryWrapper.eq(\"group_id\", groupId);\n        List<PzcActivityGroupApply> pzcActivityGroupApplies = pzcActivityGroupApplyMapper.selectList(queryWrapper);\n        List<Long> delApplyIds = new ArrayList<>();\n        pzcActivityGroupApplies.forEach(\n            pzcActivityGroupApply -> {\n                if (pzcActivityGroupApply.getApplyStatus() != -1 && pzcActivityGroupApply.getApplyStatus() != 0 && pzcActivityGroupApply.getApplyStatus() != 15) {\n                    throw new RuntimeException(\"有组队正在进行中，不能取消\");\n                }\n                if (pzcActivityGroupApply.getApplyStatus() == 0) {\n                    delApplyIds.add(pzcActivityGroupApply.getActivityId());\n                    //异步给对方发消息 通知对方已拒绝 修改为异步请求\n                    newSingleThreadExecutor.execute(() -> {\n\n                        wxUtils.insertPzcOfficialMsg(userId, pzcActivityGroupApply.getUserId(),\n                            \"来自您的申请组队【\" + pzcActivityGroup.getTitle() + \"】信息：\",\n                            \"您的申请组队【\" + pzcActivityGroup.getTitle() + \"】已被发布者取消\",\n                            pzcActivityGroup.getGroupId(), pzcActivityGroup.getActivityId());\n                    });\n\n                }\n\n            }\n        );\n        //删除申请\n        if (delApplyIds.size() > 0) {\n            pzcActivityGroupApplyMapper.deleteBatchIds(delApplyIds);\n        }\n        //删除组队\n        pzcActivityGroupMapper.deleteById(groupId);\n        return R.ok();\n    }\n\n\n    @PostMapping(\"/refurbish\") //刷新\n    public R refurbish(@RequestBody RefurbishBo refurbishBo) throws Exception {\n        log.info(\"刷新: {}\", JsonUtils.toJsonString(refurbishBo));\n        //首先查询这个组队是否是我发起的\n        PzcActivityGroupApply pzcActivityGroupApply = pzcActivityGroupApplyMapper.selectById(refurbishBo.getApplyId());\n        if (pzcActivityGroupApply == null) {\n            return R.fail(\"申请不存在\");\n        }\n\n        PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupApply.getGroupId());\n        Pattern pattern = Pattern.compile(\"【(.*?)】(.*?)\");\n        Matcher matcher = pattern.matcher(pzcActivityGroup.getAddress());\n\n        String end = \"\";\n        if (matcher.find()) {\n            end = matcher.group(1);\n        } else {\n            return R.fail(\"地址解析失败,请联系管理员\");\n        }\n\n        String[] startJwd = refurbishBo.getAddress().split(\",\");\n        Long data = gaoDeMapUtil.getDistance(refurbishBo.getAddress(), end).getData();\n        refurbishBo.setAddress(gaoDeMapUtil.getAddress(startJwd[0], startJwd[1]).getData().toString());\n        log.info(\"调用高德API获取到的用户目前地址为: {}\", refurbishBo.getAddress());\n        log.info(\"调用高德API获取到的用户距离目标地址距离为: {}\", data);\n        if (refurbishBo.getRole() == 0) //申请方\n        {\n            //判断申请方现在是否可以打卡\n            pzcActivityGroupApply.setApplyAddress(refurbishBo.getAddress());\n            pzcActivityGroupApplyMapper.updateById(pzcActivityGroupApply);\n            RefurbishVO refurbishVO = new RefurbishVO();\n            refurbishVO.setApplyAddress(refurbishBo.getAddress());\n            refurbishVO.setApplyId(refurbishBo.getApplyId());\n            refurbishVO.setStartAddress(pzcActivityGroupApply.getStartAddress());\n            refurbishVO.setDistance(data);\n            log.info(\"刷新返回的信息为： {}\", JSONUtil.toJsonPrettyStr(refurbishVO));\n\n            return R.ok(refurbishVO);\n\n        } else {\n            pzcActivityGroupApply.setStartAddress(refurbishBo.getAddress());\n            pzcActivityGroupApplyMapper.updateById(pzcActivityGroupApply);\n            RefurbishVO refurbishVO = new RefurbishVO();\n            refurbishVO.setApplyAddress(pzcActivityGroupApply.getApplyAddress());\n            refurbishVO.setApplyId(refurbishBo.getApplyId());\n            refurbishVO.setStartAddress(refurbishBo.getAddress());\n            refurbishVO.setDistance(data);\n            log.info(\"刷新返回的信息为： {}\", JSONUtil.toJsonPrettyStr(refurbishVO));\n\n            return R.ok(refurbishVO);\n        }\n    }\n\n\n    @PostMapping(\"/cancel\") //取消 双方都可以取消\n    @Transactional\n    @RepeatSubmit()\n    public R cancel(@RequestParam(\"applyId\") Integer applyId) {\n\n        //首先查询这个组队是否是我发起的\n        PzcActivityGroupApplyVo pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryById(applyId.longValue());\n        if (pzcActivityGroupApplyVo == null) {\n            return R.fail(\"申请不存在\");\n        }\n        Long userId = LoginHelper.getUserId();\n        Long groupId = pzcActivityGroupApplyVo.getGroupId();\n        PzcActivityGroupVo pzcActivityGroupVo = iPzcActivityGroupService.queryById(groupId);\n        if (pzcActivityGroupVo == null) {\n            return R.fail(\"组队不存在\");\n        }\n\n\n        PzcUser otherUser = null;\n        if (pzcActivityGroupVo.getUserId().equals(userId)) //我是发起人 取消\n        {\n            otherUser = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId());\n\n        } else { //我是申请人\n            otherUser = pzcUserMapper.selectById(pzcActivityGroupMapper.selectById(groupId).getUserId());\n        }\n        // 给对方发官方消息 通知对方已取消\n        wxUtils.insertPzcOfficialMsg(userId, otherUser.getUserId(),\n            \"来自\" + otherUser.getNickname() + \"与您的组队信息：\",\n            \"在【\" + pzcActivityGroupVo.getTitle() + \"】组队中途，对方已经取消本次组队。您可以再次同意或者申请其他用户\",\n            groupId, pzcActivityGroupVo.getActivityId());\n\n        Integer applyStatus = pzcActivityGroupApplyVo.getApplyStatus();\n        if (applyStatus != 0 && applyStatus != 1 && applyStatus != 9 && applyStatus != 10) {\n            return R.fail(\"该订单位于【\" + wxUtils.applyStatus(applyStatus) + \"】状态，不可取消\");\n        }\n\n        //查询用户是否有免责取消次数\n        PzcUser pzcUser = pzcUserMapper.selectById(LoginHelper.getUserId());\n        if (pzcUser.getExemptCancel() > 0) {\n            pzcUser.setExemptCancel(pzcUser.getExemptCancel() - 1);\n        } else {\n            pzcUser.setMoney(pzcUser.getMoney().subtract(new BigDecimal(\"10\"))); //扣除10派币\n            wxUtils.insertUserHistory(pzcUser.getUserId(), pzcActivityGroupVo.getActivityId(), 4L, \"取消组队扣除10派币\", new BigDecimal(\"10\").negate());\n        }\n        pzcUserMapper.updateById(pzcUser);\n        //修改状态为 已取消\n\n        newSingleThreadExecutor.execute(() -> {\n            // 更新 组队状态为已结束\n            PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupVo.getGroupId());\n            pzcActivityGroup.setStatus(1); //已结束\n            pzcActivityGroupMapper.updateById(pzcActivityGroup);\n        });\n        return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), -1));\n    }\n\n\n    @PostMapping(\"/cancelByGroupIn\") //活动过程中取消组队 扣除保证金 + 20派币 退还对方派币 + 对方的保证金 通知对方\n    @Transactional\n    @RepeatSubmit()\n    public R cancelByGroupIn(@RequestParam(\"applyId\") Integer applyId) {\n        Long userId = LoginHelper.getUserId();\n        PzcUser my = pzcUserMapper.selectById(userId);\n        PzcActivityGroupApplyVo pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryById(applyId.longValue());\n        if (pzcActivityGroupApplyVo == null) {\n            return R.fail(\"申请不存在\");\n        }\n\n        PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupApplyVo.getGroupId());\n        if (pzcActivityGroupApplyVo.getUserId().equals(userId)) //我是申请方\n        {\n            //把钱都返还给发起方\n            BigDecimal money = pzcActivityGroupApplyVo.getMoney();\n            money = money.subtract(new BigDecimal(\"20\"));\n            PzcUser startUser = pzcUserMapper.selectById(pzcActivityGroup.getUserId());\n            startUser.setMoney(startUser.getMoney().add(money).add(pzcActivityGroup.getMoney())); //全额返回给发起方的保证金 + 对方扣除 0.2保证金 后的派币\n            pzcUserMapper.updateById(startUser);\n\n\n            BigDecimal finalMoney = money;\n            newSingleThreadExecutor.execute(() -> {\n\n                //官方推送消息\n                wxUtils.insertPzcOfficialMsg(userId, startUser.getUserId(),\n                    \"来自\" + my.getNickname() + \"与您的组队信息：\",\n                    \"很遗憾地通知您：您在【\" + pzcActivityGroup.getTitle() + \"】组队活动中，申请方已经取消本次组队活动。对方的违约金 【\" + finalMoney + \"派币】已纳入您的账户。您可以再次同意或者申请其他用户。\",\n                    pzcActivityGroup.getGroupId(), pzcActivityGroup.getActivityId());\n\n                //历史记录\n                wxUtils.insertUserHistory(userId, pzcActivityGroup.getActivityId(), 4L, \"在【\" + pzcActivityGroup.getTitle() + \"】活动中途取消组队，扣除违约金 【\" + pzcActivityGroup.getMoney() + \"】派币\", pzcActivityGroup.getMoney().negate());\n                wxUtils.insertUserHistory(startUser.getUserId(), pzcActivityGroup.getActivityId(), 2L, \"在【\" + pzcActivityGroup.getTitle() + \"】活动中途取消组队，违约金所得 【\" + pzcActivityGroup.getMoney().subtract(new BigDecimal(\"20\")) + \"】派币\", pzcActivityGroup.getMoney().subtract(new BigDecimal(\"20\")));\n\n            });\n\n\n        } else {\n            //我是发起方\n            PzcUser applyUser = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId());\n            applyUser.setMoney(applyUser.getMoney().\n                add(pzcActivityGroup.getMoney().subtract(new BigDecimal(\"20\")))\n                .add(pzcActivityGroupApplyVo.getMoney())); //全额返回给申请方的保证金\n            pzcUserMapper.updateById(applyUser);\n\n\n            newSingleThreadExecutor.execute(() -> {\n                //官方推送消息\n                wxUtils.insertPzcOfficialMsg(userId, applyUser.getUserId(),\n                    \"来自\" + my.getNickname() + \"与您的组队信息：\",\n                    \"很遗憾地通知您：您在在【\" + pzcActivityGroup.getTitle() + \"】组队活动中，发起方已经取消本次组队活动。对方的违约金 【\" + pzcActivityGroup.getMoney().subtract(new BigDecimal(\"20\")) + \"派币】已纳入您的账户。您可以再次同意或者申请其他用户。\",\n                    pzcActivityGroup.getGroupId(), pzcActivityGroup.getActivityId());\n\n                //历史记录\n                wxUtils.insertUserHistory(userId, pzcActivityGroup.getActivityId(), 4L, \"在【\" + pzcActivityGroup.getTitle() + \"】活动中途取消组队，违约金 【\" + pzcActivityGroup.getMoney() + \"】派币\", pzcActivityGroup.getMoney().negate());\n                wxUtils.insertUserHistory(applyUser.getUserId(), pzcActivityGroup.getActivityId(), 2L, \"在【\" + pzcActivityGroup.getTitle() + \"】活动中途取消组队，违约金所得 【\" + pzcActivityGroup.getMoney().subtract(new BigDecimal(\"20\")) + \"】派币\", pzcActivityGroup.getMoney().subtract(new BigDecimal(\"20\")));\n\n            });\n\n        }\n\n        newSingleThreadExecutor.execute(() -> {\n            // 更新 组队状态为已结束\n            pzcActivityGroup.setStatus(1); //已结束\n            pzcActivityGroupMapper.updateById(pzcActivityGroup);\n        });\n\n        return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), -1));//修改状态为 已取消\n    }\n\n    /**\n     * 我创建的活动的申请列表\n     * 思路整理\n     * 首先查出所有 GroupId\n     * 然后查出groupId 对应的申请列表\n     *\n     * @return\n     */\n    @GetMapping(\"/applyList\")\n    public R<List<PzcActivityGroupApplyVo>> applyList() {\n        PzcActivityGroupBo bo = new PzcActivityGroupBo();\n        bo.setUserId(LoginHelper.getUserId());\n        List<PzcActivityGroupVo> pzcActivityGroupVos = iPzcActivityGroupService.queryList(bo);\n        List<Long> groupIds = pzcActivityGroupVos.stream().map(PzcActivityGroupVo::getGroupId).collect(Collectors.toList());\n        if (groupIds.size() == 0) {\n            return R.ok();\n        }\n\n        List<PzcActivityGroupApplyVo> pzcActivityGroupApplyVos = iPzcActivityGroupApplyService.queryListByGroupIds(groupIds);\n        pzcActivityGroupApplyVos.forEach(\n            s -> {\n                PzcUser pzcUser = pzcUserMapper.selectById(s.getUserId());\n                s.setNickName(pzcUser.getNickname());\n                s.setAvatar(pzcUser.getAvatar());\n                Integer region = pzcActivityGroupVos.stream().filter(s1 -> s1.getGroupId().equals(s.getGroupId())).findFirst().get().getRegion();\n\n                String title = \"\";\n                PzcActivity pzcActivity = pzcActivityMapper.selectById(s.getActivityId());\n                if (s.getActivityId() == 0) {\n                    PzcRegion pzcRegion = pzcRegionMapper.selectById(region);\n                    if (pzcRegion != null) {\n                        title = \"【\" + pzcRegion.getName() + \"】\";\n                    }\n                    log.info(\"申请活动列表 title:{}\", title);\n\n                } else {\n                    if (pzcActivity != null) {\n                        title = pzcActivity.getTitle();\n                    } else {\n                        title = \"【活动已结束】\";\n                    }\n\n                }\n\n                s.setActivityTitle(title);\n                s.setGroupTitle(pzcActivityGroupVos.stream().filter(s1 -> s1.getGroupId().equals(s.getGroupId())).findFirst().get().getTitle());\n            }\n        );\n        List<PzcActivityGroupApplyVo> result = pzcActivityGroupApplyVos.stream().filter(s -> s.getApplyStatus() == 0).collect(Collectors.toList());//过滤掉已取消的\n        return R.ok(result);\n    }\n\n    @GetMapping(\"/userInfo\") //查看申请人或者发起人信息\n    public R userInfo(@RequestParam(\"userId\") Long userId, @RequestParam(\"groupId\") Long groupId) {\n        //首先查询该用户是否申请了我的组 申请了 才有资格去查看  这个不应该是申请的 人才能看   双方都能互相看\n        PzcActivityGroupApplyVo pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryByUserIdAndGroupId(userId, groupId);\n        if (pzcActivityGroupApplyVo == null) {\n            PzcActivityGroupVo pzcActivityGroupVo = iPzcActivityGroupService.queryById(groupId);\n            if (!userId.equals(pzcActivityGroupVo.getUserId())) {\n                return R.fail(\"无权查看该用户信息\");\n            }\n            pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryByUserIdAndGroupId(LoginHelper.getUserId(), groupId);\n\n        }\n        PzcUser pzcUser = pzcUserMapper.selectById(userId);\n        pzcUser.setMoney(null);\n        pzcUser.setUserPhoto(pzcUserPhotoMapper.selectList(new QueryWrapper<>(new PzcUserPhoto()).eq(\"user_id\", userId)));\n\n        PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(groupId);\n        pzcActivityGroup.setAddress(pzcActivityGroup.getAddress().substring(pzcActivityGroup.getAddress().indexOf(\"】\") + 1));\n\n        pzcActivityGroupApplyVo.setActivityTitle(pzcActivityGroup.getActivityName());\n        pzcUser.setPzcActivityGroupApplyVo(pzcActivityGroupApplyVo);\n        pzcUser.setPzcActivityGroup(pzcActivityGroup);\n        pzcUser.setLiveStatus(concurrentHashMap.get(userId) != null);\n        pzcUser.setNotReadCount(pzcUserTalkMapper.selectNotReadCount(userId, LoginHelper.getUserId(), LoginHelper.getUserId()));\n        pzcUser.setExemptCancel(pzcUserMapper.selectById(LoginHelper.getUserId()).getExemptCancel()); //获取我的免责取消次数\n        return R.ok(pzcUser);\n    }\n\n    /**\n     * 13 发起方已评价\n     * 14 申请方已评价\n     * 15 双方已评价\n     *\n     * @param applyId\n     * @return\n     */\n\n    @PostMapping(\"/pj\") //双方评价 （可选）\n    @Transactional\n    public R pj(@RequestParam(\"applyId\") Integer applyId, @RequestParam(\"score\") Integer score) {\n        wxUtils.checkApplyScore(score);\n        PzcActivityGroupApplyVo pzcActivityGroupApplyVo = wxUtils.checkApplyPj(applyId.longValue());\n        //首先获取我的UserId\n        Long userId = LoginHelper.getUserId();\n        Long groupId = pzcActivityGroupApplyVo.getGroupId();\n        Integer applyStatus = pzcActivityGroupApplyVo.getApplyStatus();\n        if (pzcActivityGroupApplyVo.getUserId().equals(userId)) {\n            //获取对方 userId 并且修改对方积分\n            Long otherUserId = pzcActivityGroupMapper.selectById(groupId).getUserId();\n            PzcUser otherUser = pzcUserMapper.selectById(otherUserId);\n            if (applyStatus == 13) {\n                return R.fail(\"您已经评价过了 不可重复操作\");\n            }\n            otherUser.setIntegration(otherUser.getIntegration() + score);\n            otherUser.setIntegrationNow(otherUser.getIntegrationNow() + score);\n            wxUtils.updateUserMsg(otherUser);\n\n            pzcUserMapper.updateById(otherUser);\n\n            if (applyStatus == 14) //发起方评价了\n            {\n                return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 15)); //双方都已评价\n            }\n            return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 13));//申请方评价\n        }\n        //判断当前 用户是否为组队发起人 如果不是 直接报错\n        PzcActivityGroupVo pzcActivityGroupVo = iPzcActivityGroupService.queryById(groupId);\n        if (pzcActivityGroupVo == null) {\n            return R.fail(\"组队不存在\");\n        }\n        if (!pzcActivityGroupVo.getUserId().equals(userId)) {\n            return R.fail(\"你不是组队发起人\");\n        }\n        if (applyStatus == 14) {\n            return R.fail(\"您已经评价过了 不可重复操作\");\n        }\n\n        // ============================================================================================\n        Long otherUserId = pzcActivityGroupApplyVo.getUserId();\n        PzcUser otherUser = pzcUserMapper.selectById(otherUserId);\n        otherUser.setIntegration(otherUser.getIntegration() + score);\n        otherUser.setIntegrationNow(otherUser.getIntegrationNow() + score);\n        wxUtils.updateUserMsg(otherUser);\n        pzcUserMapper.updateById(otherUser);\n\n\n        //看看申请方是否评价了\n        if (applyStatus == 13) //申请方评价了\n        {\n            return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 15)); //双方都已评价\n        }\n\n        return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 14));//发起方评价\n\n    }\n\n\n    /**\n     * 双方都到达了目的地 开始扣手续费\n     *\n     * @param applyId\n     * @return\n     */\n    @PostMapping(\"/confirmReach\") //确认到达目的地\n    @Transactional\n    public R confirmReach(@RequestParam(\"applyId\") Integer applyId) {\n        PzcActivityGroupApplyVo pzcActivityGroupApplyVo = wxUtils.checkApplyConfirm(applyId.longValue());\n        Long userId = LoginHelper.getUserId();\n        PzcUser my = pzcUserMapper.selectById(userId);\n        Long groupId = pzcActivityGroupApplyVo.getGroupId();\n        Integer applyStatus = pzcActivityGroupApplyVo.getApplyStatus();\n        if (applyStatus != 2 && applyStatus != 11 && applyStatus != 12) {\n            return R.fail(\"该订单目前状态为【\" + wxUtils.applyStatus(applyStatus) + \"】不可确认\");\n        }\n\n        //获取发起方的保证金\n        PzcActivityGroupVo pzcActivityGroupVo = iPzcActivityGroupService.queryById(groupId);\n        if (pzcActivityGroupVo == null) {\n            return R.fail(\"组队不存在\");\n        }\n        PzcActivity pzcActivity = pzcActivityMapper.selectById(pzcActivityGroupVo.getActivityId());\n        if (pzcActivityGroupApplyVo.getUserId().equals(userId)) { //我是申请方\n            if (applyStatus == 11) //发起方确认了\n            {\n\n                BigDecimal money = pzcActivityGroupVo.getMoney();\n                money = money.subtract(new BigDecimal(\"10\"));\n                //将保证金还给发起方\n                PzcUser pzcUser = pzcUserMapper.selectById(pzcActivityGroupVo.getUserId());\n                pzcUser.setMoney(pzcUser.getMoney().add(money));\n                pzcUserMapper.updateById(pzcUser);\n                //========================================================================\n                //获取申请方的保证金\n                PzcUser applyUser = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId());\n                BigDecimal applyMoney = pzcActivityGroupApplyVo.getMoney();\n                applyMoney = applyMoney.subtract(new BigDecimal(\"10\"));\n                //将保证金还给申请方\n                applyUser.setMoney(applyUser.getMoney().add(applyMoney));\n                pzcUserMapper.updateById(applyUser);\n\n\n                BigDecimal finalMoney = money;\n                BigDecimal finalApplyMoney = applyMoney;\n                newSingleThreadExecutor.execute(() -> {\n                    wxUtils.insertUserHistory(pzcActivityGroupVo.getUserId(), pzcActivityGroupVo.getActivityId(), 2L, \"组队完成退还保证金 【\" + finalMoney + \"】 派币\", finalMoney);\n                    wxUtils.insertUserHistory(pzcActivityGroupApplyVo.getUserId(), pzcActivityGroupVo.getActivityId(), 2L, \"组队完成退还保证金 【\" + finalApplyMoney + \"】 派币\", finalMoney);\n\n                    wxUtils.insertUserHistory(pzcActivityGroupVo.getUserId(), pzcActivityGroupVo.getActivityId(), 1L, JsonUtils.toJsonString(pzcActivity), null);\n                    wxUtils.insertUserHistory(pzcActivityGroupApplyVo.getUserId(), pzcActivityGroupVo.getActivityId(), 1L, JsonUtils.toJsonString(pzcActivity), null);\n\n\n                    PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupApplyVo.getGroupId());\n                    // 更新 组队状态为已结束\n                    pzcActivityGroup.setStatus(1); //已结束\n                    pzcActivityGroupMapper.updateById(pzcActivityGroup);\n\n\n                    //更新双方积分\n                    PzcUser pzcUser1 = pzcUserMapper.selectById(pzcActivityGroup.getUserId());\n                    PzcUser pzcUser2 = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId());\n                    pzcUser1.setIntegration(pzcUser1.getIntegration() + 3);\n                    pzcUser1.setIntegrationNow(pzcUser1.getIntegrationNow() + 3);\n                    pzcUser2.setIntegration(pzcUser2.getIntegration() + 3);\n                    pzcUser2.setIntegrationNow(pzcUser2.getIntegrationNow() + 3);\n                    wxUtils.updateUserMsg(pzcUser1);\n                    wxUtils.updateUserMsg(pzcUser2);\n                });\n\n\n                return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 3)); //双方都已确认\n            }\n            if (applyStatus == 12) {\n                return R.fail(\"您已经确认过了 不可重复操作\");\n            }\n            //申请方确认了 给发起方推送微信消息\n            PzcUser pzcUser = pzcUserMapper.selectById(pzcActivityGroupVo.getUserId());\n            Map<String, Map> dataMap = new HashMap<>();\n            dataMap.put(\"thing4\", MapBuilder.create().put(\"value\", my.getNickname()).build());\n            dataMap.put(\"thing5\", MapBuilder.create().put(\"value\", \"对方已到达，您需在活动时间内到达\").build());\n            dataMap.put(\"time6\", MapBuilder.create().put(\"value\", DateUtils.format(new Date(), \"yyyy-MM-dd HH:mm:ss\")).build());\n            log.info(\"发起方确认到达目的地，给申请方推送微信消息：{}\", JsonUtils.toJsonString(dataMap));\n\n            newSingleThreadExecutor.execute(() -> {\n                wxUtils.sendArriveMsg(pzcUser.getOpenid(), dataMap);\n            });\n\n            return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 12));//申请方确认\n        }\n        //判断当前 用户是否为组队发起人 如果不是 直接报错\n\n        if (!pzcActivityGroupVo.getUserId().equals(userId)) {\n            return R.fail(\"你不是组队发起人\");\n        }\n        //看看申请方是否确认了\n        if (applyStatus == 12) //我是发起方\n        {\n            //获取发起方的保证金\n            BigDecimal money = pzcActivityGroupVo.getMoney();\n            money = money.subtract(new BigDecimal(\"10\"));\n            //将保证金还给发起方\n            PzcUser pzcUser = pzcUserMapper.selectById(pzcActivityGroupVo.getUserId());\n            pzcUser.setMoney(pzcUser.getMoney().add(money));\n            pzcUserMapper.updateById(pzcUser);\n            //========================================================================\n            //获取申请方的保证金\n            PzcUser applyUser = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId());\n            BigDecimal applyMoney = pzcActivityGroupApplyVo.getMoney();\n            applyMoney = applyMoney.subtract(new BigDecimal(\"10\"));\n            //将保证金还给申请方\n            applyUser.setMoney(applyUser.getMoney().add(applyMoney));\n            pzcUserMapper.updateById(applyUser);\n\n\n            BigDecimal finalMoney = money;\n            BigDecimal finalApplyMoney = applyMoney;\n            newSingleThreadExecutor.execute(() -> {\n\n                wxUtils.insertUserHistory(pzcActivityGroupVo.getUserId(), pzcActivityGroupVo.getActivityId(), 2L, \"组队完成退还保证金 \" + finalMoney + \" 派币\", finalMoney);\n                wxUtils.insertUserHistory(pzcActivityGroupApplyVo.getUserId(), pzcActivityGroupVo.getActivityId(), 2L, \"组队完成退还保证金 \" + finalApplyMoney + \" 派币\", finalMoney);\n\n                wxUtils.insertUserHistory(pzcActivityGroupVo.getUserId(), pzcActivityGroupVo.getActivityId(), 1L, JsonUtils.toJsonString(pzcActivity), null);\n                wxUtils.insertUserHistory(pzcActivityGroupApplyVo.getUserId(), pzcActivityGroupVo.getActivityId(), 1L, JsonUtils.toJsonString(pzcActivity), null);\n\n\n                PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupApplyVo.getGroupId());\n                // 更新 组队状态为已结束\n                pzcActivityGroup.setStatus(1); //已结束\n                pzcActivityGroupMapper.updateById(pzcActivityGroup);\n\n                //更新双方积分\n                PzcUser pzcUser1 = pzcUserMapper.selectById(pzcActivityGroup.getUserId());\n                PzcUser pzcUser2 = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId());\n                pzcUser1.setIntegration(pzcUser1.getIntegration() + 3);\n                pzcUser1.setIntegrationNow(pzcUser1.getIntegrationNow() + 3);\n                pzcUser2.setIntegration(pzcUser2.getIntegration() + 3);\n                pzcUser2.setIntegrationNow(pzcUser2.getIntegrationNow() + 3);\n                wxUtils.updateUserMsg(pzcUser1);\n                wxUtils.updateUserMsg(pzcUser2);\n            });\n            return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 3)); //双方都已确认\n        }\n        if (applyStatus == 11) {\n            return R.fail(\"您已经确认过了 不可重复操作\");\n        }\n        //发起方确认了 给申请方推送微信消息\n        PzcUser pzcUser = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId());\n        Map<String, Map<Object, Object>> dataMap = new HashMap<>();\n        dataMap.put(\"thing4\", MapBuilder.create().put(\"value\", my.getNickname()).build());\n        dataMap.put(\"thing5\", MapBuilder.create().put(\"value\", \"对方已到达，您需在活动时间内到达\").build()); //对方已到达约定的见面地点，您需在【\"+pzcActivityGroupVo.getActivityTime()+\"】前到达目的地，否则，按照违约处理。或您也可以与对方沟通，申请无限制确认到达，以保证组队的正常。\n        dataMap.put(\"time6\", MapBuilder.create().put(\"value\", DateUtils.format(new Date(), \"yyyy-MM-dd HH:mm:ss\")).build());\n        log.info(\"申请方确认到达目的地，给发起方推送微信消息：{}\", JsonUtils.toJsonString(dataMap));\n        newSingleThreadExecutor.execute(() -> {\n            wxUtils.sendArriveMsg(pzcUser.getOpenid(), dataMap);\n        });\n\n        return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 11));//发起方确认\n    }\n\n\n    @GetMapping(\"/applyRole\")\n    public R applyRole(@RequestParam(\"applyId\") Integer applyId) {\n        Long userId = LoginHelper.getUserId();\n        PzcActivityGroupApplyVo pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryById(applyId.longValue());\n        if (pzcActivityGroupApplyVo.getUserId().equals(userId)) {\n            return R.ok(0); //我是申请方\n        } else {\n            return R.ok(1); //我是发起方\n        }\n    }\n\n\n    @PostMapping(\"/confirm\") //确认申请 (这里判断 保证金 是否足够缴纳 保证 新人卡 bug)\n    @Transactional\n    public R confirm(@RequestParam(\"applyId\") Integer applyId) {\n        PzcActivityGroupApplyVo pzcActivityGroupApplyVo = wxUtils.checkApplyConfirm(applyId.longValue());\n        Long userId = LoginHelper.getUserId();\n        Long groupId = pzcActivityGroupApplyVo.getGroupId();\n        Integer applyStatus = pzcActivityGroupApplyVo.getApplyStatus();\n\n        if (applyStatus == 2) {\n            return R.fail(\"该订单进行中，不可确认\");\n        }\n        PzcActivityGroupVo group = iPzcActivityGroupService.queryById(groupId);\n        //获取双方保证金\n        BigDecimal applyMoney = pzcActivityGroupApplyVo.getMoney();\n        BigDecimal startMoney = group.getMoney();\n        //获取双方\n        PzcUser applyUser = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId());\n        PzcUser startUser = pzcUserMapper.selectById(group.getUserId());\n\n        //如果可以确认 判断 是那一方确认的\n        if (pzcActivityGroupApplyVo.getUserId().equals(userId)) {// 自己是申请方 申请方还得等发起方最后确认时间地点\n            if (applyStatus == 10) {\n                return R.fail(\"您已经确认过了 不可重复操作\");\n            }\n\n            //如果是自己确认的 则修改状态为 已确认\n            if (applyStatus == 9) //发起方确认了\n            {\n                log.info(\"申请方的余额与保证金 {}----{}\", applyUser.getMoney(), applyMoney);\n                // 如果有一方保证金不足以缴纳 则报错\n                if (applyUser.getMoney().compareTo(applyMoney) < 0) {\n                    return R.fail(\"您的保证金不足以缴纳\");\n                }\n\n\n                //双方都已确认 开始 扣除保证金\n                //获取申请方保证金\n                applyUser.setMoney(applyUser.getMoney().subtract(applyMoney));\n                pzcUserMapper.updateById(applyUser);\n                //获取发起方保证金\n                startUser.setMoney(startUser.getMoney().subtract(startMoney));\n                pzcUserMapper.updateById(startUser);\n\n                //存入历史记录\n                wxUtils.insertUserHistory(pzcActivityGroupApplyVo.getUserId(), pzcActivityGroupApplyVo.getActivityId(), 3L, \"组队开始扣除保证金 【\" + applyMoney + \"】 派币\", applyMoney.negate());\n                wxUtils.insertUserHistory(group.getUserId(), pzcActivityGroupApplyVo.getActivityId(), 3L, \"组队开始扣除保证金 【\" + startMoney + \"】 派币\", startMoney.negate());\n\n                return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 2)); //双方都已确认\n            } else {\n                return R.fail(\"发起方还未确认最后时间地点,请继续保持沟通哦\");\n            }\n\n        } else { //自己是发起方\n            //判断当前 用户是否为组队发起人 如果不是 直接报错‘\n            PzcActivityGroupVo pzcActivityGroupVo = iPzcActivityGroupService.queryById(groupId);\n            if (pzcActivityGroupVo == null) {\n                return R.fail(\"组队不存在\");\n            }\n            if (!pzcActivityGroupVo.getUserId().equals(userId)) {\n                return R.fail(\"你不是组队发起人\");\n            }\n            if (applyStatus == 9) {\n                return R.fail(\"您已经确认过了 不可重复操作\");\n            }\n\n            //看看申请方是否确认了\n            if (applyStatus == 10) //申请方确认了\n            {\n\n                log.info(\"发起方的余额与保证金 {}----{}\", startUser.getMoney(), startMoney);\n                if (startUser.getMoney().compareTo(startMoney) < 0) {\n                    return R.fail(\"您的的保证金不足以缴纳\");\n                }\n\n                //双方都已确认 开始 扣除保证金\n                //获取申请方保证金\n                applyUser.setMoney(applyUser.getMoney().subtract(applyMoney));\n                pzcUserMapper.updateById(applyUser);\n                //获取发起方保证金\n                startUser.setMoney(startUser.getMoney().subtract(startMoney));\n                pzcUserMapper.updateById(startUser);\n\n                newSingleThreadExecutor.execute(() -> {\n                    //存入历史记录\n                    wxUtils.insertUserHistory(pzcActivityGroupApplyVo.getUserId(), pzcActivityGroupApplyVo.getActivityId(), 3L, \"组队开始扣除保证金 【\" + applyMoney + \"】 派币\", applyMoney.negate());\n                    wxUtils.insertUserHistory(group.getUserId(), pzcActivityGroupApplyVo.getActivityId(), 3L, \"组队开始扣除保证金 【\" + startMoney + \"】 派币\", startMoney.negate());\n\n                });\n\n                return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 2)); //双方都已确认\n            }\n            return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 9));//发起方确认\n        }\n    }\n\n\n    /**\n     * 同意用户申请 进入下一阶段\n     * 同意用户申请时 先判断对方是否  处于组队进程\n     * <p>\n     * <p>\n     * -1 已取消 0 位于申请列表中 1 申请通过待确认时\n     * 2 确认通过进行中 3 组队结束  9发起方已确认\n     * 10申请方已确认 11 发起方已打卡 12 申请方已打卡\n     * 13 申请方已评价 14 发起方已评价 15 双方已评价\n     *\n     * @return\n     */\n    @PostMapping(\"/apply\")\n    public R apply(@RequestParam(\"applyId\") Long applyId) {\n        //首先查询这个组队是否是我发起的\n        PzcActivityGroupApplyVo pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryById(applyId);\n        if (pzcActivityGroupApplyVo == null) {\n            return R.fail(\"申请不存在\");\n        }\n        Long userId = LoginHelper.getUserId();\n        PzcUser my = pzcUserMapper.selectById(userId);\n\n        Long groupId = pzcActivityGroupApplyVo.getGroupId();\n        PzcActivityGroupVo pzcActivityGroupVo = iPzcActivityGroupService.queryById(groupId);\n        if (pzcActivityGroupVo == null) {\n            return R.fail(\"组队不存在\");\n        }\n        if (!pzcActivityGroupVo.getUserId().equals(userId)) {\n            return R.fail(\"你不是组队发起人\");\n        }\n\n        //判断对方是否 处于 组队进程 如果是 则不可同意\n        Long applyUserId = pzcActivityGroupApplyVo.getUserId();\n        //获取活动Id\n        Long activityId = pzcActivityGroupVo.getActivityId();\n        //获取活动组队列表 除了自己这个队伍外\n        List<PzcActivityGroup> groups = pzcActivityGroupMapper.selectList(new QueryWrapper<PzcActivityGroup>().eq(\"activity_id\", activityId));\n        List<Long> groupIds = groups.stream().filter(s -> !s.getGroupId().equals(groupId)).map(PzcActivityGroup::getGroupId).collect(Collectors.toList());\n        if (groupIds.size() != 0) {\n            //然后获取当前对方申请了几个队伍 判断每个队伍的进程 如果有 进程处于 组队状态中 则不可以同意\n            List<PzcActivityGroupApply> applies = pzcActivityGroupApplyMapper.selectList(new QueryWrapper<PzcActivityGroupApply>().in(\"group_id\", groupIds).eq(\"user_id\", applyUserId));\n            applies.forEach(\n                a -> {\n                    if (a.getApplyStatus() != 3 && a.getApplyStatus() != 13 && a.getApplyStatus() != 14 && a.getApplyStatus() != 15 && a.getApplyStatus() != -1) {\n                        log.info(\"对方当前进程为 {} 详细信息为 {}\", a.getApplyStatus(), JsonUtils.toJsonString(a));\n                        throw new RuntimeException(\"该用户已经处于组队进程中 等待对方结束活动再试哦\");\n                    }\n                }\n            );\n        }\n\n        //判断一下 我是否在当前活动下 有未完成的组队 如果有就不能 同意其他的申请\n        List<PzcActivityGroupApply> applies = pzcActivityGroupApplyMapper.selectList(new QueryWrapper<PzcActivityGroupApply>().eq(\"group_id\", groupId).\n            eq(\"activity_id\", activityId).notIn(\"apply_status\",-1,0,3,13,14,15));\n\n        log.info(\"当前申请人的申请列表为：{}\",JSONUtil.toJsonPrettyStr(applies));\n        if(applies.size()>1)\n        {\n            return R.fail(\"您当前有未完成的组队，无法同意其他申请\");\n        }\n\n        //修改状态为 已同意\n        Integer integer = iPzcActivityGroupApplyService.updateStatus(applyId, 1);\n        if (integer == null || integer != 1) {\n            return R.fail(\"修改状态失败\");\n        }\n\n        //给对方发消息 已经同意了对方的申请 请尽快确认\n        PzcUser otherUser = pzcUserMapper.selectById(applyUserId);\n        wxUtils.insertPzcOfficialMsg(my.getUserId(), otherUser.getUserId(),\"来自\" + my.getNickname() + \"与您的组队信息：\",\"您的组队申请已经被对方同意，请尽快确认~\",groupId,activityId);\n\n        try {\n            // 创建一个任务逻辑，接受参数并在任务执行时使用\n            ScheduledExecutorUtils.RunnableWithParams task = (params) -> {\n                System.out.println(\"Task executed at: \" + new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\").format(new Date()));\n                if (params.length > 0) {\n                    System.out.println(\"Parameter received: \" + params[0]);\n                }\n                // 在这里添加你的任务逻辑\n                PzcActivityGroupApply pzcActivityGroupApply = pzcActivityGroupApplyMapper.selectById((Serializable) params[1]);\n                log.info(\"定时任务执行中，当前申请信息为：{}\", JsonUtils.toJsonString(pzcActivityGroupApply));\n\n                if (pzcActivityGroupApply.getApplyStatus() == 1) {\n                    //如果对方还未确认 则修改状态为 已取消\n                    iPzcActivityGroupApplyService.updateStatus((Long) params[1], -1);\n                    //发起方扣除10派币或者免责取消的一次机会\n                    PzcUser pzcUser = pzcUserMapper.selectById(pzcActivityGroupVo.getUserId());\n                    if (pzcUser.getExemptCancel() == 0) {\n                        pzcUser.setMoney(pzcUser.getMoney().subtract(new BigDecimal(\"10\")));\n                        pzcUserMapper.updateById(pzcUser);\n                        wxUtils.insertUserHistory(pzcActivityGroupVo.getUserId(), pzcActivityGroupVo.getActivityId(), 3L, \"组队取消扣除保证金 【10】 派币\", new BigDecimal(\"10\").negate());\n                    } else {\n                        pzcUser.setExemptCancel(pzcUser.getExemptCancel() - 1);\n                        pzcUserMapper.updateById(pzcUser);\n                    }\n                    //修改组队状态为已完成\n                    PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupVo.getGroupId());\n                    pzcActivityGroup.setStatus(1); //已结束\n                    pzcActivityGroupMapper.updateById(pzcActivityGroup);\n\n\n                    //给对方发消息 对方未确认 申请已取消\n                    PzcOfficial pzcOfficial1 = new PzcOfficial();\n                    pzcOfficial1.setIsRead(0L);\n                    PzcUser otherUser1 = pzcUserMapper.selectById(pzcActivityGroupApply.getUserId());\n                    pzcOfficial1.setToUserId(otherUser1.getUserId());\n                    pzcOfficial1.setTitle(\"派之城官方提醒您：\");\n                    pzcOfficial1.setContent(\"您好，由于对方未确认地址，您的组队已取消。平台已对对方进行相应惩罚。\");\n                    pzcOfficial1.setGroupId(groupId);\n                    pzcOfficial1.setActivityId(activityId);\n                    pzcOfficialMapper.insert(pzcOfficial1);\n\n                    PzcOfficial pzcOfficial2 = new PzcOfficial();\n                    pzcOfficial2.setIsRead(0L);\n                    pzcOfficial2.setToUserId(pzcActivityGroupVo.getUserId());\n                    pzcOfficial2.setTitle(\"派之城官方提醒您：\");\n                    pzcOfficial2.setContent(\"您好，由于您未进行确认地址，您的组队已取消，平台扣除您一次免责取消机会或10派币。(具体根据您免责机会次数确定）\");\n                    pzcOfficial2.setGroupId(groupId);\n                    pzcOfficial2.setActivityId(activityId);\n                    pzcOfficialMapper.insert(pzcOfficial2);\n                }\n                if (pzcActivityGroupApply.getApplyStatus() == 9) { //发起方确认了地址 而申请方未确认\n                    //如果对方还未确认 则修改状态为 已取消\n                    iPzcActivityGroupApplyService.updateStatus((Long) params[1], -1);\n                    //申请方扣除10派币或者免责取消的一次机会\n                    PzcUser pzcUser = pzcUserMapper.selectById(pzcActivityGroupApply.getUserId());\n                    if (pzcUser.getExemptCancel() == 0) {\n                        pzcUser.setMoney(pzcUser.getMoney().subtract(new BigDecimal(\"10\")));\n                        pzcUserMapper.updateById(pzcUser);\n                        wxUtils.insertUserHistory(pzcActivityGroupApply.getUserId(), pzcActivityGroupApply.getActivityId(), 3L, \"组队取消扣除保证金 【10】 派币\", new BigDecimal(\"10\").negate());\n                    } else {\n                        pzcUser.setExemptCancel(pzcUser.getExemptCancel() - 1);\n                        pzcUserMapper.updateById(pzcUser);\n                    }\n                    //修改组队状态为已完成\n                    PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupVo.getGroupId());\n                    pzcActivityGroup.setStatus(1); //已结束\n                    pzcActivityGroupMapper.updateById(pzcActivityGroup);\n\n                    //给对方发消息 对方未确认 申请已取消\n                    PzcOfficial pzcOfficial1 = new PzcOfficial();\n                    pzcOfficial1.setIsRead(0L);\n                    PzcUser otherUser1 = pzcUserMapper.selectById(pzcActivityGroupApply.getUserId()); //申请方\n                    pzcOfficial1.setToUserId(otherUser1.getUserId());\n                    pzcOfficial1.setTitle(\"派之城官方提醒您：\");\n                    pzcOfficial1.setContent(\"您好，由于您未进行确认地址，您的组队已取消，平台扣除您一次免责取消机会或10派币。(具体根据您免责机会次数确定）\");\n                    pzcOfficial1.setGroupId(groupId);\n                    pzcOfficial1.setActivityId(activityId);\n                    pzcOfficialMapper.insert(pzcOfficial1);\n\n                    PzcOfficial pzcOfficial2 = new PzcOfficial();\n                    pzcOfficial2.setIsRead(0L);\n                    pzcOfficial2.setToUserId(pzcActivityGroupVo.getUserId()); //发起方\n                    pzcOfficial2.setTitle(\"派之城官方提醒您：\");\n                    pzcOfficial2.setContent(\"您好，由于对方未确认地址，您的组队已取消。平台已对对方进行相应惩罚。\");\n                    pzcOfficial2.setGroupId(groupId);\n                    pzcOfficial2.setActivityId(activityId);\n                    pzcOfficialMapper.insert(pzcOfficial2);\n                }\n                if (pzcActivityGroupApply.getApplyStatus() == 10) { //申请方确认了地址\n                    //如果对方还未确认 则修改状态为 已取消\n                    iPzcActivityGroupApplyService.updateStatus((Long) params[1], -1);\n                    //修改组队状态为已完成\n                    PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupVo.getGroupId());\n                    pzcActivityGroup.setStatus(1); //已结束\n                    pzcActivityGroupMapper.updateById(pzcActivityGroup);\n\n                    //双方均扣除10派币\n                    PzcUser startUser = pzcUserMapper.selectById(pzcActivityGroupVo.getUserId());\n\n                    startUser.setMoney(startUser.getMoney().subtract(new BigDecimal(\"10\")));\n                    pzcUserMapper.updateById(startUser);\n                    wxUtils.insertUserHistory(pzcActivityGroupVo.getUserId(), pzcActivityGroupVo.getActivityId(), 3L, \"组队取消扣除保证金 【10】 派币\", new BigDecimal(\"10\").negate());\n\n                    PzcUser applyUser = pzcUserMapper.selectById(pzcActivityGroupApply.getUserId());\n                    applyUser.setMoney(applyUser.getMoney().subtract(new BigDecimal(\"10\")));\n                    pzcUserMapper.updateById(applyUser);\n                    wxUtils.insertUserHistory(pzcActivityGroupApply.getUserId(), pzcActivityGroupApply.getActivityId(), 3L, \"组队取消扣除保证金 【10】 派币\", new BigDecimal(\"10\").negate());\n                }\n\n\n                if (pzcActivityGroupApply.getApplyStatus() == 11 || pzcActivityGroupApply.getApplyStatus() == 12) {\n                    //如果对方还未确认 则修改状态为 已取消\n                    iPzcActivityGroupApplyService.updateStatus((Long) params[1], -1);\n                    //修改组队状态为已完成\n                    PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupVo.getGroupId());\n                    pzcActivityGroup.setStatus(1); //已结束\n                    pzcActivityGroupMapper.updateById(pzcActivityGroup);\n                    if (pzcActivityGroupApply.getApplyStatus() == 11) //发起方打卡了 申请方 全责\n                    {\n                        BigDecimal money = pzcActivityGroupApply.getMoney();\n\n                        PzcOfficial pzcOfficial1 = new PzcOfficial();\n                        pzcOfficial1.setIsRead(0L);\n                        pzcOfficial1.setFromUserId(null);\n                        pzcOfficial1.setToUserId(pzcActivityGroupApply.getUserId());\n                        pzcOfficial1.setTitle(\"派之城提醒您：\");\n                        pzcOfficial1.setContent(\"您好，由于您在活动结束前未进行到约定地方打卡签到，平台已对您进行违约处理，已扣除您全部保障金。\");\n                        pzcOfficial1.setGroupId(groupId);\n                        pzcOfficial1.setActivityId(activityId);\n                        pzcOfficialMapper.insert(pzcOfficial1);\n\n                        PzcUser pzcUser1 = pzcUserMapper.selectById(pzcActivityGroupVo.getUserId());\n                        pzcUser1.setMoney(pzcUser1.getMoney().add(pzcActivityGroup.getMoney()).add(money.subtract(new BigDecimal(\"20\"))));\n                        pzcUserMapper.updateById(pzcUser1); //发起方获得申请方 扣除20派币 后的保证金\n\n                        PzcOfficial pzcOfficial2 = new PzcOfficial();\n                        pzcOfficial2.setIsRead(0L);\n                        pzcOfficial2.setFromUserId(null);\n                        pzcOfficial2.setToUserId(pzcActivityGroupVo.getUserId());\n                        pzcOfficial2.setTitle(\"派之城提醒您：\");\n                        pzcOfficial2.setContent(\"您好，您在【\" + pzcActivityGroupVo.getTitle() + \"】组队活动中，由于对方未在活动结束前进行签到打卡，平台已对对方进行违约处理。对方的违约金【\" + money.subtract(new BigDecimal(\"20\")) + \"】派币已纳入您的账户。您可以再次同意或申请其他用户。\");\n                        pzcOfficial2.setGroupId(groupId);\n                        pzcOfficial2.setActivityId(activityId);\n                        pzcOfficialMapper.insert(pzcOfficial2);\n\n                        wxUtils.insertUserHistory(pzcActivityGroupApply.getUserId(), pzcActivityGroupApply.getActivityId(), 3L, \"活动时间到 自动取消组队 扣除保证金 【\" + money + \"】 派币\", money.negate());\n\n\n                    }\n                    if (pzcActivityGroupApply.getApplyStatus() == 12) //申请方打卡了 发起方 全责\n                    {\n\n                        BigDecimal money = pzcActivityGroupVo.getMoney();\n\n\n                        PzcOfficial pzcOfficial1 = new PzcOfficial();\n                        pzcOfficial1.setIsRead(0L);\n                        pzcOfficial1.setFromUserId(null);\n                        pzcOfficial1.setToUserId(pzcActivityGroupVo.getUserId());\n                        pzcOfficial1.setTitle(\"派之城提醒您：\");\n                        pzcOfficial1.setContent(\"您好，由于您在活动结束前未进行到约定地方打卡签到，平台已对您进行违约处理，已扣除您全部保障金。\");\n                        pzcOfficial1.setGroupId(groupId);\n                        pzcOfficial1.setActivityId(activityId);\n                        pzcOfficialMapper.insert(pzcOfficial1);\n\n\n                        PzcUser pzcUser1 = pzcUserMapper.selectById(pzcActivityGroupApply.getUserId());\n                        pzcUser1.setMoney(pzcUser1.getMoney().add(pzcActivityGroupApply.getMoney()).add(money.subtract(new BigDecimal(\"20\"))));\n                        pzcUserMapper.updateById(pzcUser1); //申请方获得发起方 扣除20派币 后的保证金\n\n                        PzcOfficial pzcOfficial2 = new PzcOfficial();\n                        pzcOfficial2.setIsRead(0L);\n                        pzcOfficial2.setFromUserId(null);\n                        pzcOfficial2.setToUserId(pzcActivityGroupApply.getUserId());\n                        pzcOfficial2.setTitle(\"派之城提醒您：\");\n                        pzcOfficial2.setContent(\"您好，您在【\" + pzcActivityGroupVo.getTitle() + \"】组队活动中，由于对方未在活动结束前进行签到打卡，平台已对对方进行违约处理。对方的违约金【\" + money.subtract(new BigDecimal(\"20\")) + \"】派币已纳入您的账户。您可以再次同意或申请其他用户。\");\n                        pzcOfficial2.setGroupId(groupId);\n                        pzcOfficial2.setActivityId(activityId);\n                        pzcOfficialMapper.insert(pzcOfficial2);\n\n\n                        wxUtils.insertUserHistory(pzcActivityGroupVo.getUserId(), pzcActivityGroupVo.getActivityId(), 3L, \"活动时间到 自动取消组队 扣除保证金 【\" + money + \"】 派币\", money.negate());\n\n                    }\n\n                }\n            };\n            ScheduledExecutorUtils.scheduleTask(new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\").format(pzcActivityGroupVo.getActivityTime()), task, groupId, applyId);\n        } catch (ParseException e) {\n            log.info(\"创建定时任务失败,活动信息为 {}\", JsonUtils.toJsonString(pzcActivityGroupVo));\n        }\n\n        return R.ok();\n    }\n\n\n    /**\n     * 查询活动组队列表\n     */\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcActivityGroupVo> list(PzcActivityGroupBo bo, PageQuery pageQuery) {\n        log.info(\"组队大厅 查询条件是： {}\", JsonUtils.toJsonString(bo));\n        return iPzcActivityGroupService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 导出活动组队列表\n     */\n    @SaCheckPermission(\"system:activityGroup:export\")\n    @Log(title = \"活动组队\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcActivityGroupBo bo, HttpServletResponse response) {\n        List<PzcActivityGroupVo> list = iPzcActivityGroupService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"活动组队\", PzcActivityGroupVo.class, response);\n    }\n\n    /**\n     * 获取活动组队详细信息\n     *\n     * @param groupId 主键\n     */\n    @GetMapping(\"/{groupId}\")\n    public R<PzcActivityGroupVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                         @PathVariable Long groupId) {\n        return R.ok(iPzcActivityGroupService.queryById(groupId));\n    }\n\n\n    @Autowired\n    private PzcRegionMapper pzcRegionMapper;\n\n    /**\n     * 发起活动组队\n     */\n    @Log(title = \"活动组队\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcActivityGroupBo bo) {\n        Long userId = LoginHelper.getUserId();\n        bo.setUserId(userId);\n\n        wxUtils.checkMgc(bo.getTitle());//校验敏感词\n        //校验活动是否存在\n        if (!iPzcActivityGroupService.checkActivity(bo.getActivityId()) && bo.getActivityId() != 0) { //如果不是城市活动 并且活动id不为0 则校验活动是否存在\n            return R.fail(\"活动不存在\");\n        }\n        PzcRegion pzcRegion = pzcRegionMapper.selectById(bo.getRegion());\n        //校验城市是否存在 只对派对生效\n        if (pzcRegion == null && pzcActivityMapper.selectById(bo.getActivityId()).getClassify() != 0) {\n            log.info(\"传入的城市Id is {} \", bo.getRegion());\n            return R.fail(\"当前城市不存在\");\n        }\n        if(bo.getMoney().compareTo(new BigDecimal(99))<0)\n        {\n            return R.fail(\"派币保证金至少为99\");\n        }\n\n        //检验自己是否已经在此城市里发起过组队\n        if (bo.getActivityId() == 0) {\n            //如果是城市活动 则校验是否已经发起过组队\n            List<PzcActivityGroup> groups = pzcActivityGroupMapper.selectList(new QueryWrapper<PzcActivityGroup>().\n                eq(\"user_id\", userId).eq(\"activity_id\", 0).eq(\"region\", bo.getRegion()).eq(\"status\", 0));\n            if (groups.size() != 0) {\n                log.info(\"用户id为：{} 在城市id为：{} 发起过组队 {}\", userId, bo.getRegion(), JsonUtils.toJsonString(groups));\n                return R.fail(\"您已经在此城市发起过组队了\");\n            }\n            bo.setActivityName(\"【\" + pzcRegion.getName() + \"】\");\n        } else {\n            //如果是活动 则校验是否已经发起过组队 //并且状态不为已结束\n            List<PzcActivityGroup> groups = pzcActivityGroupMapper.selectList(new QueryWrapper<PzcActivityGroup>().eq(\"user_id\", userId).eq(\"activity_id\", bo.getActivityId()).eq(\"status\", 0));\n            if (groups.size() != 0) {\n                log.info(\"用户id为：{} 在活动id为：{} 发起过组队 {}\", userId, bo.getActivityId(), JsonUtils.toJsonString(groups));\n                return R.fail(\"您已经在此活动发起过组队了\");\n            }\n            bo.setActivityName(pzcActivityMapper.selectById(bo.getActivityId()).getTitle());\n        }\n\n        // 校验保证金\n        PzcUser pzcUser = pzcUserMapper.selectById(userId);\n        if (pzcUser.getMoney().compareTo(bo.getMoney()) < 0 || bo.getMoney().compareTo(new BigDecimal(99)) < 0) {\n            return R.fail(\"保证金不足 至少拥有99个派币\");\n        }\n\n\n        return toAjax(iPzcActivityGroupService.insertByBo(bo));\n    }\n\n    /**\n     * 修改活动组队\n     */\n    @Log(title = \"活动组队\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcActivityGroupBo bo) {\n        bo.setUserId(LoginHelper.getUserId());\n        if(bo.getMoney().compareTo(new BigDecimal(99))<0)\n        {\n            return R.fail(\"保证金至少为99派币\");\n        }\n        //判断是否在组队进程中 判断组队状态\n        //获取我的申请列表\n        List<PzcActivityGroupApply> applies = pzcActivityGroupApplyMapper.selectList(new QueryWrapper<PzcActivityGroupApply>().eq(\"group_id\", bo.getGroupId()));\n        //判断是否有正在进行中的订单\n        applies.forEach(\n            a -> {\n                if (a.getApplyStatus() != -1 && a.getApplyStatus() != 0 && a.getApplyStatus() != 1) {\n                    throw new RuntimeException(\"当前活动处于\" + wxUtils.applyStatus(a.getApplyStatus()) + \" 无法修改\");\n                }\n            }\n        );\n\n        return toAjax(iPzcActivityGroupService.updateByBo(bo));\n    }\n\n    /**\n     * 删除活动组队\n     *\n     * @param groupIds 主键串\n     */\n    @SaCheckPermission(\"system:activityGroup:remove\")\n    @Log(title = \"活动组队\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{groupIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Long[] groupIds) {\n        return toAjax(iPzcActivityGroupService.deleteWithValidByIds(Arrays.asList(groupIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcArtistController.java",
    "content": "package top.flya.system.controller;\n\nimport java.util.List;\nimport java.util.Arrays;\n\nimport lombok.RequiredArgsConstructor;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.*;\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.validation.annotation.Validated;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.vo.PzcArtistVo;\nimport top.flya.system.domain.bo.PzcArtistBo;\nimport top.flya.system.service.IPzcArtistService;\nimport top.flya.common.core.page.TableDataInfo;\n\n/**\n * 艺人\n *\n * @author flya\n * @date 2023-06-01\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/artist\")\npublic class PzcArtistController extends BaseController {\n\n    private final IPzcArtistService iPzcArtistService;\n\n    /**\n     * 查询艺人列表\n     */\n    @SaCheckPermission(\"system:artist:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcArtistVo> list(PzcArtistBo bo, PageQuery pageQuery) {\n        return iPzcArtistService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 导出艺人列表\n     */\n    @SaCheckPermission(\"system:artist:export\")\n    @Log(title = \"艺人\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcArtistBo bo, HttpServletResponse response) {\n        List<PzcArtistVo> list = iPzcArtistService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"艺人\", PzcArtistVo.class, response);\n    }\n\n    /**\n     * 获取艺人详细信息\n     *\n     * @param artistId 主键\n     */\n    @SaCheckPermission(\"system:artist:query\")\n    @GetMapping(\"/{artistId}\")\n    public R<PzcArtistVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Long artistId) {\n        return R.ok(iPzcArtistService.queryById(artistId));\n    }\n\n    /**\n     * 新增艺人\n     */\n    @SaCheckPermission(\"system:artist:add\")\n    @Log(title = \"艺人\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcArtistBo bo) {\n        return toAjax(iPzcArtistService.insertByBo(bo));\n    }\n\n    /**\n     * 修改艺人\n     */\n    @SaCheckPermission(\"system:artist:edit\")\n    @Log(title = \"艺人\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcArtistBo bo) {\n        return toAjax(iPzcArtistService.updateByBo(bo));\n    }\n\n    /**\n     * 删除艺人\n     *\n     * @param artistIds 主键串\n     */\n    @SaCheckPermission(\"system:artist:remove\")\n    @Log(title = \"艺人\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{artistIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Long[] artistIds) {\n        return toAjax(iPzcArtistService.deleteWithValidByIds(Arrays.asList(artistIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcIntroController.java",
    "content": "package top.flya.system.controller;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.bo.PzcIntroBo;\nimport top.flya.system.domain.vo.PzcIntroVo;\nimport top.flya.system.service.IPzcIntroService;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 活动介绍\n *\n * @author ruoyi\n * @date 2023-08-04\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@Slf4j\n@RequestMapping(\"/system/intro\")\npublic class PzcIntroController extends BaseController {\n\n    private final IPzcIntroService iPzcIntroService;\n\n    /**\n     * 查询活动介绍列表\n     */\n    @SaCheckPermission(\"system:intro:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcIntroVo> list(PzcIntroBo bo, PageQuery pageQuery) {\n        return iPzcIntroService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 导出活动介绍列表\n     */\n    @SaCheckPermission(\"system:intro:export\")\n    @Log(title = \"活动介绍\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcIntroBo bo, HttpServletResponse response) {\n        List<PzcIntroVo> list = iPzcIntroService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"活动介绍\", PzcIntroVo.class, response);\n    }\n\n    /**\n     * 获取活动介绍详细信息\n     *\n     * @param introId 主键\n     */\n    @SaCheckPermission(\"system:intro:query\")\n    @GetMapping(\"/{introId}\")\n    public R<PzcIntroVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Long introId) {\n        return R.ok(iPzcIntroService.queryById(introId));\n    }\n\n    /**\n     * 新增活动介绍\n     */\n    @SaCheckPermission(\"system:intro:add\")\n    @Log(title = \"活动介绍\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcIntroBo bo) {\n        return toAjax(iPzcIntroService.insertByBo(bo));\n    }\n\n    /**\n     * 修改活动介绍\n     */\n    @SaCheckPermission(\"system:intro:edit\")\n    @Log(title = \"活动介绍\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcIntroBo bo) {\n        log.info(\"bo:{}\", JsonUtils.toJsonString(bo));\n        return toAjax(iPzcIntroService.updateByBo(bo));\n    }\n\n    /**\n     * 删除活动介绍\n     *\n     * @param introIds 主键串\n     */\n    @SaCheckPermission(\"system:intro:remove\")\n    @Log(title = \"活动介绍\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{introIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Long[] introIds) {\n        return toAjax(iPzcIntroService.deleteWithValidByIds(Arrays.asList(introIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcOfficialController.java",
    "content": "package top.flya.system.controller;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.bo.PzcOfficialBo;\nimport top.flya.system.domain.vo.PzcOfficialVo;\nimport top.flya.system.service.IPzcOfficialService;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 官方消息\n *\n * @author ruoyi\n * @date 2023-07-27\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/official\")\npublic class PzcOfficialController extends BaseController {\n\n    private final IPzcOfficialService iPzcOfficialService;\n\n    /**\n     * 查询官方 未读消息列表\n     */\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcOfficialVo> list(PzcOfficialBo bo, PageQuery pageQuery) {\n        bo.setToUserId(LoginHelper.getUserId());\n        pageQuery.setOrderByColumn(\"create_time\");\n        pageQuery.setIsAsc(\"desc\");\n        return iPzcOfficialService.queryPageList(bo, pageQuery);\n    }\n\n\n    /**\n     * 已读消息\n     * 如果officialId为空，则表示全部已读\n     * @param officialId\n     * @return\n     */\n    @PostMapping(\"/read\")\n    public R read(@RequestParam(value = \"officialId\",required = false) Integer officialId) {\n        return R.ok(iPzcOfficialService.read(officialId));\n    }\n\n\n\n    /**\n     * 导出官方消息列表\n     */\n    @SaCheckPermission(\"system:official:export\")\n    @Log(title = \"官方消息\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcOfficialBo bo, HttpServletResponse response) {\n        List<PzcOfficialVo> list = iPzcOfficialService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"官方消息\", PzcOfficialVo.class, response);\n    }\n\n    /**\n     * 获取官方消息详细信息\n     *\n     * @param officialId 主键\n     */\n    @SaCheckPermission(\"system:official:query\")\n    @GetMapping(\"/{officialId}\")\n    public R<PzcOfficialVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Long officialId) {\n        return R.ok(iPzcOfficialService.queryById(officialId));\n    }\n\n    /**\n     * 新增官方消息\n     */\n    @SaCheckPermission(\"system:official:add\")\n    @Log(title = \"官方消息\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcOfficialBo bo) {\n        return toAjax(iPzcOfficialService.insertByBo(bo));\n    }\n\n    /**\n     * 修改官方消息\n     */\n    @SaCheckPermission(\"system:official:edit\")\n    @Log(title = \"官方消息\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcOfficialBo bo) {\n        return toAjax(iPzcOfficialService.updateByBo(bo));\n    }\n\n    /**\n     * 删除官方消息\n     *\n     * @param officialIds 主键串\n     */\n    @SaCheckPermission(\"system:official:remove\")\n    @Log(title = \"官方消息\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{officialIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Long[] officialIds) {\n        return toAjax(iPzcOfficialService.deleteWithValidByIds(Arrays.asList(officialIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcOrderController.java",
    "content": "package top.flya.system.controller;\n\nimport java.util.List;\nimport java.util.Arrays;\n\nimport lombok.RequiredArgsConstructor;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.*;\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.validation.annotation.Validated;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.vo.PzcOrderVo;\nimport top.flya.system.domain.bo.PzcOrderBo;\nimport top.flya.system.service.IPzcOrderService;\nimport top.flya.common.core.page.TableDataInfo;\n\n/**\n * 订单\n *\n * @author ruoyi\n * @date 2023-07-09\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/pzc_order\")\npublic class PzcOrderController extends BaseController {\n\n    private final IPzcOrderService iPzcOrderService;\n\n    /**\n     * 查询订单列表\n     */\n    @SaCheckPermission(\"system:pzc_order:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcOrderVo> list(PzcOrderBo bo, PageQuery pageQuery) {\n        return iPzcOrderService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 导出订单列表\n     */\n    @SaCheckPermission(\"system:pzc_order:export\")\n    @Log(title = \"订单\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcOrderBo bo, HttpServletResponse response) {\n        List<PzcOrderVo> list = iPzcOrderService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"订单\", PzcOrderVo.class, response);\n    }\n\n    /**\n     * 获取订单详细信息\n     *\n     * @param orderId 主键\n     */\n    @SaCheckPermission(\"system:pzc_order:query\")\n    @GetMapping(\"/{orderId}\")\n    public R<PzcOrderVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Long orderId) {\n        return R.ok(iPzcOrderService.queryById(orderId));\n    }\n\n    /**\n     * 新增订单\n     */\n    @SaCheckPermission(\"system:pzc_order:add\")\n    @Log(title = \"订单\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcOrderBo bo) {\n        return toAjax(iPzcOrderService.insertByBo(bo));\n    }\n\n    /**\n     * 修改订单\n     */\n    @SaCheckPermission(\"system:pzc_order:edit\")\n    @Log(title = \"订单\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcOrderBo bo) {\n        return toAjax(iPzcOrderService.updateByBo(bo));\n    }\n\n    /**\n     * 删除订单\n     *\n     * @param orderIds 主键串\n     */\n    @SaCheckPermission(\"system:pzc_order:remove\")\n    @Log(title = \"订单\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{orderIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Long[] orderIds) {\n        return toAjax(iPzcOrderService.deleteWithValidByIds(Arrays.asList(orderIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcOrganizerController.java",
    "content": "package top.flya.system.controller;\n\nimport java.util.List;\nimport java.util.Arrays;\n\nimport lombok.RequiredArgsConstructor;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.*;\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.validation.annotation.Validated;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.vo.PzcOrganizerVo;\nimport top.flya.system.domain.bo.PzcOrganizerBo;\nimport top.flya.system.service.IPzcOrganizerService;\nimport top.flya.common.core.page.TableDataInfo;\n\n/**\n * 活动主办方\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/organizer\")\npublic class PzcOrganizerController extends BaseController {\n\n    private final IPzcOrganizerService iPzcOrganizerService;\n\n    /**\n     * 查询活动主办方列表\n     */\n    @SaCheckPermission(\"system:organizer:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcOrganizerVo> list(PzcOrganizerBo bo, PageQuery pageQuery) {\n        return iPzcOrganizerService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 导出活动主办方列表\n     */\n    @SaCheckPermission(\"system:organizer:export\")\n    @Log(title = \"活动主办方\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcOrganizerBo bo, HttpServletResponse response) {\n        List<PzcOrganizerVo> list = iPzcOrganizerService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"活动主办方\", PzcOrganizerVo.class, response);\n    }\n\n    /**\n     * 获取活动主办方详细信息\n     *\n     * @param organizerId 主键\n     */\n    @SaCheckPermission(\"system:organizer:query\")\n    @GetMapping(\"/{organizerId}\")\n    public R<PzcOrganizerVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Long organizerId) {\n        return R.ok(iPzcOrganizerService.queryById(organizerId));\n    }\n\n    /**\n     * 新增活动主办方\n     */\n    @SaCheckPermission(\"system:organizer:add\")\n    @Log(title = \"活动主办方\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcOrganizerBo bo) {\n        return toAjax(iPzcOrganizerService.insertByBo(bo));\n    }\n\n    /**\n     * 修改活动主办方\n     */\n    @SaCheckPermission(\"system:organizer:edit\")\n    @Log(title = \"活动主办方\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcOrganizerBo bo) {\n        return toAjax(iPzcOrganizerService.updateByBo(bo));\n    }\n\n    /**\n     * 删除活动主办方\n     *\n     * @param organizerIds 主键串\n     */\n    @SaCheckPermission(\"system:organizer:remove\")\n    @Log(title = \"活动主办方\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{organizerIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Long[] organizerIds) {\n        return toAjax(iPzcOrganizerService.deleteWithValidByIds(Arrays.asList(organizerIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcOrganizerTicketController.java",
    "content": "package top.flya.system.controller;\n\nimport java.util.List;\nimport java.util.Arrays;\n\nimport lombok.RequiredArgsConstructor;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.*;\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.validation.annotation.Validated;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.vo.PzcOrganizerTicketVo;\nimport top.flya.system.domain.bo.PzcOrganizerTicketBo;\nimport top.flya.system.service.IPzcOrganizerTicketService;\nimport top.flya.common.core.page.TableDataInfo;\n\n/**\n * 主办方票务\n *\n * @author ruoyi\n * @date 2023-07-22\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/organizerTicket\")\npublic class PzcOrganizerTicketController extends BaseController {\n\n    private final IPzcOrganizerTicketService iPzcOrganizerTicketService;\n\n    /**\n     * 查询主办方票务列表\n     */\n    @SaCheckPermission(\"system:organizerTicket:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcOrganizerTicketVo> list(PzcOrganizerTicketBo bo, PageQuery pageQuery) {\n        return iPzcOrganizerTicketService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 导出主办方票务列表\n     */\n    @SaCheckPermission(\"system:organizerTicket:export\")\n    @Log(title = \"主办方票务\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcOrganizerTicketBo bo, HttpServletResponse response) {\n        List<PzcOrganizerTicketVo> list = iPzcOrganizerTicketService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"主办方票务\", PzcOrganizerTicketVo.class, response);\n    }\n\n    /**\n     * 获取主办方票务详细信息\n     *\n     * @param organizerTicketId 主键\n     */\n    @SaCheckPermission(\"system:organizerTicket:query\")\n    @GetMapping(\"/{organizerTicketId}\")\n    public R<PzcOrganizerTicketVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Long organizerTicketId) {\n        return R.ok(iPzcOrganizerTicketService.queryById(organizerTicketId));\n    }\n\n    /**\n     * 新增主办方票务\n     */\n    @SaCheckPermission(\"system:organizerTicket:add\")\n    @Log(title = \"主办方票务\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcOrganizerTicketBo bo) {\n        return toAjax(iPzcOrganizerTicketService.insertByBo(bo));\n    }\n\n    /**\n     * 修改主办方票务\n     */\n    @SaCheckPermission(\"system:organizerTicket:edit\")\n    @Log(title = \"主办方票务\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcOrganizerTicketBo bo) {\n        return toAjax(iPzcOrganizerTicketService.updateByBo(bo));\n    }\n\n    /**\n     * 删除主办方票务\n     *\n     * @param organizerTicketIds 主键串\n     */\n    @SaCheckPermission(\"system:organizerTicket:remove\")\n    @Log(title = \"主办方票务\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{organizerTicketIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Long[] organizerTicketIds) {\n        return toAjax(iPzcOrganizerTicketService.deleteWithValidByIds(Arrays.asList(organizerTicketIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcRegionController.java",
    "content": "package top.flya.system.controller;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.bo.PzcRegionBo;\nimport top.flya.system.domain.vo.PzcRegionVo;\nimport top.flya.system.mapper.RegionTreeMapper;\nimport top.flya.system.service.IPzcRegionService;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 地区\n *\n * @author ruoyi\n * @date 2023-07-22\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/region\")\npublic class PzcRegionController extends BaseController {\n\n    private final IPzcRegionService iPzcRegionService;\n\n    private final RegionTreeMapper regionTreeMapper;\n\n//    /**\n//     * 查询地区列表\n//     */\n//    @SaCheckPermission(\"system:region:list\")\n//    @GetMapping(\"/list\")\n//    public TableDataInfo<PzcRegionVo> list(PzcRegionBo bo, PageQuery pageQuery) {\n//        return iPzcRegionService.queryPageList(bo, pageQuery);\n//    }\n\n\n    @GetMapping(\"/list\")\n    public R list(@RequestParam(required = false) String regionName) {\n        PzcRegionBo bo = new PzcRegionBo();\n        bo.setName(regionName);\n        List<PzcRegionVo> pzcRegionVos = iPzcRegionService.queryList(bo);\n        //根据 pzcRegionVos 生成树形结构 base 字段相同的为同一级\n        return R.ok(regionTreeMapper.buildTree(pzcRegionVos));\n    }\n\n    /**\n     * 导出地区列表\n     */\n    @SaCheckPermission(\"system:region:export\")\n    @Log(title = \"地区\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcRegionBo bo, HttpServletResponse response) {\n        List<PzcRegionVo> list = iPzcRegionService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"地区\", PzcRegionVo.class, response);\n    }\n\n    /**\n     * 获取地区详细信息\n     *\n     * @param regionId 主键\n     */\n    @SaCheckPermission(\"system:region:query\")\n    @GetMapping(\"/{regionId}\")\n    public R<PzcRegionVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Long regionId) {\n        return R.ok(iPzcRegionService.queryById(regionId));\n    }\n\n    /**\n     * 新增地区\n     */\n    @SaCheckPermission(\"system:region:add\")\n    @Log(title = \"地区\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcRegionBo bo) {\n        return toAjax(iPzcRegionService.insertByBo(bo));\n    }\n\n    /**\n     * 修改地区\n     */\n    @SaCheckPermission(\"system:region:edit\")\n    @Log(title = \"地区\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcRegionBo bo) {\n        return toAjax(iPzcRegionService.updateByBo(bo));\n    }\n\n    /**\n     * 删除地区\n     *\n     * @param regionIds 主键串\n     */\n    @SaCheckPermission(\"system:region:remove\")\n    @Log(title = \"地区\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{regionIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Long[] regionIds) {\n        return toAjax(iPzcRegionService.deleteWithValidByIds(Arrays.asList(regionIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcTagController.java",
    "content": "package top.flya.system.controller;\n\nimport java.util.List;\nimport java.util.Arrays;\n\nimport lombok.RequiredArgsConstructor;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.*;\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.validation.annotation.Validated;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.vo.PzcTagVo;\nimport top.flya.system.domain.bo.PzcTagBo;\nimport top.flya.system.service.IPzcTagService;\nimport top.flya.common.core.page.TableDataInfo;\n\n/**\n * 活动标签\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/tag\")\npublic class PzcTagController extends BaseController {\n\n    private final IPzcTagService iPzcTagService;\n\n    /**\n     * 查询活动标签列表\n     */\n    @SaCheckPermission(\"system:tag:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcTagVo> list(PzcTagBo bo, PageQuery pageQuery) {\n        return iPzcTagService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 导出活动标签列表\n     */\n    @SaCheckPermission(\"system:tag:export\")\n    @Log(title = \"活动标签\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcTagBo bo, HttpServletResponse response) {\n        List<PzcTagVo> list = iPzcTagService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"活动标签\", PzcTagVo.class, response);\n    }\n\n    /**\n     * 获取活动标签详细信息\n     *\n     * @param tagId 主键\n     */\n    @SaCheckPermission(\"system:tag:query\")\n    @GetMapping(\"/{tagId}\")\n    public R<PzcTagVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Long tagId) {\n        return R.ok(iPzcTagService.queryById(tagId));\n    }\n\n    /**\n     * 新增活动标签\n     */\n    @SaCheckPermission(\"system:tag:add\")\n    @Log(title = \"活动标签\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcTagBo bo) {\n        return toAjax(iPzcTagService.insertByBo(bo));\n    }\n\n    /**\n     * 修改活动标签\n     */\n    @SaCheckPermission(\"system:tag:edit\")\n    @Log(title = \"活动标签\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcTagBo bo) {\n        return toAjax(iPzcTagService.updateByBo(bo));\n    }\n\n    /**\n     * 删除活动标签\n     *\n     * @param tagIds 主键串\n     */\n    @SaCheckPermission(\"system:tag:remove\")\n    @Log(title = \"活动标签\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{tagIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Long[] tagIds) {\n        return toAjax(iPzcTagService.deleteWithValidByIds(Arrays.asList(tagIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcUserCollectController.java",
    "content": "package top.flya.system.controller;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.common.BatchUtils;\nimport top.flya.system.domain.PzcActivity;\nimport top.flya.system.domain.bo.PzcUserCollectBo;\nimport top.flya.system.domain.vo.PzcUserCollectVo;\nimport top.flya.system.mapper.PzcActivityMapper;\nimport top.flya.system.service.IPzcUserCollectService;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.NotNull;\nimport java.util.*;\n\n/**\n * 用户收藏活动\n *\n * @author ruoyi\n * @date 2023-07-08\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/userCollect\")\n@Slf4j\npublic class PzcUserCollectController extends BaseController {\n\n    private final IPzcUserCollectService iPzcUserCollectService;\n\n    private final PzcActivityMapper pzcActivityMapper;\n\n\n    private final StringRedisTemplate stringRedisTemplate;\n\n    private final BatchUtils batchUtils;\n\n    /**\n     * 查询用户收藏活动列表\n     */\n    @GetMapping(\"/list\")\n    public R<List<PzcActivity>> list(PzcUserCollectBo bo, PageQuery pageQuery) {\n        Set<String> members = stringRedisTemplate.opsForSet().members(\"collect:\" + getUserId());\n        if(members==null||members.isEmpty())\n        {\n            return R.ok();\n        }\n        List<String> collect = new ArrayList<>(members);\n        List<PzcActivity> pzcActivities = pzcActivityMapper.selectActivityByActivityIds(collect, bo.getType());\n        log.info(\"用户收藏活动列表 {}\", JsonUtils.toJsonString(pzcActivities));\n        pzcActivities.stream().forEach(\n                pzcActivity -> {\n                    pzcActivity.setCoverImage(pzcActivity.getCoverImage().contains(\"http\")?pzcActivity.getCoverImage():\n                        (batchUtils.getNewImageUrls(Collections.singletonList(pzcActivity.getCoverImage())).get(Long.parseLong((pzcActivity.getCoverImage())))));\n                }\n        );\n        return R.ok(pzcActivities);\n    }\n\n    /**\n     * 导出用户收藏活动列表\n     */\n    @SaCheckPermission(\"system:userCollect:export\")\n    @Log(title = \"用户收藏活动\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcUserCollectBo bo, HttpServletResponse response) {\n        List<PzcUserCollectVo> list = iPzcUserCollectService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"用户收藏活动\", PzcUserCollectVo.class, response);\n    }\n\n    /**\n     * 获取用户收藏活动详细信息\n     *\n     * @param collectId 主键\n     */\n    @GetMapping(\"/{collectId}\")\n    public R<PzcUserCollectVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Long collectId) {\n        return R.ok(iPzcUserCollectService.queryById(collectId));\n    }\n\n    @GetMapping(\"/status\")\n    public R<Boolean> status(@RequestParam(\"activityId\") Long activityId) {\n        return R.ok(Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember(\"collect:\" + getUserId(), activityId.toString())));\n    }\n    /**\n     * 新增用户收藏活动 这里改为存入Redis 加快响应速度\n     */\n    @Log(title = \"用户收藏/取消活动\", businessType = BusinessType.INSERT)\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcUserCollectBo bo) {\n        log.info(\"用户收藏/取消活动 {}\", JsonUtils.toJsonString(bo));\n        if(Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember(\"collect:\" + getUserId(), bo.getActivityId().toString())))\n        {\n            //取消收藏活动\n            stringRedisTemplate.opsForSet().remove(\"collect:\" + getUserId(),bo.getActivityId().toString());\n        }else {\n            stringRedisTemplate.opsForSet().add(\"collect:\" + getUserId(),bo.getActivityId().toString());\n        }\n        return R.ok(\"1\");\n    }\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcUserController.java",
    "content": "package top.flya.system.controller;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.bo.PzcUserBo;\nimport top.flya.system.domain.bo.UpdateMoneyBo;\nimport top.flya.system.domain.vo.PzcUserVo;\nimport top.flya.system.service.IPzcUserService;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 用户\n *\n * @author ruoyi\n * @date 2023-07-09\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/pzc_user\")\npublic class PzcUserController extends BaseController {\n\n    private final IPzcUserService iPzcUserService;\n\n\n\n\n    /**\n     * 查询用户列表\n     */\n    @SaCheckPermission(\"system:pzc_user:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcUserVo> list(PzcUserBo bo, PageQuery pageQuery) {\n        return iPzcUserService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 导出用户列表\n     */\n    @SaCheckPermission(\"system:pzc_user:export\")\n    @Log(title = \"用户\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcUserBo bo, HttpServletResponse response) {\n        List<PzcUserVo> list = iPzcUserService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"用户\", PzcUserVo.class, response);\n    }\n\n    /**\n     * 获取用户详细信息\n     *\n     * @param userId 主键\n     */\n    @SaCheckPermission(\"system:pzc_user:query\")\n    @GetMapping(\"/{userId}\")\n    public R<PzcUserVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Long userId) {\n        return R.ok(iPzcUserService.queryById(userId));\n    }\n\n    /**\n     * 新增用户\n     */\n    @SaCheckPermission(\"system:pzc_user:add\")\n    @Log(title = \"用户\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcUserBo bo) {\n        return toAjax(iPzcUserService.insertByBo(bo));\n    }\n\n\n    @PostMapping(\"/updateMoney\")\n    public R<Void> updateMoney(@Validated(AddGroup.class) @RequestBody UpdateMoneyBo bo) {\n        return toAjax(iPzcUserService.updateMoney(bo));\n    }\n\n    /**\n     * 修改用户\n     */\n    @SaCheckPermission(\"system:pzc_user:edit\")\n    @Log(title = \"用户\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcUserBo bo) {\n        return toAjax(iPzcUserService.updateByBo(bo));\n    }\n\n    /**\n     * 删除用户\n     *\n     * @param userIds 主键串\n     */\n    @SaCheckPermission(\"system:pzc_user:remove\")\n    @Log(title = \"用户\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{userIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Long[] userIds) {\n        return toAjax(iPzcUserService.deleteWithValidByIds(Arrays.asList(userIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcUserHistoryController.java",
    "content": "package top.flya.system.controller;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.bo.PzcUserHistoryBo;\nimport top.flya.system.domain.vo.PzcUserHistoryVo;\nimport top.flya.system.service.IPzcUserHistoryService;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 用户操作历史记录\n *\n * @author ruoyi\n * @date 2023-07-06\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/userHistory\")\npublic class PzcUserHistoryController extends BaseController {\n\n    private final IPzcUserHistoryService iPzcUserHistoryService;\n\n    /**\n     * 查询用户操作历史记录列表\n     */\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcUserHistoryVo> list(PzcUserHistoryBo bo, PageQuery pageQuery) {\n        bo.setUserId(LoginHelper.getUserId());\n        pageQuery.setOrderByColumn(\"create_time\");\n        pageQuery.setIsAsc(\"desc\");\n        return iPzcUserHistoryService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 导出用户操作历史记录列表\n     */\n    @SaCheckPermission(\"system:userHistory:export\")\n    @Log(title = \"用户操作历史记录\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcUserHistoryBo bo, HttpServletResponse response) {\n        List<PzcUserHistoryVo> list = iPzcUserHistoryService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"用户操作历史记录\", PzcUserHistoryVo.class, response);\n    }\n\n    /**\n     * 获取用户操作历史记录详细信息\n     *\n     * @param historyId 主键\n     */\n    @SaCheckPermission(\"system:userHistory:query\")\n    @GetMapping(\"/{historyId}\")\n    public R<PzcUserHistoryVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Long historyId) {\n        return R.ok(iPzcUserHistoryService.queryById(historyId));\n    }\n\n    /**\n     * 新增用户操作历史记录\n     */\n    @SaCheckPermission(\"system:userHistory:add\")\n    @Log(title = \"用户操作历史记录\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcUserHistoryBo bo) {\n        return toAjax(iPzcUserHistoryService.insertByBo(bo));\n    }\n\n    /**\n     * 修改用户操作历史记录\n     */\n    @SaCheckPermission(\"system:userHistory:edit\")\n    @Log(title = \"用户操作历史记录\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcUserHistoryBo bo) {\n        return toAjax(iPzcUserHistoryService.updateByBo(bo));\n    }\n\n    /**\n     * 删除用户操作历史记录\n     *\n     * @param historyIds 主键串\n     */\n    @SaCheckPermission(\"system:userHistory:remove\")\n    @Log(title = \"用户操作历史记录\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{historyIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Long[] historyIds) {\n        return toAjax(iPzcUserHistoryService.deleteWithValidByIds(Arrays.asList(historyIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcUserPhotoController.java",
    "content": "package top.flya.system.controller;\n\nimport java.util.List;\nimport java.util.Arrays;\n\nimport lombok.RequiredArgsConstructor;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.*;\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.validation.annotation.Validated;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.vo.PzcUserPhotoVo;\nimport top.flya.system.domain.bo.PzcUserPhotoBo;\nimport top.flya.system.service.IPzcUserPhotoService;\nimport top.flya.common.core.page.TableDataInfo;\n\n/**\n * 用户资料相册\n *\n * @author ruoyi\n * @date 2023-07-11\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/userPhoto\")\npublic class PzcUserPhotoController extends BaseController {\n\n    private final IPzcUserPhotoService iPzcUserPhotoService;\n\n    /**\n     * 查询用户资料相册列表\n     */\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcUserPhotoVo> list(PzcUserPhotoBo bo, PageQuery pageQuery) {\n        if(bo.getUserId()==null)\n        {\n            bo.setUserId(LoginHelper.getUserId());\n        }\n        return iPzcUserPhotoService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 导出用户资料相册列表\n     */\n    @Log(title = \"用户资料相册\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcUserPhotoBo bo, HttpServletResponse response) {\n        List<PzcUserPhotoVo> list = iPzcUserPhotoService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"用户资料相册\", PzcUserPhotoVo.class, response);\n    }\n\n    /**\n     * 获取用户资料相册详细信息\n     *\n     * @param photoId 主键\n     */\n    @GetMapping(\"/{photoId}\")\n    public R<PzcUserPhotoVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Long photoId) {\n        return R.ok(iPzcUserPhotoService.queryById(photoId));\n    }\n\n    /**\n     * 新增用户资料相册\n     */\n    @Log(title = \"用户资料相册\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcUserPhotoBo bo) {\n        bo.setUserId(LoginHelper.getUserId());\n        return toAjax(iPzcUserPhotoService.insertByBo(bo));\n    }\n\n    /**\n     * 修改用户资料相册\n     */\n    @Log(title = \"用户资料相册\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcUserPhotoBo bo) {\n        return toAjax(iPzcUserPhotoService.updateByBo(bo));\n    }\n\n    /**\n     * 删除用户资料相册\n     *\n     * @param photoIds 主键串\n     */\n    @Log(title = \"用户资料相册\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{photoIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Long[] photoIds) {\n        return toAjax(iPzcUserPhotoService.deleteWithValidByIds(Arrays.asList(photoIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcUserTalkController.java",
    "content": "package top.flya.system.controller;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.system.domain.PzcUser;\nimport top.flya.system.domain.PzcUserTalk;\nimport top.flya.system.domain.bo.PzcUserTalkBo;\nimport top.flya.system.domain.vo.PzcUserTalkVo;\nimport top.flya.system.mapper.PzcUserMapper;\nimport top.flya.system.mapper.PzcUserPhotoMapper;\nimport top.flya.system.mapper.PzcUserTalkMapper;\nimport top.flya.system.service.IPzcUserTalkService;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport static top.flya.system.config.ClientCache.concurrentHashMap;\n\n/**\n * 用户聊天\n *\n * @author ruoyi\n * @date 2023-07-14\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/userTalk\")\n@Slf4j\npublic class PzcUserTalkController extends BaseController {\n\n    private final IPzcUserTalkService iPzcUserTalkService;\n\n    private final PzcUserTalkMapper pzcUserTalkMapper;\n\n    private final PzcUserMapper pzcUserMapper;\n\n    private final PzcUserPhotoMapper pzcUserPhotoMapper;\n\n\n    @PostMapping(\"/deleteByUserId\") //删除聊天记录\n    public R deleteByUserId(@RequestParam(\"userId\") String userId, @RequestParam(\"groupId\") Long groupId) {\n        Long my = LoginHelper.getUserId();\n        pzcUserTalkMapper.delete(new QueryWrapper<PzcUserTalk>().\n            eq(\"group_id\", groupId).\n            and(\n                i -> i.eq(\"user_id\", my).\n                    eq(\"from_user_id\", userId).or().\n                    eq(\"user_id\", my).eq(\"to_user_id\", userId)));\n        return R.ok();\n    }\n\n    /**\n     * 用户在线状态\n     *\n     * @param userId\n     * @return\n     */\n    @GetMapping(\"/live\")\n    public R liveStatus(@RequestParam(\"userId\") String userId) {\n        Boolean liveStatus = false;\n        if (concurrentHashMap.get(userId) != null) {\n            liveStatus = true;\n        }\n\n        PzcUser pzcUser = pzcUserMapper.selectById(userId);\n        Map<String, Object> result = new java.util.HashMap<>();\n        result.put(\"liveStatus\", liveStatus.toString());\n        result.put(\"userId\", userId);\n        result.put(\"nickName\", pzcUser.getNickname());\n        result.put(\"address\", pzcUser.getAddress());\n        result.put(\"sex\", String.valueOf(pzcUser.getSex()));\n        result.put(\"info\", pzcUser.getIntro());\n        result.put(\"avatar\", pzcUser.getAvatar());\n        result.put(\"age\", pzcUser.getAge());\n        result.put(\"level\", pzcUser.getUserLevel());\n        // 查询用户相册\n        result.put(\"photo\", pzcUserPhotoMapper.selectList(new QueryWrapper<top.flya.system.domain.PzcUserPhoto>().eq(\"user_id\", userId)).stream().map(top.flya.system.domain.PzcUserPhoto::getUrl).collect(Collectors.toList()));\n        return R.ok(result);\n    }\n\n    /**\n     * 一键已读\n     *\n     * @param userId\n     * @return\n     */\n    @PostMapping(\"/read\")\n    public R<Boolean> read(@RequestParam(\"userId\") String userId) {\n        List<PzcUserTalk> pzcUserTalks = pzcUserTalkMapper.selectList(new QueryWrapper<PzcUserTalk>().eq(\"from_user_id\", userId).eq(\"to_user_id\", LoginHelper.getUserId())).stream()\n            .filter(pzcUserTalk -> pzcUserTalk.getMessageStatus() == 0)\n            .peek(pzcUserTalk -> pzcUserTalk.setMessageStatus(1L))\n            .collect(Collectors.toList());\n        log.info(\"pzcUserTalks:{}\", JsonUtils.toJsonString(pzcUserTalks));\n        if (pzcUserTalks.size() == 0) {\n            return R.ok(true);\n        }\n        boolean b = pzcUserTalkMapper.updateBatchById(pzcUserTalks);\n        return R.ok(b);\n    }\n\n\n    /**\n     * 我的聊天列表\n     * <p>\n     * 1. 按照 最后聊天的时间倒序排列\n     * 2. 展示 最新一条聊天记录 以及未读消息条数\n     */\n    @GetMapping(\"/myList\")\n    public TableDataInfo<PzcUserTalkVo> myList(PzcUserTalkBo bo, PageQuery pageQuery) {\n        return iPzcUserTalkService.queryMyPageList(bo, pageQuery);\n    }\n\n\n    /**\n     * 查询用户聊天列表\n     */\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcUserTalkVo> list(PzcUserTalkBo bo, PageQuery pageQuery) {\n        bo.setFromUserId(LoginHelper.getUserId());\n        bo.setUserId(LoginHelper.getUserId());\n        pageQuery.setIsAsc(\"desc\");\n        pageQuery.setOrderByColumn(\"create_time\");\n        return iPzcUserTalkService.queryPageList(bo, pageQuery);\n    }\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/PzcViewPagerController.java",
    "content": "package top.flya.system.controller;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.bo.PzcViewPagerBo;\nimport top.flya.system.domain.vo.PzcViewPagerVo;\nimport top.flya.system.service.IPzcViewPagerService;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 轮播图\n *\n * @author ruoyi\n * @date 2023-05-23\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/viewPager\")\npublic class PzcViewPagerController extends BaseController {\n\n    private final IPzcViewPagerService iPzcViewPagerService;\n\n    /**\n     * 查询轮播图列表\n     */\n    @GetMapping(\"/list\")\n    public TableDataInfo<PzcViewPagerVo> list(PzcViewPagerBo bo, PageQuery pageQuery) {\n        return iPzcViewPagerService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 导出轮播图列表\n     */\n    @SaCheckPermission(\"system:viewPager:export\")\n    @Log(title = \"轮播图\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(PzcViewPagerBo bo, HttpServletResponse response) {\n        List<PzcViewPagerVo> list = iPzcViewPagerService.queryList(bo);\n        ExcelUtil.exportExcel(list, \"轮播图\", PzcViewPagerVo.class, response);\n    }\n\n    /**\n     * 获取轮播图详细信息\n     *\n     * @param viewPagerId 主键\n     */\n    @GetMapping(\"/{viewPagerId}\")\n    public R<PzcViewPagerVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Integer viewPagerId) {\n        return R.ok(iPzcViewPagerService.queryById(viewPagerId));\n    }\n\n    /**\n     * 新增轮播图\n     */\n    @SaCheckPermission(\"system:viewPager:add\")\n    @Log(title = \"轮播图\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody PzcViewPagerBo bo) {\n        return toAjax(iPzcViewPagerService.insertByBo(bo));\n    }\n\n    /**\n     * 修改轮播图\n     */\n    @SaCheckPermission(\"system:viewPager:edit\")\n    @Log(title = \"轮播图\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PzcViewPagerBo bo) {\n        return toAjax(iPzcViewPagerService.updateByBo(bo));\n    }\n\n    /**\n     * 删除轮播图\n     *\n     * @param viewPagerIds 主键串\n     */\n    @SaCheckPermission(\"system:viewPager:remove\")\n    @Log(title = \"轮播图\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{viewPagerIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Integer[] viewPagerIds) {\n        return toAjax(iPzcViewPagerService.deleteWithValidByIds(Arrays.asList(viewPagerIds), true));\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/controller/WxUserController.java",
    "content": "package top.flya.system.controller;\n\n\nimport cn.dev33.satoken.stp.StpUtil;\nimport cn.hutool.core.bean.BeanUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.http.HttpUtil;\nimport cn.hutool.json.JSONUtil;\nimport com.alibaba.excel.util.DateUtils;\nimport com.alibaba.fastjson.JSONObject;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.wechat.pay.contrib.apache.httpclient.util.AesUtil;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.entity.StringEntity;\nimport org.apache.http.impl.client.CloseableHttpClient;\nimport org.apache.http.util.EntityUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport top.flya.common.constant.Constants;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.domain.event.LogininforEvent;\nimport top.flya.common.core.domain.model.XcxLoginUser;\nimport top.flya.common.enums.DeviceType;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.MessageUtils;\nimport top.flya.common.utils.ServletUtils;\nimport top.flya.common.utils.spring.SpringUtils;\nimport top.flya.system.domain.*;\nimport top.flya.system.domain.bo.PayOrderBo;\nimport top.flya.system.domain.bo.PzcUserBo;\nimport top.flya.system.domain.bo.SuccessCallBackObjBo;\nimport top.flya.system.domain.vo.PzcUserHistoryVo;\nimport top.flya.system.handel.WxPayInitHandel;\nimport top.flya.system.mapper.*;\nimport top.flya.system.utils.CreateSign;\nimport top.flya.system.utils.WxUtils;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.lang.reflect.Field;\nimport java.math.BigDecimal;\nimport java.security.GeneralSecurityException;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/wx/user\")\n@Slf4j\npublic class WxUserController extends BaseController {\n\n    @Value(\"${wx.appId}\")\n    private String appId;\n\n    @Value(\"${wx.appSecret}\")\n    private String secret;\n\n    @Value(\"${sa-token.token-prefix}\")\n    private String tokenPrefix;\n\n    @Value(\"${wx.merchantId}\")\n    private String mchId;\n\n    @Value(\"${wx.api3}\")\n    private String api3;\n\n    @Value(\"${wx.notifyUrl}\")\n    private String notifyUrl;\n\n    @Value(\"${api.school}\")\n    private String schoolUrl;\n\n    @Value(\"${api.apiKey}\")\n    private String apiKey;\n\n    private final PzcUserMapper userMapper;\n\n    private final WxUtils wxUtils;\n\n    private final PzcUserHistoryMapper userHistoryMapper;\n\n    @Autowired\n    private WxPayInitHandel wxPayInitHandel;\n\n    @Autowired\n    private CreateSign createSign;\n\n    @Autowired\n    private PzcOrderMapper orderMapper;\n\n    @Autowired\n    private PzcOfficialMapper officialMapper;\n\n    @Autowired\n    private PzcUserTalkMapper talkMapper;\n\n    @Autowired\n    private SysUserMapper sysUserMapper;\n\n\n    @Value(\"${neteaseCloudMusicApi}\")\n    private String neteaseCloudMusicApi;\n\n\n    @GetMapping(\"/filterKeyWords\")\n    public R filterKeyWords(@RequestParam(\"msg\") String msg) {\n        wxUtils.checkMgc(msg);\n        return R.ok();\n    }\n\n    @GetMapping(\"/music\")\n    public R music() {\n        String url =  neteaseCloudMusicApi + sysUserMapper.selectUserById(1L).getNickName();\n        log.info(\"url is {}\", url);\n        String result = HttpUtil.get(url);\n        JSONObject jsonObject = JSONObject.parseObject(result);\n        return R.ok(jsonObject.getJSONArray(\"data\").getJSONObject(0).getString(\"url\"));\n    }\n\n    @GetMapping(\"/notRead\") // 获取首页 未读消息 （红点点）\n    public R notRead() {\n        PzcUser user = wxUtils.checkUser();\n        List<PzcOfficial> pzcOfficials = officialMapper.selectList(new QueryWrapper<PzcOfficial>().eq(\"to_user_id\", user.getUserId()).eq(\"is_read\", 0));\n        Integer size1 = pzcOfficials.size();\n        Integer size2 = talkMapper.selectList(new QueryWrapper<PzcUserTalk>().eq(\"to_user_id\", user.getUserId()).eq(\"message_status\", 0)).size();\n        return R.ok(Math.min(size1 + size2, 99));\n    }\n\n\n    @PostMapping(\"/login\") // 登录\n    public R login(@RequestBody @Validated PzcUserBo loginUser) {\n        String tokenValue = userLogin(loginUser);\n        return (tokenValue != null) ? R.ok(tokenPrefix + \" \" + tokenValue) : R.fail(\"登录失败\");\n    }\n\n\n    @GetMapping(\"/getSchoolList\")\n    public R getSchoolList(@RequestParam(\"schoolName\") String schoolName) {\n        String baseUrl = schoolUrl + schoolName + \"&\" + apiKey;\n        log.info(\"baseUrl is {}\", baseUrl);\n        String result = HttpUtil.get(baseUrl);\n        return R.ok(JSONObject.parseObject(result).get(\"data\"));\n    }\n\n    @GetMapping(\"/userInfo\") // 获取用户信息\n    public R userInfo() {\n        return R.ok(wxUtils.checkUser());\n    }\n\n    @PostMapping(\"/updateUserInfo\") // 更新用户信息\n    public R updateUserInfo(@RequestBody PzcUserBo pzcUserBo) {\n        log.info(\"更新用户信息： pzcUserBo is {}\", pzcUserBo);\n        PzcUser user = wxUtils.checkUser();\n        //获取现在时间和一年前的时间 并格式化\n        String nowTime = DateUtils.format(new Date());\n        String lastYearNow = LocalDateTime.of(LocalDate.now().minusYears(1), LocalDateTime.now().toLocalTime()).toString();\n        log.info(\"nowTime is {} , lastYearNow is {}\", nowTime, lastYearNow);\n\n        if (pzcUserBo.getNickname() != null && !user.getNickname().equals(pzcUserBo.getNickname())) {\n            //判断用户是否之前一年内是否更新过昵称\n            List<PzcUserHistoryVo> pzcUserHistoryVos = userHistoryMapper.\n                selectVoList(new QueryWrapper<PzcUserHistory>().eq(\"user_id\", user.getUserId()).eq(\"type\", 0).like(\"message\", \"%昵称%\")\n                    .between(\"create_time\", lastYearNow, nowTime));\n            if (!pzcUserHistoryVos.isEmpty()) {\n                return R.fail(\"一年内只能修改一次昵称\");\n            } else {\n                wxUtils.checkMgc(pzcUserBo.getNickname());\n\n                //更新用户信息\n                user.setNickname(pzcUserBo.getNickname());\n                userMapper.updateById(user);\n                //存入用户历史记录\n                wxUtils.insertUserHistory(user.getUserId(), 0L, 0L, \"昵称修改为\" + pzcUserBo.getNickname(), null);\n                return R.ok(userMapper.selectById(user.getUserId()));\n            }\n        } else {\n            pzcUserBo.setMoney(user.getMoney());//余额不允许修改\n            pzcUserBo.setUserId(user.getUserId());//用户id不允许修改\n            pzcUserBo.setRealname(user.getRealname());//真实姓名不允许修改\n            pzcUserBo.setPhone(user.getPhone());//手机号不允许修改\n            pzcUserBo.setOpenid(user.getOpenid());//openid不允许修改\n            pzcUserBo.setExemptCancel(user.getExemptCancel());//免责不允许修改\n\n            Map<String, Object> map = BeanUtil.beanToMap(pzcUserBo);\n\n            //存入用户历史记录\n            wxUtils.insertUserHistory(user.getUserId(), 0L, 0L, \"更新用户其他信息\", null);\n            return R.ok(updateUser(map, user));\n        }\n\n    }\n\n\n    @PostMapping(\"/recharge\") // 充值\n    @Transactional\n    public R createOrder(@RequestBody @Validated PayOrderBo payOrder) throws Exception {\n        PzcUser user = wxUtils.checkUser();\n\n        String openId = user.getOpenid();\n\n        CloseableHttpClient httpClient = wxPayInitHandel.setup();\n\n        //请求URL\n        HttpPost httpPost = new HttpPost(\"https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi\");\n\n        HashMap<String, Object> amount = new HashMap<>();\n        amount.put(\"total\", payOrder.getCount());\n        amount.put(\"currency\", \"CNY\");\n\n        HashMap<String, Object> payer = new HashMap<>();\n        payer.put(\"openid\", openId);\n\n        String orderNum = IdUtil.getSnowflakeNextIdStr();\n\n        HashMap<String, Object> toData = new HashMap<>();\n        toData.put(\"amount\", amount);\n        toData.put(\"mchid\", mchId);\n        toData.put(\"description\", \"派币充值订单\");\n        toData.put(\"notify_url\", notifyUrl);\n        toData.put(\"payer\", payer);\n        toData.put(\"attach\", orderNum);\n\n        toData.put(\"out_trade_no\", orderNum);\n        toData.put(\"goods_tag\", \"WXG\");\n        toData.put(\"appid\", appId);\n\n        String s = JSONObject.toJSONString(toData);\n\n        log.info(\"订单： \" + s);\n\n\n        StringEntity entity = new StringEntity(s, \"utf-8\");\n        entity.setContentType(\"application/json\");\n        httpPost.setEntity(entity);\n        httpPost.setHeader(\"Accept\", \"application/json\");\n        //完成签名并执行请求\n        CloseableHttpResponse response = httpClient.execute(httpPost);\n\n        int statusCode = response.getStatusLine().getStatusCode();\n        if (statusCode == 200) {\n\n            String result = \"prepay_id=\" + JSONObject.parseObject(EntityUtils.toString(response.getEntity())).get(\"prepay_id\").toString();\n            response.close();\n            //关闭连接\n            wxPayInitHandel.after(httpClient);\n            String nonceStr = \"5K8264ILTKCH16CQ2502SI8ZNMTM67VS\";\n            long timestamp = System.currentTimeMillis() / 1000;\n\n            String paySign = createSign.getSign(appId, timestamp, nonceStr, result);\n\n            HashMap<String, Object> map = new HashMap<>();\n\n            map.put(\"timestamp\", String.valueOf(timestamp));\n            map.put(\"nonceStr\", nonceStr);\n            map.put(\"package\", result);\n            map.put(\"signType\", \"RSA\");\n            map.put(\"paySign\", paySign);\n            map.put(\"orderNum\", orderNum);\n\n            //创建未支付的充值订单\n            PzcOrder pzcRechargeOrder = new PzcOrder();\n            pzcRechargeOrder.setActivityId(null);\n            pzcRechargeOrder.setMoney(BigDecimal.valueOf(payOrder.getCount()).divide(BigDecimal.valueOf(100)));\n            pzcRechargeOrder.setOrderStatus(0L);\n            pzcRechargeOrder.setType(0L);\n            pzcRechargeOrder.setOutOrderNum(orderNum);\n            pzcRechargeOrder.setIntro(\"派币充值订单\");\n            pzcRechargeOrder.setTitle(\"派币充值\");\n            pzcRechargeOrder.setUserId(user.getUserId());\n\n            orderMapper.insert(pzcRechargeOrder);\n\n\n            return R.ok(map);\n\n        } else if (statusCode == 204) {\n            log.info(\"success\");\n            response.close();\n            //关闭连接\n            wxPayInitHandel.after(httpClient);\n            return R.ok(\"成功但未返回预支付订单号\");\n        } else {\n            log.info(\"failed,resp code = \" + statusCode + \",return body = \" + EntityUtils.toString(response.getEntity()));\n            String result = EntityUtils.toString(response.getEntity());\n            response.close();\n            //关闭连接\n            wxPayInitHandel.after(httpClient);\n            return R.fail(\"创建预支付订单失败: \" + result);\n        }\n    }\n\n    /**/\n    @RequestMapping(\"/callback\")\n    @Transactional\n    public R callback(HttpServletRequest request, @RequestBody SuccessCallBackObjBo obj) {\n        log.info(\"进入支付回调啦~\");\n        String associated_data = obj.getResource().getAssociated_data();\n        String nonce = obj.getResource().getNonce();\n        String ciphertext = obj.getResource().getCiphertext();\n        AesUtil aesUtil = new AesUtil(api3.getBytes());\n        try {\n            String s = aesUtil.decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);\n\n            JSONObject jsonObject = JSONObject.parseObject(s);\n            jsonObject.forEach((k, v) -> log.info(\"k:\" + k + \"  v:\" + v + \"\\n\"));\n            String orderNum = jsonObject.getString(\"out_trade_no\");\n            //更新订单状态和用户余额\n            PzcOrder pzcOrder = orderMapper.selectOne(new QueryWrapper<PzcOrder>().eq(\"out_order_num\", orderNum));\n            if (pzcOrder == null) {\n                return R.fail(\"订单不存在\");\n            }\n            if (pzcOrder.getOrderStatus() == 1) {\n                return R.fail(\"订单已支付\");\n            }\n            pzcOrder.setOrderStatus(1L);\n            orderMapper.updateById(pzcOrder);\n            Long userId = pzcOrder.getUserId();\n            PzcUser user = userMapper.selectById(userId);\n            user.setMoney(user.getMoney().add(pzcOrder.getMoney()));\n            userMapper.updateById(user);\n            wxUtils.insertUserHistory(user.getUserId(), 0L, 2L, \"派币充值【\" + pzcOrder.getMoney() + \"】\", pzcOrder.getMoney());\n\n\n        } catch (GeneralSecurityException e) {\n            e.printStackTrace();\n        }\n\n        return R.ok();\n    }\n\n    // 假设接收到的请求参数为Map<String, Object> userInfo\n    public PzcUser updateUser(Map<String, Object> userInfo, PzcUser user) {\n\n        // 反射动态更新用户信息\n        try {\n            Class<?> clazz = user.getClass();\n            for (Map.Entry<String, Object> entry : userInfo.entrySet()) {\n                String fieldName = entry.getKey();\n                Object fieldValue = entry.getValue();\n                log.info(\"fieldName is {} , fieldValue is {}\", fieldName, fieldValue);\n                if (fieldValue != null) {\n                    wxUtils.checkMgc(String\n                        .valueOf(fieldValue)); //敏感词检测\n                }\n\n\n                if (fieldValue instanceof Map) //跳过map类型\n                {\n                    continue;\n                }\n\n                if (fieldValue != null) {\n                    Field field = clazz.getDeclaredField(fieldName);\n                    field.setAccessible(true);\n                    field.set(user, fieldValue);\n                }\n\n            }\n        } catch (NoSuchFieldException | IllegalAccessException e) {\n            throw new RuntimeException(\"更新用户信息失败 反射异常\");\n        }\n\n        // 保存更新后的用户信息\n        userMapper.updateById(user);\n        return userMapper.selectById(user.getUserId());\n    }\n\n    public String userLogin(PzcUserBo pzcUserBo) {\n        String url = \"https://api.weixin.qq.com/sns/jscode2session?appid=\" + appId +\n            \"&secret=\" + secret + \"&js_code=\" + pzcUserBo.getLoginCode() + \"&grant_type=authorization_code\";\n\n        String response = HttpUtil.get(url);\n        log.info(\"微信小程序登录 url : {}，response is {}\", url, response);\n        JSONObject wxUser = JSONObject.parseObject(response);\n        if (com.baomidou.mybatisplus.core.toolkit.StringUtils.checkValNull(wxUser) || wxUser.get(\"errcode\") != null) {\n            throw new RuntimeException(\"微信登录失败 可能是code过期了\");\n        }\n        String openId = wxUser.get(\"openid\").toString();\n        //如果存在 就直接返回 不存在就新建用户\n        PzcUser user = userMapper.selectOne(new QueryWrapper<PzcUser>().eq(\"openid\", openId));\n        if (user == null) {\n            //存入用户信息\n            PzcUser newUser = new PzcUser();\n            newUser.setNickname(pzcUserBo.getNickname());\n            newUser.setOpenid(openId);\n            newUser.setAvatar(pzcUserBo.getAvatar());\n\n            //新注册时 根据 POST https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=ACCESS_TOKEN 获取手机号\n            String getPhoneUrl = \"https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=\" + wxUtils.getAccessToken();\n            Map<String, String> codeMap = new HashMap<>();\n            codeMap.put(\"code\", pzcUserBo.getPhoneCode());\n            String phoneResponse = HttpUtil.post(getPhoneUrl, JSONUtil.toJsonStr(codeMap));\n            log.info(\"微信小程序获取用户手机号信息 url : {}，response is {}\", getPhoneUrl, phoneResponse);\n            cn.hutool.json.JSONObject phoneJson = JSONUtil.parseObj(phoneResponse);\n            if (phoneJson.getInt(\"errcode\") != 0) {\n                log.info(\"微信小程序获取用户手机号信息失败\");\n                throw new RuntimeException(\"微信小程序获取用户手机号信息失败\");\n            }\n            newUser.setPhone(phoneJson.getJSONObject(\"phone_info\").getStr(\"purePhoneNumber\"));\n            newUser.setSex(pzcUserBo.getSex());\n            newUser.setMoney(new BigDecimal(100)); //新用户注册送1元\n            newUser.setUserLevel(1L);\n\n            int insert = userMapper.insert(newUser);\n            log.info(\"insertUser: \" + insert);\n            user = userMapper.selectOne(new QueryWrapper<PzcUser>().eq(\"openid\", openId));\n        }\n\n        if (user.getState() == 0) {\n            throw new RuntimeException(\"用户已被禁用\");\n        }\n\n\n        // 此处可根据登录用户的数据不同 自行创建 loginUser\n        XcxLoginUser loginUser = new XcxLoginUser();\n        loginUser.setUserId(Long.valueOf(user.getUserId()));\n        loginUser.setUsername(user.getNickname());\n        loginUser.setUserType(\"微信小程序用户\");\n        loginUser.setOpenid(openId);\n        // 生成token\n        LoginHelper.loginByDevice(loginUser, DeviceType.XCX);\n        recordLogininfor(user.getNickname(), MessageUtils.message(\"user.login.success\"));\n        return StpUtil.getTokenValue();\n    }\n\n    private void recordLogininfor(String username, String message) {\n        LogininforEvent logininforEvent = new LogininforEvent();\n        logininforEvent.setUsername(username);\n        logininforEvent.setStatus(Constants.LOGIN_SUCCESS);\n        logininforEvent.setMessage(message);\n        logininforEvent.setRequest(ServletUtils.getRequest());\n        SpringUtils.context().publishEvent(logininforEvent);\n    }\n\n\n    @PostMapping(\"/sendArriveMsg\") //推送微信小程序通知\n    public R sendArriveMsg(String toUserOpenId, String data) {\n        String getTokenUrl = \"https://api.weixin.qq.com/cgi-bin/token?\" + \"grant_type=client_credential&appid=\" + appId + \"&secret=\" + secret;\n        String response = HttpUtil.get(getTokenUrl);\n        log.info(\"微信小程序获取token url : {}，response is {}\", getTokenUrl, response);\n        JSONObject wxUser = JSONObject.parseObject(response);\n        if (com.baomidou.mybatisplus.core.toolkit.StringUtils.checkValNull(wxUser) || wxUser.get(\"errcode\") != null) {\n            throw new RuntimeException(\"微信登录失败 可能是code过期了\");\n        }\n        String accessToken = wxUser.get(\"access_token\").toString();\n        String msgUrl = \"https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=\" + accessToken;\n        Map<String, Object> map = new HashMap<>();\n        map.put(\"template_id\", \"MMHCiz9Z5faTwbDI9ywE0ScIvGMeDduTxXm00wdLxmw\");\n        map.put(\"touser\", toUserOpenId);\n        map.put(\"data\", data);\n        map.put(\"miniprogram_state\", \"trial\");//developer为开发版；trial为体验版；formal为正式版；默认为正式版\n        map.put(\"lang\", \"zh_CN\");\n        String msgResponse = HttpUtil.post(msgUrl, JSONUtil.toJsonStr(map));\n        log.info(\"微信小程序推送消息 url : {}，response is {}\", msgUrl, msgResponse);\n        JSONObject msgJson = JSONObject.parseObject(msgResponse);\n        if (msgJson.getInteger(\"errcode\") != 0) {\n            throw new RuntimeException(\"微信小程序推送消息失败\");\n        }\n\n        return R.ok(msgJson.get(\"errcode\").toString());\n    }\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcActivity.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\n\n/**\n * 活动对象 pzc_activity\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_activity\")\npublic class PzcActivity extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * 活动id\n     */\n    @TableId(value = \"activity_id\", type = IdType.AUTO)\n    private Integer activityId;\n    /**\n     * 地址\n     */\n    private String address;\n    /**\n     * 城市ID\n     */\n    private Integer regionId;\n    /**\n     * 活动标题\n     */\n    private String title;\n    /**\n     * 开始时间\n     */\n    private String startTime;\n    /**\n     * 结束时间\n     */\n    private String endDate;\n    /**\n     * 活动详情主图\n     */\n    private String innerImage;\n    /**\n     * 展示时间\n     */\n    private String showTime;\n    /**\n     * 封面图片\n     */\n    private String coverImage;\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    private Integer state;\n\n    private Integer classify; //属于哪个分类 0 电音节 1派对\n\n    private Integer region; // 0 国内 1 国外\n\n\n    private Long organizerId; //主办方id\n\n    private String shareImage; //分享图片\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcActivityConnArtist.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\n\n/**\n * 活动关联艺人对象 pzc_activity_conn_artist\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_activity_conn_artist\")\npublic class PzcActivityConnArtist extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * ID\n     */\n    @TableId(value = \"activity_conn_artist_id\", type = IdType.AUTO)\n    private Integer activityConnArtistId;\n    /**\n     * 活动ID\n     */\n    private Integer activityId;\n    /**\n     * 艺人ID\n     */\n    private Integer artistId;\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    private Integer state;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcActivityConnIntro.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\n\n/**\n * 活动介绍与活动关联对象 pzc_activity_conn_intro\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_activity_conn_intro\")\npublic class PzcActivityConnIntro extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * ID\n     */\n    @TableId(value = \"activity_conn_intro_id\", type = IdType.AUTO)\n    private Integer activityConnIntroId;\n    /**\n     * 活动ID\n     */\n    private Integer activityId;\n    /**\n     * 活动介绍ID\n     */\n    private Integer introId;\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    private Integer state;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcActivityConnTag.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\n\n/**\n * 活动标签与活动关联对象 pzc_activity_conn_tag\n *\n * @author ruoyi\n * @date 2023-06-03\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_activity_conn_tag\")\npublic class PzcActivityConnTag extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * ID\n     */\n    @TableId(value = \"activity_conn_tag_id\")\n    private Integer activityConnTagId;\n    /**\n     * 活动ID\n     */\n    private Integer activityId;\n    /**\n     * 活动标签ID\n     */\n    private Integer tagId;\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    private Integer state;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcActivityGroup.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\nimport java.math.BigDecimal;\nimport java.util.Date;\n\n/**\n * 活动组队对象 pzc_activity_group\n *\n * @author ruoyi\n * @date 2023-07-10\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_activity_group\")\npublic class PzcActivityGroup extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * 组队ID\n\n     */\n    @TableId(value = \"group_id\",type = IdType.AUTO)\n    private Long groupId;\n    /**\n     * 活动ID\n     */\n    private Long activityId;\n\n    private Integer region;\n    /**\n     * 活动组队发起人ID\n     */\n    private Long userId;\n    /**\n     * 活动主题\n     */\n    private String title;\n    /**\n     * 活动组队所缴纳的保证金\n     */\n    private BigDecimal money;\n    /**\n     * 买单方式\n     */\n    private Long groupType;\n    /**\n     * 活动地址\n     */\n    private String address;\n    /**\n     * 一起约定好的时间\n     */\n    private Date activityTime;\n    /**\n     * 权限\n     */\n    private Long auth;\n\n    @TableField(exist = false)\n    private PzcUser user;\n\n\n    private Integer status;\n\n\n    private String activityName;\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcActivityGroupApply.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\nimport java.math.BigDecimal;\n\n/**\n * 活动组队申请列对象 pzc_activity_group_apply\n *\n * @author ruoyi\n * @date 2023-07-10\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_activity_group_apply\")\npublic class PzcActivityGroupApply extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * 申请ID\n     */\n    @TableId(value = \"apply_id\",type = IdType.AUTO)\n    private Long applyId;\n    /**\n     * 申请人ID\n     */\n    private Long userId;\n    /**\n     * 申请的活动ID\n     */\n    private Long activityId;\n    /**\n     * 申请加入的组ID\n     */\n    private Long groupId;\n    /**\n     * 0 AA制\n1 我买单\n2 你买单\n     */\n    private Long groupType;\n    /**\n     * 活动保证金\n     */\n    private BigDecimal money;\n    /**\n     * 留言内容\n     */\n    private String message;\n\n    /**\n     * 0 位于申请列表中 1 申请通过待确认时 2 确认通过进行中 3 组队结束\n     */\n    private Integer applyStatus;\n\n    /**\n     * 无限制确认到达  0 未确认 1 已确认\n     */\n    private Integer wxz;\n\n\n    @TableField(exist = false)\n    private BigDecimal otherMoney; //对方的保证金\n\n    @TableField(exist = false)\n    private String otherName; //对方的名字\n\n    @TableField(exist = false)\n    private String otherAvatar; //对方的头像\n\n    @TableField(exist = false)\n    private String otherUserId; //对方的userId\n\n    @TableField(exist = false)\n    private Integer otherLevel; //对方的等级\n\n\n    @TableField(exist = false)\n    private String myAvatar; //我的头像\n\n    @TableField(exist = false)\n    private String title;\n\n\n    //发起方当前位置\n    private String startAddress;\n//申请方当前位置\n    private String applyAddress;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcArtist.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\n\n/**\n * 艺人对象 pzc_artist\n *\n * @author flya\n * @date 2023-06-01\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_artist\")\npublic class PzcArtist extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * ID\n     */\n    @TableId(value = \"artist_id\", type = IdType.AUTO)\n    private Long artistId;\n    /**\n     * 艺人名\n     */\n    private String name;\n    /**\n     * 艺人照片\n     */\n    private String imageUrl;\n    /**\n     * 艺人介绍\n     */\n    private String description;\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    private Integer state;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcIntro.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\n\n/**\n * 活动介绍对象 pzc_intro\n *\n * @author ruoyi\n * @date 2023-08-04\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_intro\")\npublic class PzcIntro extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * ID\n     */\n    @TableId(value = \"intro_id\",type = IdType.AUTO)\n    private Long introId;\n    /**\n     * 标题\n     */\n    private String title;\n    /**\n     * 内容\n     */\n    private String content;\n    /**\n     * 0 场地舞台介绍 1 更多介绍\n     */\n    private Long type;\n    /**\n     * 图片\n     */\n    private String imageFullUrl;\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    private Integer state;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcOfficial.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\n\n/**\n * 官方消息对象 pzc_official\n *\n * @author ruoyi\n * @date 2023-07-27\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_official\")\npublic class PzcOfficial extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * ID\n     */\n    @TableId(value = \"official_id\",type = IdType.AUTO)\n    private Long officialId;\n    /**\n     * 来自谁的消息\n     */\n    private Long fromUserId;\n    /**\n     * 给谁发的消息\n     */\n    private Long toUserId;\n    /**\n     * 标题\n     */\n    private String title;\n    /**\n     * 主体消息\n     */\n    private String content;\n    /**\n     * 是否已读\n     */\n    private Long isRead;\n    /**\n     * 来自那场组队的消息\n     */\n    private Long groupId;\n    /**\n     * 来自那场活动的消息\n     */\n    private Long activityId;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcOrder.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\nimport java.math.BigDecimal;\n\n/**\n * 订单对象 pzc_order\n *\n * @author ruoyi\n * @date 2023-07-09\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_order\")\npublic class PzcOrder extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * 订单ID\n     */\n    @TableId(value = \"order_id\",type = IdType.AUTO)\n    private Long orderId;\n    /**\n     * 用户ID\n     */\n    private Long userId;\n    /**\n     * 活动ID\n     */\n    private Long activityId;\n    /**\n     * 订单金额\n     */\n    private BigDecimal money;\n    /**\n     * 订单状态\n     */\n    private Long orderStatus;\n    /**\n     * 订单类型\n     */\n    private Long type;\n    /**\n     * 外部订单号\n     */\n    private String outOrderNum;\n    /**\n     * 描述\n     */\n    private String intro;\n    /**\n     * 标题\n     */\n    private String title;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcOrganizer.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\nimport java.util.List;\n\n\n/**\n * 活动主办方对象 pzc_organizer\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_organizer\")\npublic class PzcOrganizer extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * ID\n     */\n    @TableId(value = \"organizer_id\", type = IdType.AUTO)\n    private Long organizerId;\n    /**\n     * 电话号码\n     */\n    private String phone;\n    /**\n     * 名称\n     */\n    private String name;\n    /**\n     * 组织者标志图片\n     */\n    private String logo;\n    /**\n     * 主办方介绍\n     */\n    private String content;\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    private Integer state;\n\n    @TableField(exist = false)\n    private List<PzcOrganizerTicket> organizerTickets;  // 组织者票务列表\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcOrganizerTicket.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\n\n/**\n * 主办方票务对象 pzc_organizer_ticket\n *\n * @author ruoyi\n * @date 2023-07-22\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_organizer_ticket\")\npublic class PzcOrganizerTicket extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * ID\n     */\n    @TableId(value = \"organizer_ticket_id\", type = com.baomidou.mybatisplus.annotation.IdType.AUTO)\n    private Long organizerTicketId;\n    /**\n     * 名称\n     */\n    private String name;\n    /**\n     * 二维码图片\n     */\n    private String qrImage;\n    /**\n     * logo图\n     */\n    private String logoImage;\n    /**\n     * 关联主办方ID\n     */\n    private Long organizerId;\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    private Integer state;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcRegion.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\n\n/**\n * 地区对象 pzc_region\n *\n * @author ruoyi\n * @date 2023-07-22\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_region\")\npublic class PzcRegion extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * 地区id\n     */\n    @TableId(value = \"region_id\",type = IdType.AUTO)\n    private Long regionId;\n    /**\n     * 省\n     */\n    private String base;\n    /**\n     * 地区名称\n     */\n    private String name;\n    /**\n     * 城市主活动图\n     */\n    private String imgUrl;\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    private Integer state;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcTag.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\n\n/**\n * 活动标签对象 pzc_tag\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_tag\")\npublic class PzcTag extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * ID\n     */\n    @TableId(value = \"tag_id\", type = IdType.AUTO)\n    private Long tagId;\n    /**\n     * 名称\n     */\n    private String name;\n    /**\n     * 图片\n     */\n    private String imageUrl;\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    private Integer state;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcUser.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.system.domain.vo.PzcActivityGroupApplyVo;\n\nimport java.math.BigDecimal;\nimport java.util.List;\n\n/**\n * 用户对象 pzc_user\n *\n * @author ruoyi\n * @date 2023-07-09\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_user\")\npublic class PzcUser extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * 用户主键\n     */\n    @TableId(value = \"user_id\",type = IdType.AUTO)\n    private Long userId;\n    /**\n     * 用户在小程序端的 openId 唯一\n     */\n    private String openid;\n    /**\n     * 派币余额\n     */\n    private BigDecimal money;\n    /**\n     * 用户等级\n     */\n    private Long userLevel;\n    /**\n     * 用户累计积分\n     */\n    private Long integration;\n    /**\n     * 用户现有积分\n     */\n    private Long integrationNow;\n    /**\n     * 真实姓名\n     */\n    private String realname;\n    /**\n     * 昵称\n     */\n    private String nickname;\n    /**\n     * 用户性别 0 男  1 女  2 未知\n     */\n    private Integer sex;\n    /**\n     * 手机号\n     */\n    private String phone;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 地址\n     */\n    private String address;\n    /**\n     * 个人介绍\n     */\n    private String intro;\n    /**\n     * 年龄\n     */\n    private Long age;\n    /**\n     * 星座\n     */\n    private String constellation;\n    /**\n     * 人格类型\n     */\n    private String mbti;\n    /**\n     * 兴趣爱好\n     */\n    private String hobby;\n    /**\n     * 学校\n     */\n    private String school;\n    /**\n     * 职业\n     */\n    private String occupation;\n    /**\n     * 喜欢的音乐风格\n     */\n    private String musicStyle;\n    /**\n     * 状态 是否被封禁\n     */\n    private Long state;\n\n    @TableField(exist = false)\n    private List<PzcUserPhoto> userPhoto;\n\n\n    @TableField(exist = false)\n    private PzcActivityGroup pzcActivityGroup;\n\n    @TableField(exist = false)\n    private PzcActivityGroupApplyVo pzcActivityGroupApplyVo;\n\n\n    /**\n     * 用户免责取消次数  0点定时任务刷新\n     */\n    private Integer exemptCancel;\n\n\n    @TableField(exist = false)\n    private Boolean liveStatus;\n\n\n    @TableField(exist = false)\n    private Integer notReadCount;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcUserCollect.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\n\n/**\n * 用户收藏活动对象 pzc_user_collect\n *\n * @author ruoyi\n * @date 2023-07-08\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_user_collect\")\npublic class PzcUserCollect extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * ID\n     */\n    @TableId(value = \"collect_id\",type = IdType.AUTO)\n    private Long collectId;\n    /**\n     * 用户Id\n     */\n    private Long userId;\n    /**\n     * 收藏活动的Id\n     */\n    private Long activityId;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcUserHistory.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\nimport java.math.BigDecimal;\n\n\n/**\n * 用户操作历史记录对象 pzc_user_history\n *\n * @author ruoyi\n * @date 2023-07-06\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_user_history\")\npublic class PzcUserHistory extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     *\n     */\n    @TableId(value = \"history_id\",type = IdType.AUTO)\n    private Long historyId;\n    /**\n     * 关联用户Id\n     */\n    private Long userId;\n    /**\n     * 关联活动Id\n     */\n    private Long activityId;\n    /**\n     * 操作类型\n     */\n    private Long type;\n    /**\n     * 信息\n     */\n    private String message;\n\n    private BigDecimal money;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcUserPhoto.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\n\n/**\n * 用户资料相册对象 pzc_user_photo\n *\n * @author ruoyi\n * @date 2023-07-11\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_user_photo\")\npublic class PzcUserPhoto extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * 照片ID\n     */\n    @TableId(value = \"photo_id\", type = IdType.AUTO)\n    private Long photoId;\n    /**\n     * 用户ID\n     */\n    private Long userId;\n    /**\n     * 照片\n     */\n    private String url;\n    /**\n     * 照片说明\n     */\n    private String message;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcUserTalk.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\n\n\n/**\n * 用户聊天对象 pzc_user_talk\n *\n * @author ruoyi\n * @date 2023-07-16\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_user_talk\")\npublic class PzcUserTalk extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * 聊天ID\n     */\n    @TableId(value = \"talk_id\",type = IdType.AUTO)\n    private Long talkId;\n    /**\n     * 发起方\n     */\n    private Long fromUserId;\n    /**\n     * 接受方\n     */\n    private Long toUserId;\n\n\n    private Long userId; //消息的归属者 单向删除\n    /**\n     * 消息\n     */\n    private String message;\n    /**\n     * 消息状态\n     */\n    private Long messageStatus;\n    /**\n     * 消息类型\n     */\n    private Long messageType;\n\n    @TableField(exist = false)\n    private Integer notReadCount;\n\n    private Long groupId; //群组id\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/PzcViewPager.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n/**\n * 轮播图对象 pzc_view_pager\n *\n * @author ruoyi\n * @date 2023-05-23\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_view_pager\")\npublic class PzcViewPager extends BaseEntity {\n\n    private static final long serialVersionUID=1L;\n\n    /**\n     * 轮播图id\n     */\n    @TableId(value = \"view_pager_id\", type = IdType.AUTO)\n    private Integer viewPagerId;\n    /**\n     * 轮播图名称\n     */\n    private String name;\n    /**\n     * 轮播图图片Url\n     */\n    private String imageUrl;\n    /**\n     * 轮播图链接Url\n     */\n    private String linkUrl;\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    private Integer state;\n    /**\n     * 关联活动id  0表示不关联\n     */\n    private Long activityId;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PayOrderBo.java",
    "content": "package top.flya.system.domain.bo;\n\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Data\npublic class PayOrderBo {\n\n    @NotNull\n    private Integer count;\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcActivityBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.system.domain.PzcArtist;\nimport top.flya.system.domain.PzcIntro;\nimport top.flya.system.domain.PzcOrganizer;\nimport top.flya.system.domain.PzcTag;\n\nimport javax.validation.constraints.*;\nimport java.util.List;\n\n\n/**\n * 活动业务对象 pzc_activity\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcActivityBo extends BaseEntity {\n\n    /**\n     * 活动id\n     */\n    @NotNull(message = \"活动id不能为空\", groups = { EditGroup.class })\n    private Integer activityId;\n\n    /**\n     * 地址\n     */\n    @NotBlank(message = \"地址不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String address;\n\n    /**\n     * 城市ID\n     */\n    @NotNull(message = \"城市ID不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Integer regionId;\n\n    /**\n     * 活动标题\n     */\n    @NotBlank(message = \"活动标题不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String title;\n\n    /**\n     * 开始时间\n     */\n    @NotBlank(message = \"开始时间不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String startTime;\n\n    /**\n     * 结束时间\n     */\n    @NotBlank(message = \"结束时间不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String endDate;\n\n\n    /**\n     * 展示时间\n     */\n//    @NotBlank(message = \"展示时间不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String showTime;\n\n\n    /**\n     * 封面图片\n     */\n    @NotBlank(message = \"封面图片不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String coverImage;\n    /**\n     * 活动详情主图\n     */\n    @NotNull(message = \"活动详情主图不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String innerImage;\n\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    private Integer state;\n\n\n    private Integer classify; //属于哪个分类\n\n    private Integer region; // 0 国际 1 国内\n\n\n    private List<PzcIntro> stageList;  // 场地舞台列表\n\n    private List<PzcIntro> introList;  // 简介列表\n\n    private List<PzcTag> tagList;  // 标签列表\n\n    private List<PzcArtist> artistList;  // 艺人列表\n\n    @NotNull(message = \"主办方不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private PzcOrganizer organizerList;  // 主办方列表\n\n\n    private String shareImage; //分享图片\n\n\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcActivityConnArtistBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.*;\n\n\n/**\n * 活动关联艺人业务对象 pzc_activity_conn_artist\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcActivityConnArtistBo extends BaseEntity {\n\n    /**\n     * ID\n     */\n    @NotNull(message = \"ID不能为空\", groups = { EditGroup.class })\n    private Integer activityConnArtistId;\n\n    /**\n     * 活动ID\n     */\n    @NotNull(message = \"活动ID不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Integer activityId;\n\n    /**\n     * 艺人ID\n     */\n    @NotNull(message = \"艺人ID不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Integer artistId;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcActivityConnIntroBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.*;\n\n\n/**\n * 活动介绍与活动关联业务对象 pzc_activity_conn_intro\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcActivityConnIntroBo extends BaseEntity {\n\n    /**\n     * ID\n     */\n    @NotNull(message = \"ID不能为空\", groups = { EditGroup.class })\n    private Integer activityConnIntroId;\n\n    /**\n     * 活动ID\n     */\n    @NotNull(message = \"活动ID不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Integer activityId;\n\n    /**\n     * 活动介绍ID\n     */\n    @NotNull(message = \"活动介绍ID不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Integer introId;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcActivityConnTagBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.*;\n\n\n/**\n * 活动标签与活动关联业务对象 pzc_activity_conn_tag\n *\n * @author ruoyi\n * @date 2023-06-03\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcActivityConnTagBo extends BaseEntity {\n\n    /**\n     * ID\n     */\n    @NotNull(message = \"ID不能为空\", groups = { EditGroup.class })\n    private Integer activityConnTagId;\n\n    /**\n     * 活动ID\n     */\n    @NotNull(message = \"活动ID不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Integer activityId;\n\n    /**\n     * 活动标签ID\n     */\n    @NotNull(message = \"活动标签ID不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Integer tagId;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcActivityGroupApplyBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.*;\n\nimport java.math.BigDecimal;\n\n/**\n * 活动组队申请列业务对象 pzc_activity_group_apply\n *\n * @author ruoyi\n * @date 2023-07-10\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcActivityGroupApplyBo extends BaseEntity {\n\n    /**\n     * 申请ID\n     */\n    @NotNull(message = \"申请ID不能为空\", groups = { EditGroup.class })\n    private Long applyId;\n\n    /**\n     * 申请人ID\n     */\n//    @NotNull(message = \"申请人ID不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long userId;\n\n    /**\n     * 申请的活动ID\n     */\n    @NotNull(message = \"申请的活动ID不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long activityId;\n\n    /**\n     * 申请加入的组ID\n     */\n    @NotNull(message = \"申请加入的组ID不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long groupId;\n\n    /**\n     * 申请状态\n     *  0 位于申请列表中 1 申请通过待确认时 2 确认通过进行中 3 组队结束  //申请状态不应该由前端传入 由后端自动计算\n     */\n//    private Integer applyStatus;\n    /**\n     * 0 AA制\n1 我买单\n2 你买单\n     */\n    @NotNull(message = \"0 AA制 我买单 2 你买单不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long groupType;\n\n    /**\n     * 活动保证金\n     */\n    @NotNull(message = \"活动保证金不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private BigDecimal money;\n\n    /**\n     * 留言内容\n     */\n    @NotBlank(message = \"留言内容不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String message;\n\n\n    // 无限制确认到达\n    private Integer wxz;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcActivityGroupBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.math.BigDecimal;\nimport java.util.Date;\n\n/**\n * 活动组队业务对象 pzc_activity_group\n *\n * @author ruoyi\n * @date 2023-07-10\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcActivityGroupBo extends BaseEntity {\n\n    /**\n     * 组队ID\n     */\n    @NotNull(message = \"组队ID不能为空\", groups = { EditGroup.class })\n    private Long groupId;\n\n    /**\n     * 活动ID\n     */\n    @NotNull(message = \"活动ID不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long activityId;\n\n\n    private Integer region;\n    /**\n     * 活动组队发起人ID\n     */\n    private Long userId;\n\n\n    private String activityName;\n\n    /**\n     * 活动主题\n     */\n    @NotBlank(message = \"活动主题不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String title;\n\n    /**\n     * 活动组队所缴纳的保证金\n     */\n    @NotNull(message = \"活动组队所缴纳的保证金不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private BigDecimal money;\n\n    /**\n     * 买单方式\n     */\n    @NotNull(message = \"买单方式不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long groupType;\n\n    /**\n     * 活动地址\n     */\n    @NotBlank(message = \"活动地址不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String address;\n\n    /**\n     * 一起约定好的时间\n     */\n    @NotNull(message = \"一起约定好的时间不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Date activityTime;\n\n    /**\n     * 权限\n     */\n    @NotNull(message = \"权限不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long auth;\n\n\n    private Integer userSex;\n\n    private Integer distance;// 1\n\n\n    private Integer userLevel;\n\n    private Integer activityTime1;\n\n    private String  longitudeAndLatitude; //经纬度\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcArtistBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.*;\n\n\n/**\n * 艺人业务对象 pzc_artist\n *\n * @author flya\n * @date 2023-06-01\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcArtistBo extends BaseEntity {\n\n    /**\n     * ID\n     */\n    private Long artistId;\n\n    /**\n     * 艺人名\n     */\n    @NotBlank(message = \"艺人名不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String name;\n\n    /**\n     * 艺人照片\n     */\n    private String imageUrl;\n\n    /**\n     * 艺人介绍\n     */\n    private String description;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcIntroBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\n\n\n/**\n * 活动介绍业务对象 pzc_intro\n *\n * @author ruoyi\n * @date 2023-08-04\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcIntroBo extends BaseEntity {\n\n    /**\n     * ID\n     */\n    @NotNull(message = \"ID不能为空\", groups = { EditGroup.class })\n    private Long introId;\n\n    /**\n     * 标题\n     */\n    @NotBlank(message = \"标题不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String title;\n\n    /**\n     * 内容\n     */\n//    @NotBlank(message = \"内容不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String content;\n\n    /**\n     * 0 场地舞台介绍 1 更多介绍\n     */\n    @NotNull(message = \"0 场地舞台介绍 1 更多介绍不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long type;\n\n    /**\n     * 图片\n     */\n    @NotBlank(message = \"图片不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String imageFullUrl;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcOfficialBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\n\n\n/**\n * 官方消息业务对象 pzc_official\n *\n * @author ruoyi\n * @date 2023-07-27\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcOfficialBo extends BaseEntity {\n\n    /**\n     * ID\n     */\n    @NotNull(message = \"ID不能为空\", groups = { EditGroup.class })\n    private Long officialId;\n\n    /**\n     * 来自谁的消息\n     */\n    @NotNull(message = \"来自谁的消息不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long fromUserId;\n\n    /**\n     * 给谁发的消息\n     */\n    @NotNull(message = \"给谁发的消息不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long toUserId;\n\n    /**\n     * 标题\n     */\n    @NotBlank(message = \"标题不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String title;\n\n    /**\n     * 主体消息\n     */\n    @NotBlank(message = \"主体消息不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String content;\n\n    /**\n     * 是否已读\n     */\n    @NotNull(message = \"是否已读不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long isRead;\n\n    /**\n     * 来自那场组队的消息\n     */\n    @NotNull(message = \"来自那场组队的消息不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long groupId;\n\n    /**\n     * 来自那场活动的消息\n     */\n    @NotNull(message = \"来自那场活动的消息不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long activityId;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcOrderBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.*;\n\nimport java.math.BigDecimal;\n\n/**\n * 订单业务对象 pzc_order\n *\n * @author ruoyi\n * @date 2023-07-09\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcOrderBo extends BaseEntity {\n\n    /**\n     * 订单ID\n     */\n    @NotNull(message = \"订单ID不能为空\", groups = { EditGroup.class })\n    private Long orderId;\n\n    /**\n     * 用户ID\n     */\n    @NotNull(message = \"用户ID不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long userId;\n\n    /**\n     * 活动ID\n     */\n    @NotNull(message = \"活动ID不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long activityId;\n\n    /**\n     * 订单金额\n     */\n    @NotNull(message = \"订单金额不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private BigDecimal money;\n\n    /**\n     * 订单状态\n     */\n    @NotNull(message = \"订单状态不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long orderStatus;\n\n    /**\n     * 订单类型\n     */\n    @NotNull(message = \"订单类型不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long type;\n\n    /**\n     * 外部订单号\n     */\n    @NotBlank(message = \"外部订单号不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String outOrderNum;\n\n    /**\n     * 描述\n     */\n    @NotBlank(message = \"描述不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String intro;\n\n    /**\n     * 标题\n     */\n    @NotBlank(message = \"标题不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String title;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcOrganizerBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.*;\n\n\n/**\n * 活动主办方业务对象 pzc_organizer\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcOrganizerBo extends BaseEntity {\n\n    /**\n     * ID\n     */\n    @NotNull(message = \"ID不能为空\", groups = { EditGroup.class })\n    private Long organizerId;\n\n    /**\n     * 电话号码\n     */\n    @NotBlank(message = \"电话号码不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String phone;\n\n    /**\n     * 名称\n     */\n    @NotBlank(message = \"名称不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String name;\n\n    /**\n     * 组织者标志图片\n     */\n    private String logo;\n\n    /**\n     * 主办方介绍\n     */\n    @NotBlank(message = \"主办方介绍不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String content;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcOrganizerTicketBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\n\n\n/**\n * 主办方票务业务对象 pzc_organizer_ticket\n *\n * @author ruoyi\n * @date 2023-07-22\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcOrganizerTicketBo extends BaseEntity {\n\n    /**\n     * ID\n     */\n    @NotNull(message = \"ID不能为空\", groups = { EditGroup.class })\n    private Long organizerTicketId;\n\n    /**\n     * 名称\n     */\n    @NotBlank(message = \"名称不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String name;\n\n    /**\n     * 二维码图片\n     */\n    @NotBlank(message = \"二维码图片不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String qrImage;\n\n    /**\n     * logo图\n     */\n    @NotBlank(message = \"logo图不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String logoImage;\n\n    /**\n     * 关联主办方ID\n     */\n    @NotNull(message = \"关联主办方ID不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long organizerId;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcRegionBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\n\n\n/**\n * 地区业务对象 pzc_region\n *\n * @author ruoyi\n * @date 2023-07-22\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcRegionBo extends BaseEntity {\n\n    /**\n     * 地区id\n     */\n    @NotNull(message = \"地区id不能为空\", groups = { EditGroup.class })\n    private Long regionId;\n\n    /**\n     * 省\n     */\n    @NotBlank(message = \"省不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String base;\n\n    /**\n     * 地区名称\n     */\n    @NotBlank(message = \"地区名称不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String name;\n\n    /**\n     * 城市主活动图\n     */\n    @NotBlank(message = \"城市主活动图不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String imgUrl;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcTagBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.*;\n\n\n/**\n * 活动标签业务对象 pzc_tag\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcTagBo extends BaseEntity {\n\n    /**\n     * ID\n     */\n    @NotNull(message = \"ID不能为空\", groups = { EditGroup.class })\n    private Long tagId;\n\n    /**\n     * 名称\n     */\n    @NotBlank(message = \"名称不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String name;\n\n    /**\n     * 图片\n     */\n    @NotBlank(message = \"图片不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String imageUrl;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcUserBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.*;\n\nimport java.math.BigDecimal;\n\n/**\n * 用户业务对象 pzc_user\n *\n * @author ruoyi\n * @date 2023-07-06\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcUserBo extends BaseEntity {\n\n    /**\n     * 用户主键\n     */\n    @NotNull(message = \"用户主键不能为空\", groups = { EditGroup.class })\n    private Long userId;\n\n    /**\n     * OpenId\n     */\n    private String openid;\n\n\n    /**\n     * 换取openid的code\n     */\n    private String loginCode;\n\n    /**\n     * 换取手机号的code\n     */\n    private String phoneCode;\n\n    /**\n     * 派币余额\n     */\n    private BigDecimal money;\n\n    /**\n     * 真实姓名\n     */\n    @NotBlank(message = \"真实姓名不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String realname;\n\n    /**\n     * 昵称\n     */\n    @NotBlank(message = \"昵称不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String nickname;\n\n    /**\n     * 性别\n     */\n    @NotNull(message = \"性别不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Integer sex;\n\n\n    /**\n     * 用户等级\n     */\n    private Long userLevel;\n    /**\n     * 用户累计积分\n     */\n    private Long integration;\n    /**\n     * 用户现有积分\n     */\n    private Long integrationNow;\n\n\n    /**\n     * 手机号\n     */\n    private String phone;\n\n    /**\n     * 头像\n     */\n    private String avatar;\n\n    /**\n     * 地址\n     */\n    private String address;\n\n    /**\n     * 介绍\n     */\n    private String intro;\n\n    /**\n     * 年龄\n     */\n    private Long age;\n\n    /**\n     * 星座\n     */\n    private String constellation;\n\n    /**\n     * 人格类型\n     */\n    private String mbti;\n\n    /**\n     * 兴趣爱好\n     */\n    private String hobby;\n\n    /**\n     * 学校\n     */\n    private String school;\n\n    /**\n     * 职业\n     */\n    private String occupation;\n\n    /**\n     * 音乐风格\n     */\n    private String musicStyle;\n\n    /**\n     * 封禁状态\n     */\n    private Integer state;\n\n\n    private Integer exemptCancel;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcUserCollectBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.*;\n\n\n/**\n * 用户收藏活动业务对象 pzc_user_collect\n *\n * @author ruoyi\n * @date 2023-07-08\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcUserCollectBo extends BaseEntity {\n\n    /**\n     * ID\n     */\n    @NotNull(message = \"ID不能为空\", groups = { EditGroup.class })\n    private Long collectId;\n\n    /**\n     * 用户Id\n     */\n    @NotNull(message = \"用户Id不能为空\", groups = { EditGroup.class })\n    private Long userId;\n\n    /**\n     * 收藏活动的Id\n     */\n    @NotNull(message = \"收藏活动的Id不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long activityId;\n\n\n    private Integer type ; // 收藏类型 0电音节 1派对\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcUserHistoryBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\n\n\n/**\n * 用户操作历史记录业务对象 pzc_user_history\n *\n * @author ruoyi\n * @date 2023-07-06\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcUserHistoryBo extends BaseEntity {\n\n    /**\n     *\n     */\n    @NotNull(message = \"不能为空\", groups = { EditGroup.class })\n    private Long historyId;\n\n    /**\n     * 关联用户Id\n     */\n    @NotNull(message = \"关联用户Id不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long userId;\n\n    /**\n     * 关联活动Id\n     */\n    @NotNull(message = \"关联活动Id不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long activityId;\n\n    /**\n     * 操作类型\n     */\n    @NotNull(message = \"操作类型不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long[] type;\n\n    /**\n     * 信息\n     */\n    @NotBlank(message = \"信息不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String message;\n\n\n    private String nowTime;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcUserPhotoBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.*;\n\n\n/**\n * 用户资料相册业务对象 pzc_user_photo\n *\n * @author ruoyi\n * @date 2023-07-11\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcUserPhotoBo extends BaseEntity {\n\n    /**\n     * 照片ID\n     */\n    @NotNull(message = \"照片ID不能为空\", groups = { EditGroup.class })\n    private Long photoId;\n\n    /**\n     * 用户ID\n     */\n    private Long userId;\n\n    /**\n     * 照片\n     */\n    @NotBlank(message = \"照片不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String url;\n\n    /**\n     * 照片说明\n     */\n    private String message;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcUserTalkBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.*;\n\n\n/**\n * 用户聊天业务对象 pzc_user_talk\n *\n * @author ruoyi\n * @date 2023-07-16\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcUserTalkBo extends BaseEntity {\n\n    /**\n     * 聊天ID\n     */\n    @NotNull(message = \"聊天ID不能为空\", groups = { EditGroup.class })\n    private Long talkId;\n\n    /**\n     * 发起方\n     */\n    @NotNull(message = \"发起方不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long fromUserId;\n\n    /**\n     * 接受方\n     */\n    @NotNull(message = \"接受方不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long toUserId;\n\n    /**\n     * 消息\n     */\n    @NotBlank(message = \"消息不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String message;\n\n    /**\n     * 消息状态\n     */\n//    @NotNull(message = \"消息状态不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long messageStatus;\n\n    /**\n     * 消息类型\n     */\n    @NotNull(message = \"消息类型不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long messageType;\n\n\n    private Long userId; //消息的归属者 单向删除\n\n\n    private Long groupId; //群组id\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcViewPagerBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\n\nimport javax.validation.constraints.*;\n\n\n/**\n * 轮播图业务对象 pzc_view_pager\n *\n * @author ruoyi\n * @date 2023-05-23\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PzcViewPagerBo extends BaseEntity {\n\n    /**\n     * 轮播图id\n     */\n    @NotNull(message = \"轮播图id不能为空\", groups = { EditGroup.class })\n    private Integer viewPagerId;\n\n    /**\n     * 轮播图名称\n     */\n    @NotBlank(message = \"轮播图名称不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String name;\n\n    /**\n     * 轮播图图片Url\n     */\n    @NotBlank(message = \"轮播图图片Url不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String imageUrl;\n\n    /**\n     * 轮播图链接Url\n     */\n    @NotBlank(message = \"轮播图链接Url不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private String linkUrl;\n\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    @NotNull(message = \"删除状态，默认为1表示正常状态不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Integer state;\n\n    /**\n     * 关联活动id  0表示不关联\n     */\n    @NotNull(message = \"关联活动id  0表示不关联不能为空\", groups = { AddGroup.class, EditGroup.class })\n    private Long activityId;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/RefurbishBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\n\n@Data\npublic class RefurbishBo {\n\n   private  Long applyId;\n   private  Integer role;\n   private  String address;\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/Resource.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\n\n/**\n * Created with IntelliJ IDEA.\n *\n * @author: 风离\n * @Date: 2022/06/04/2:59\n * @Description:\n */\n@Data\npublic class Resource {\n    private String original_type;\n    private String algorithm;\n    private String ciphertext;\n    private String associated_data;\n    private String nonce;\n\n}\n\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/SuccessCallBackObjBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\n\n/**\n * Created with IntelliJ IDEA.\n *\n * @author: 风离\n * @Date: 2022/06/04/2:57\n * @Description:\n */\n@Data\npublic class SuccessCallBackObjBo {\n\n    private String id;\n\n    private String create_time;\n\n    private String event_type;\n\n    private String resource_type;\n\n    private Resource resource;\n\n    private String summary;\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/UpdateMoneyBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport lombok.Data;\n\nimport java.math.BigDecimal;\n\n@Data\npublic class UpdateMoneyBo {\n\n    private Long userId;\n\n    private BigDecimal money;\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/bo/WxzApplyBo.java",
    "content": "package top.flya.system.domain.bo;\n\n\nimport lombok.Data;\n\n@Data\npublic class WxzApplyBo {\n\n    private Integer fromUserId;\n\n    private Integer toUserId;\n\n    private Integer activityId;\n\n    private Integer groupId;\n\n    private Integer applyId;\n\n    private String message;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcActivityConnArtistVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\n\nimport java.util.Date;\n\n\n/**\n * 活动关联艺人视图对象 pzc_activity_conn_artist\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@Data\n@ExcelIgnoreUnannotated\npublic class PzcActivityConnArtistVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * ID\n     */\n    @ExcelProperty(value = \"ID\")\n    private Integer activityConnArtistId;\n\n    /**\n     * 活动ID\n     */\n    @ExcelProperty(value = \"活动ID\")\n    private Integer activityId;\n\n    /**\n     * 艺人ID\n     */\n    @ExcelProperty(value = \"艺人ID\")\n    private Integer artistId;\n\n    /**\n     * 创建时间\n     */\n    @ExcelProperty(value = \"创建时间\")\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    @ExcelProperty(value = \"更新时间\")\n    private Date updateTime;\n\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    @ExcelProperty(value = \"删除状态，默认为1表示正常状态\")\n    private Integer state;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcActivityConnIntroVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\n\nimport java.util.Date;\n\n\n/**\n * 活动介绍与活动关联视图对象 pzc_activity_conn_intro\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@Data\n@ExcelIgnoreUnannotated\npublic class PzcActivityConnIntroVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * ID\n     */\n    @ExcelProperty(value = \"ID\")\n    private Integer activityConnIntroId;\n\n    /**\n     * 活动ID\n     */\n    @ExcelProperty(value = \"活动ID\")\n    private Integer activityId;\n\n    /**\n     * 活动介绍ID\n     */\n    @ExcelProperty(value = \"活动介绍ID\")\n    private Integer introId;\n\n    /**\n     * 创建时间\n     */\n    @ExcelProperty(value = \"创建时间\")\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    @ExcelProperty(value = \"更新时间\")\n    private Date updateTime;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcActivityConnTagVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\n\nimport java.util.Date;\n\n\n/**\n * 活动标签与活动关联视图对象 pzc_activity_conn_tag\n *\n * @author ruoyi\n * @date 2023-06-03\n */\n@Data\n@ExcelIgnoreUnannotated\npublic class PzcActivityConnTagVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * ID\n     */\n    @ExcelProperty(value = \"ID\")\n    private Integer activityConnTagId;\n\n    /**\n     * 活动ID\n     */\n    @ExcelProperty(value = \"活动ID\")\n    private Integer activityId;\n\n    /**\n     * 活动标签ID\n     */\n    @ExcelProperty(value = \"活动标签ID\")\n    private Integer tagId;\n\n    /**\n     * 创建时间\n     */\n    @ExcelProperty(value = \"创建时间\")\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    @ExcelProperty(value = \"更新时间\")\n    private Date updateTime;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcActivityGroupApplyVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport lombok.Data;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\n\nimport java.math.BigDecimal;\nimport java.util.Date;\n\n\n/**\n * 活动组队申请列视图对象 pzc_activity_group_apply\n *\n * @author ruoyi\n * @date 2023-07-10\n */\n@Data\n@ExcelIgnoreUnannotated\npublic class PzcActivityGroupApplyVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 申请ID\n     */\n    @ExcelProperty(value = \"申请ID\")\n    private Long applyId;\n\n    /**\n     * 申请人ID\n     */\n    @ExcelProperty(value = \"申请人ID\")\n    private Long userId;\n\n    /**\n     * 申请的活动ID\n     */\n    @ExcelProperty(value = \"申请的活动ID\")\n    private Long activityId;\n\n\n    /**\n     * 活动标题\n     */\n    private String activityTitle;\n\n    /**\n     * 组队的标题\n     */\n    private String groupTitle;\n\n    /**\n     * 申请加入的组ID\n     */\n    @ExcelProperty(value = \"申请加入的组ID\")\n    private Long groupId;\n\n    /**\n     * 0 AA制\n1 我买单\n2 你买单\n     */\n    @ExcelProperty(value = \"0 AA制 1 我买单 2 你买单\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"group_pay_type\")\n    private Long groupType;\n\n    /**\n     * 活动保证金\n     */\n    @ExcelProperty(value = \"活动保证金\")\n    private BigDecimal money;\n\n    /**\n     * 留言内容\n     */\n    @ExcelProperty(value = \"留言内容\")\n    private String message;\n\n\n    /**\n     * 0 位于申请列表中 1 申请通过待确认时 2 确认通过进行中 3 组队结束\n     */\n    @ExcelProperty(value = \"-1 已取消 0 位于申请列表中 1 申请通过待确认时 2 确认通过进行中 3 组队结束\", converter = ExcelDictConvert.class)\n    private Integer applyStatus;\n    /**\n     *\n     */\n    @ExcelProperty(value = \"\")\n    private Date createTime;\n\n    /**\n     *\n     */\n    @ExcelProperty(value = \"\")\n    private Date updateTime;\n\n    //申请人的头像以及用户名\n    private String nickName;\n\n    private String avatar;\n\n    /**\n     *  无限制确认到达 0 未确认 1 已确认\n     */\n    private Integer wxz;\n\n\n    //发起方当前位置\n    private String startAddress;\n    //申请方当前位置\n    private String applyAddress;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcActivityGroupVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport lombok.Data;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport top.flya.system.domain.PzcUserPhoto;\n\nimport java.math.BigDecimal;\nimport java.util.Date;\nimport java.util.List;\n\n\n/**\n * 活动组队视图对象 pzc_activity_group\n *\n * @author ruoyi\n * @date 2023-07-10\n */\n@Data\n@ExcelIgnoreUnannotated\npublic class PzcActivityGroupVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 组队ID\n\n     */\n    @ExcelProperty(value = \"组队ID\")\n    private Long groupId;\n\n    /**\n     * 活动ID\n     */\n    @ExcelProperty(value = \"活动ID\")\n    private Long activityId;\n\n\n    private Integer region;\n    /**\n     * 活动组队发起人ID\n     */\n    @ExcelProperty(value = \"活动组队发起人ID\")\n    private Long userId;\n\n    /**\n     * 活动组队发起人\n     */\n    private PzcUserVo user;\n\n    /**\n     * 活动主题\n     */\n    @ExcelProperty(value = \"活动主题\")\n    private String title;\n\n\n    private String activityTitle;\n\n    /**\n     * 活动组队所缴纳的保证金\n     */\n    @ExcelProperty(value = \"活动组队所缴纳的保证金\")\n    private BigDecimal money;\n\n    /**\n     * 买单方式\n     */\n    @ExcelProperty(value = \"买单方式\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"group_pay_type\")\n    private Long groupType;\n\n    /**\n     * 活动地址\n     */\n    @ExcelProperty(value = \"活动地址\")\n    private String address;\n\n    /**\n     * 一起约定好的时间\n     */\n    @ExcelProperty(value = \"一起约定好的时间\")\n    private Date activityTime;\n\n    /**\n     * 权限\n     */\n    @ExcelProperty(value = \"权限\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"group_auth\")\n    private Long auth;\n\n    /**\n     * 创建时间\n     */\n    @ExcelProperty(value = \"创建时间\")\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    @ExcelProperty(value = \"更新时间\")\n    private Date updateTime;\n\n\n    private List<PzcUserPhoto> photo;\n\n\n    private Boolean ifShow =  true;\n\n\n    private BigDecimal distance; //距离多少米\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcActivityVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport top.flya.system.domain.PzcArtist;\nimport top.flya.system.domain.PzcIntro;\nimport top.flya.system.domain.PzcOrganizer;\nimport top.flya.system.domain.PzcTag;\n\nimport java.util.Date;\nimport java.util.List;\n\n\n/**\n * 活动视图对象 pzc_activity\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@Data\n@ExcelIgnoreUnannotated\n@AllArgsConstructor\n@NoArgsConstructor\npublic class PzcActivityVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 活动id\n     */\n    @ExcelProperty(value = \"活动id\")\n    private Integer activityId;\n\n    /**\n     * 地址\n     */\n    @ExcelProperty(value = \"地址\")\n    private String address;\n\n    /**\n     * 城市ID\n     */\n    @ExcelProperty(value = \"城市ID\")\n    private Integer regionId;\n\n    /**\n     * 活动标题\n     */\n    @ExcelProperty(value = \"活动标题\")\n    private String title;\n\n    /**\n     * 开始时间\n     */\n    @ExcelProperty(value = \"开始时间\")\n    private String startTime;\n\n    /**\n     * 结束时间\n     */\n    @ExcelProperty(value = \"结束时间\")\n    private String endDate;\n\n    /**\n     * 活动详情主图\n     */\n    @ExcelProperty(value = \"活动详情主图\")\n    private String innerImage;\n\n    /**\n     * 展示时间\n     */\n    @ExcelProperty(value = \"展示时间\")\n    private String showTime;\n\n    /**\n     * 封面图片\n     */\n    @ExcelProperty(value = \"封面图片\")\n    private String coverImage;\n\n    /**\n     * 创建时间\n     */\n    @ExcelProperty(value = \"创建时间\")\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    @ExcelProperty(value = \"更新时间\")\n    private Date updateTime;\n\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    @ExcelProperty(value = \"删除状态，默认为1表示正常状态\")\n    private Integer state;\n\n\n\n    private Long organizerId; // 主办方id\n\n    private Integer classify; //属于哪个分类\n\n    private Integer region; // 0 国内 1 国外\n\n    private List<PzcIntro> introList;  // 简介列表\n\n    private List<PzcTag> tagList;  // 标签列表\n\n    private List<PzcArtist> artistList;  // 艺人列表\n\n    private PzcOrganizer organizerList;  // 主办方列表\n\n    private List<PzcIntro> stageList;  // 场地列表\n\n\n    private String shareImage; //分享图片\n//    private String distance; //距我多少km\n\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcArtistVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.NoArgsConstructor;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\n\n\n/**\n * 艺人视图对象 pzc_artist\n *\n * @author flya\n * @date 2023-06-01\n */\n@Data\n@ExcelIgnoreUnannotated\n@AllArgsConstructor\n@NoArgsConstructor\npublic class PzcArtistVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * ID\n     */\n    @ExcelProperty(value = \"ID\")\n    private Long artistId;\n\n    /**\n     * 艺人名\n     */\n    @ExcelProperty(value = \"艺人名\")\n    private String name;\n\n    /**\n     * 艺人照片\n     */\n    @ExcelProperty(value = \"艺人照片\")\n    private String imageUrl;\n\n    /**\n     * 艺人介绍\n     */\n    @ExcelProperty(value = \"艺人介绍\")\n    private String description;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcIntroVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n\n/**\n * 活动介绍视图对象 pzc_intro\n *\n * @author ruoyi\n * @date 2023-08-04\n */\n@Data\n@ExcelIgnoreUnannotated\n@AllArgsConstructor\n@NoArgsConstructor\npublic class PzcIntroVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * ID\n     */\n    @ExcelProperty(value = \"ID\")\n    private Long introId;\n\n    /**\n     * 标题\n     */\n    @ExcelProperty(value = \"标题\")\n    private String title;\n\n    /**\n     * 内容\n     */\n    @ExcelProperty(value = \"内容\")\n    private String content;\n\n    /**\n     * 0 场地舞台介绍 1 更多介绍\n     */\n    @ExcelProperty(value = \"0 场地舞台介绍 1 更多介绍\")\n    private Long type;\n\n    /**\n     * 图片\n     */\n    @ExcelProperty(value = \"图片\")\n    private String imageFullUrl;\n\n    /**\n     * 创建时间\n     */\n    @ExcelProperty(value = \"创建时间\")\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    @ExcelProperty(value = \"更新时间\")\n    private Date updateTime;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcOfficialVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport lombok.Data;\n\nimport java.util.Date;\n\n\n/**\n * 官方消息视图对象 pzc_official\n *\n * @author ruoyi\n * @date 2023-07-27\n */\n@Data\n@ExcelIgnoreUnannotated\npublic class PzcOfficialVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * ID\n     */\n    @ExcelProperty(value = \"ID\")\n    private Long officialId;\n\n    /**\n     * 来自谁的消息\n     */\n    @ExcelProperty(value = \"来自谁的消息\")\n    private Long fromUserId;\n\n    /**\n     * 给谁发的消息\n     */\n    @ExcelProperty(value = \"给谁发的消息\")\n    private Long toUserId;\n\n    /**\n     * 标题\n     */\n    @ExcelProperty(value = \"标题\")\n    private String title;\n\n    /**\n     * 主体消息\n     */\n    @ExcelProperty(value = \"主体消息\")\n    private String content;\n\n    /**\n     * 是否已读\n     */\n    @ExcelProperty(value = \"是否已读\")\n    private Long isRead;\n\n    /**\n     * 来自那场组队的消息\n     */\n    @ExcelProperty(value = \"来自那场组队的消息\")\n    private Long groupId;\n\n    /**\n     * 来自那场活动的消息\n     */\n    @ExcelProperty(value = \"来自那场活动的消息\")\n    private Long activityId;\n\n    /**\n     * 创建时间\n     */\n    @ExcelProperty(value = \"创建时间\")\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    @ExcelProperty(value = \"更新时间\")\n    private Date updateTime;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcOrderVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport java.math.BigDecimal;\nimport java.util.Date;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\n\n\n/**\n * 订单视图对象 pzc_order\n *\n * @author ruoyi\n * @date 2023-07-09\n */\n@Data\n@ExcelIgnoreUnannotated\npublic class PzcOrderVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 订单ID\n     */\n    @ExcelProperty(value = \"订单ID\")\n    private Long orderId;\n\n    /**\n     * 用户ID\n     */\n    @ExcelProperty(value = \"用户ID\")\n    private Long userId;\n\n    /**\n     * 活动ID\n     */\n    @ExcelProperty(value = \"活动ID\")\n    private Long activityId;\n\n    /**\n     * 订单金额\n     */\n    @ExcelProperty(value = \"订单金额\")\n    private BigDecimal money;\n\n    /**\n     * 订单状态\n     */\n    @ExcelProperty(value = \"订单状态\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"order_status\")\n    private Long orderStatus;\n\n    /**\n     * 订单类型\n     */\n    @ExcelProperty(value = \"订单类型\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"order_type\")\n    private Long type;\n\n    /**\n     * 外部订单号\n     */\n    @ExcelProperty(value = \"外部订单号\")\n    private String outOrderNum;\n\n    /**\n     * 描述\n     */\n    @ExcelProperty(value = \"描述\")\n    private String intro;\n\n    /**\n     * 标题\n     */\n    @ExcelProperty(value = \"标题\")\n    private String title;\n\n    /**\n     * 订单创建时间\n     */\n    @ExcelProperty(value = \"订单创建时间\")\n    private Date createTime;\n\n    /**\n     * 订单更新时间\n     */\n    @ExcelProperty(value = \"订单更新时间\")\n    private Date updateTime;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcOrganizerTicketVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\n\n\n/**\n * 主办方票务视图对象 pzc_organizer_ticket\n *\n * @author ruoyi\n * @date 2023-07-22\n */\n@Data\n@ExcelIgnoreUnannotated\npublic class PzcOrganizerTicketVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * ID\n     */\n    @ExcelProperty(value = \"ID\")\n    private Long organizerTicketId;\n\n    /**\n     * 名称\n     */\n    @ExcelProperty(value = \"名称\")\n    private String name;\n\n    /**\n     * 二维码图片\n     */\n    @ExcelProperty(value = \"二维码图片\")\n    private String qrImage;\n\n    /**\n     * logo图\n     */\n    @ExcelProperty(value = \"logo图\")\n    private String logoImage;\n\n    /**\n     * 关联主办方ID\n     */\n    @ExcelProperty(value = \"关联主办方ID\")\n    private Long organizerId;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcOrganizerVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.NoArgsConstructor;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\n\nimport java.util.Date;\n\n\n/**\n * 活动主办方视图对象 pzc_organizer\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@Data\n@ExcelIgnoreUnannotated\n@AllArgsConstructor\n@NoArgsConstructor\npublic class PzcOrganizerVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * ID\n     */\n    @ExcelProperty(value = \"ID\")\n    private Long organizerId;\n\n    /**\n     * 电话号码\n     */\n    @ExcelProperty(value = \"电话号码\")\n    private String phone;\n\n    /**\n     * 名称\n     */\n    @ExcelProperty(value = \"名称\")\n    private String name;\n\n    /**\n     * 组织者标志图片\n     */\n    @ExcelProperty(value = \"组织者标志图片\")\n    private String logo;\n\n    /**\n     * 主办方介绍\n     */\n    @ExcelProperty(value = \"主办方介绍\")\n    private String content;\n\n    /**\n     * 创建时间\n     */\n    @ExcelProperty(value = \"创建时间\")\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    @ExcelProperty(value = \"更新时间\")\n    private Date updateTime;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcRegionVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n\n/**\n * 地区视图对象 pzc_region\n *\n * @author ruoyi\n * @date 2023-07-22\n */\n@Data\n@ExcelIgnoreUnannotated\n@AllArgsConstructor\npublic class PzcRegionVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 地区id\n     */\n    @ExcelProperty(value = \"地区id\")\n    private Long regionId;\n\n    /**\n     * 省\n     */\n    @ExcelProperty(value = \"省\")\n    private String base;\n\n    /**\n     * 地区名称\n     */\n    @ExcelProperty(value = \"地区名称\")\n    private String name;\n\n    /**\n     * 城市主活动图\n     */\n    @ExcelProperty(value = \"城市主活动图\")\n    private String imgUrl;\n\n    /**\n     * 创建时间\n     */\n    @ExcelProperty(value = \"创建时间\")\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    @ExcelProperty(value = \"更新时间\")\n    private Date updateTime;\n\n    /**\n     * 子地区列表\n     */\n    private List<PzcRegionVo> children;\n\n    // 构造方法\n    public PzcRegionVo() {\n        this.children = new ArrayList<>();\n    }\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcTagVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.NoArgsConstructor;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\n\nimport java.util.Date;\n\n\n/**\n * 活动标签视图对象 pzc_tag\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@Data\n@ExcelIgnoreUnannotated\n@AllArgsConstructor\n@NoArgsConstructor\npublic class PzcTagVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * ID\n     */\n    @ExcelProperty(value = \"ID\")\n    private Long tagId;\n\n    /**\n     * 名称\n     */\n    @ExcelProperty(value = \"名称\")\n    private String name;\n\n    /**\n     * 图片\n     */\n    @ExcelProperty(value = \"图片\")\n    private String imageUrl;\n\n    /**\n     * 创建时间\n     */\n    @ExcelProperty(value = \"创建时间\")\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    @ExcelProperty(value = \"更新时间\")\n    private Date updateTime;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcUserCollectVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport lombok.Data;\nimport top.flya.system.domain.PzcActivity;\n\n\n/**\n * 用户收藏活动视图对象 pzc_user_collect\n *\n * @author ruoyi\n * @date 2023-07-08\n */\n@Data\n@ExcelIgnoreUnannotated\npublic class PzcUserCollectVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * ID\n     */\n    @ExcelProperty(value = \"ID\")\n    private Long collectId;\n\n    /**\n     * 用户Id\n     */\n    @ExcelProperty(value = \"用户Id\")\n    private Long userId;\n\n    /**\n     * 收藏活动的Id\n     */\n    @ExcelProperty(value = \"收藏活动的Id\")\n    private Long activityId;\n\n\n    private PzcActivity activity;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcUserHistoryVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.util.Date;\n\n\n/**\n * 用户操作历史记录视图对象 pzc_user_history\n *\n * @author ruoyi\n * @date 2023-07-06\n */\n@Data\n@ExcelIgnoreUnannotated\npublic class PzcUserHistoryVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     *\n     */\n    @ExcelProperty(value = \"\")\n    private Long historyId;\n\n    /**\n     * 关联用户Id\n     */\n    @ExcelProperty(value = \"关联用户Id\")\n    private Long userId;\n\n    /**\n     * 关联活动Id\n     */\n    @ExcelProperty(value = \"关联活动Id\")\n    private Long activityId;\n\n    /**\n     * 操作类型\n     */\n    @ExcelProperty(value = \"操作类型\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"user_history_type\")\n    private Long type;\n\n    /**\n     * 信息\n     */\n    @ExcelProperty(value = \"信息\")\n    private String message;\n\n    private BigDecimal money;\n\n    private Date createTime;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcUserPhotoVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\n\nimport java.util.Date;\n\n\n/**\n * 用户资料相册视图对象 pzc_user_photo\n *\n * @author ruoyi\n * @date 2023-07-11\n */\n@Data\n@ExcelIgnoreUnannotated\npublic class PzcUserPhotoVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 照片ID\n     */\n    @ExcelProperty(value = \"照片ID\")\n    private Long photoId;\n\n    /**\n     * 用户ID\n     */\n    @ExcelProperty(value = \"用户ID\")\n    private Long userId;\n\n    /**\n     * 照片\n     */\n    @ExcelProperty(value = \"照片\")\n    private String url;\n\n    /**\n     * 照片说明\n     */\n    @ExcelProperty(value = \"照片说明\")\n    private String message;\n\n    /**\n     * 创建时间\n     */\n    @ExcelProperty(value = \"创建时间\")\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    @ExcelProperty(value = \"更新时间\")\n    private Date updateTime;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcUserTalkVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\n\nimport java.util.Date;\n\n\n/**\n * 用户聊天视图对象 pzc_user_talk\n *\n * @author ruoyi\n * @date 2023-07-16\n */\n@Data\n@ExcelIgnoreUnannotated\npublic class PzcUserTalkVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 聊天ID\n     */\n    @ExcelProperty(value = \"聊天ID\")\n    private Long talkId;\n\n    /**\n     * 发起方\n     */\n    @ExcelProperty(value = \"发起方\")\n    private Long fromUserId;\n\n    /**\n     * 接受方\n     */\n    @ExcelProperty(value = \"接受方\")\n    private Long toUserId;\n\n    /**\n     * 消息\n     */\n    @ExcelProperty(value = \"消息\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"user_talk_msg_type\")\n    private String message;\n\n    /**\n     * 消息状态\n     */\n    @ExcelProperty(value = \"消息状态\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"user_talk_msg_status\")\n    private Long messageStatus;\n\n    /**\n     * 消息类型\n     */\n    @ExcelProperty(value = \"消息类型\")\n    private Long messageType;\n\n    /**\n     *\n     */\n    @ExcelProperty(value = \"\")\n    private Date createTime;\n\n    /**\n     *\n     */\n    @ExcelProperty(value = \"\")\n    private Date updateTime;\n\n\n    private Integer notReadCount; //未读消息数量\n\n    private String username;\n\n    private String avatar;\n\n    private Long groupId;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcUserVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\n\nimport java.math.BigDecimal;\nimport java.util.Date;\n\n\n/**\n * 用户视图对象 pzc_user\n *\n * @author ruoyi\n * @date 2023-07-09\n */\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\n@ExcelIgnoreUnannotated\npublic class PzcUserVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 用户主键\n     */\n    @ExcelProperty(value = \"用户主键\")\n    private Long userId;\n\n    /**\n     * 用户在小程序端的 openId 唯一\n     */\n    @ExcelProperty(value = \"用户在小程序端的 openId 唯一\")\n    private String openid;\n\n    /**\n     * 派币余额\n     */\n    @ExcelProperty(value = \"派币余额\")\n    private BigDecimal money;\n\n    /**\n     * 用户等级\n     */\n    @ExcelProperty(value = \"用户等级\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"user_level\")\n    private Long userLevel;\n\n    /**\n     * 用户累计积分\n     */\n    @ExcelProperty(value = \"用户累计积分\")\n    private Long integration;\n\n    /**\n     * 用户现有积分\n     */\n    @ExcelProperty(value = \"用户现有积分\")\n    private Long integrationNow;\n\n    /**\n     * 真实姓名\n     */\n    @ExcelProperty(value = \"真实姓名\")\n    private String realname;\n\n    /**\n     * 昵称\n     */\n    @ExcelProperty(value = \"昵称\")\n    private String nickname;\n\n    /**\n     * 用户性别 0 男  1 女  2 未知\n     */\n    @ExcelProperty(value = \"用户性别 0 男  1 女  2 未知\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"sys_user_sex\")\n    private Integer sex;\n\n    /**\n     * 手机号\n     */\n    @ExcelProperty(value = \"手机号\")\n    private String phone;\n\n    /**\n     * 头像\n     */\n    @ExcelProperty(value = \"头像\")\n    private String avatar;\n\n    /**\n     * 地址\n     */\n    @ExcelProperty(value = \"地址\")\n    private String address;\n\n    /**\n     * 个人介绍\n     */\n    @ExcelProperty(value = \"个人介绍\")\n    private String intro;\n\n    /**\n     * 年龄\n     */\n    @ExcelProperty(value = \"年龄\")\n    private Long age;\n\n    /**\n     * 星座\n     */\n    @ExcelProperty(value = \"星座\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"constellation\")\n    private String constellation;\n\n    /**\n     * 人格类型\n     */\n    @ExcelProperty(value = \"人格类型\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"mbti\")\n    private String mbti;\n\n    /**\n     * 兴趣爱好\n     */\n    @ExcelProperty(value = \"兴趣爱好\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"user_hobby\")\n    private String hobby;\n\n    /**\n     * 学校\n     */\n    @ExcelProperty(value = \"学校\")\n    private String school;\n\n    /**\n     * 职业\n     */\n    @ExcelProperty(value = \"职业\")\n    private String occupation;\n\n    /**\n     * 创建时间\n     */\n    @ExcelProperty(value = \"创建时间\")\n    private Date createTime;\n\n\n    /**\n     * 更新时间\n     */\n    private Date updateTime;\n\n    /**\n     * 喜欢的音乐风格\n     */\n    @ExcelProperty(value = \"喜欢的音乐风格\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"music_style\")\n    private String musicStyle;\n\n    /**\n     * 状态 是否被封禁\n     */\n    @ExcelProperty(value = \"状态 是否被封禁\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"state\")\n    private Long state;\n\n\n    private Integer  exemptCancel; // 用户免责取消次数\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcViewPagerVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n\n/**\n * 轮播图视图对象 pzc_view_pager\n *\n * @author ruoyi\n * @date 2023-05-23\n */\n@Data\n@ExcelIgnoreUnannotated\n@AllArgsConstructor\n@NoArgsConstructor\npublic class PzcViewPagerVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 轮播图id\n     */\n    @ExcelProperty(value = \"轮播图id\")\n    private Integer viewPagerId;\n\n    /**\n     * 轮播图名称\n     */\n    @ExcelProperty(value = \"轮播图名称\")\n    private String name;\n\n    /**\n     * 轮播图图片Url\n     */\n    @ExcelProperty(value = \"轮播图图片Url\")\n    private String imageUrl;\n\n    /**\n     * 轮播图链接Url\n     */\n    @ExcelProperty(value = \"轮播图链接Url\")\n    private String linkUrl;\n\n    /**\n     * 删除状态，默认为1表示正常状态\n     */\n    @ExcelProperty(value = \"删除状态，默认为1表示正常状态\")\n    private Integer state;\n\n    /**\n     * 关联活动id  0表示不关联\n     */\n    @ExcelProperty(value = \"关联活动id  0表示不关联\")\n    private Long activityId;\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/domain/vo/RefurbishVO.java",
    "content": "package top.flya.system.domain.vo;\n\nimport lombok.Data;\n\n@Data\npublic class RefurbishVO {\n\n\n    //发起方当前位置\n    private String startAddress;\n    //申请方当前位置\n    private String applyAddress;\n\n    private Long applyId;\n\n    private Long distance;\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/entity/Activity.java",
    "content": "package top.flya.system.entity;\n\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport java.util.List;\n\n@Data\n@EqualsAndHashCode(callSuper = true) //活动\n@TableName(\"pzc_activity\")\npublic class Activity extends FLBaseEntity {\n\n    @TableId(value = \"activity_id\", type = com.baomidou.mybatisplus.annotation.IdType.AUTO)\n    private String activityId; //活动id\n\n    private String address;  // 地址\n\n    private Integer regionId;  // 城市ID\n\n    private String title; //活动标题\n\n    private String startTime;  // 开始时间\n\n    private String endDate;  // 结束日期\n\n    private String innerImage;  // 活动详情主图\n\n    private String showTime;  // 展示时间\n\n    private String coverImage;  // 封面图片\n\n    private Integer classify; //属于哪个分类\n\n    private Integer region; // 0 国内 1 国外\n\n    @TableField(exist = false)\n    private List<Intro> introList;  // 简介列表\n\n    @TableField(exist = false) //标签列表\n    private List<Tag> tagList;\n\n    @TableField(exist = false)\n    private List<Artist> artistList;  // 艺人列表\n\n    @TableField(exist = false)\n    private Organizer organizerList;  // 主办方列表\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/entity/ActivityConnArtist.java",
    "content": "package top.flya.system.entity;\n\n\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n@Data\n@EqualsAndHashCode(callSuper = true) //活动关联艺人\n@TableName(\"pzc_activity_conn_artist\")\npublic class ActivityConnArtist extends FLBaseEntity{\n\n    @TableId(value = \"activity_conn_artist_id\", type = com.baomidou.mybatisplus.annotation.IdType.AUTO)\n    private Integer activityConnArtistId;  // ID\n\n    private Integer activityId;  // 活动ID\n\n    private Integer artistId;  // 艺人ID\n\n}\n\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/entity/ActivityConnIntro.java",
    "content": "package top.flya.system.entity;\n\n\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n@Data\n@TableName(\"pzc_activity_conn_intro\")\n@EqualsAndHashCode(callSuper = true) //活动介绍 与 活动 关联表\npublic class ActivityConnIntro extends FLBaseEntity{\n\n    @TableId(value = \"activity_conn_intro_id\", type = com.baomidou.mybatisplus.annotation.IdType.AUTO)\n    private Integer activityConnIntroId;  // ID\n\n    private Integer activityId;  // 活动ID\n\n    private Integer introId;  // 活动介绍ID\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/entity/ActivityConnTag.java",
    "content": "package top.flya.system.entity;\n\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n@Data\n@EqualsAndHashCode(callSuper = true) //活动标签\n@TableName(\"pzc_activity_conn_tag\")\npublic class ActivityConnTag extends FLBaseEntity{\n\n    @TableId(value = \"activity_conn_tag_id\", type = com.baomidou.mybatisplus.annotation.IdType.AUTO)\n    private Integer activityConnTagId;  // ID\n\n    private Integer activityId;  // 活动ID\n\n    private Integer tagId;  // 活动标签ID\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/entity/Artist.java",
    "content": "package top.flya.system.entity;\n\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n@Data\n@TableName(\"pzc_artist\")\n@EqualsAndHashCode(callSuper = true) //艺人\npublic class Artist extends FLBaseEntity{\n\n    @TableId(value = \"artist_id\", type = com.baomidou.mybatisplus.annotation.IdType.AUTO)\n    private Integer artistId;  // ID\n\n    private String name;  // 名称\n\n    private String imageUrl;  // 图片\n\n    private String description;  // 描述\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/entity/Event.java",
    "content": "package top.flya.system.entity;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\npublic class Event {\n    @JsonProperty(\"one_way_exit_permits_flag\")\n    private int oneWayExitPermitsFlag;  // 单向出口许可标志\n\n    @JsonProperty(\"sale_start_time\")\n    private long saleStartTime;  // 销售开始时间\n\n    @JsonProperty(\"music_type\")\n    private int musicType;  // 音乐类型\n\n    @JsonProperty(\"post_flag\")\n    private int postFlag;  // 发布标志\n\n    @JsonProperty(\"logo\")\n    private String logo;  // 标志图片\n\n    @JsonProperty(\"id\")\n    private int id;  // ID\n\n    @JsonProperty(\"ticket_type\")\n    private int ticketType;  // 票务类型\n\n    @JsonProperty(\"end_time\")\n    private String endTime;  // 结束时间\n\n    @JsonProperty(\"email_flag\")\n    private int emailFlag;  // 电子邮件标志\n\n    @JsonProperty(\"cover_image\")\n    private String coverImage;  // 封面图片\n\n    @JsonProperty(\"state\")\n    private int state;  // 状态\n\n    @JsonProperty(\"telephone\")\n    private String telephone;  // 电话号码\n\n    @JsonProperty(\"passport_flag\")\n    private int passportFlag;  // 护照标志\n\n    @JsonProperty(\"address\")\n    private String address;  // 地址\n\n    @JsonProperty(\"miniapp_code_image\")\n    private String miniappCodeImage;  // 小程序码图片\n\n    @JsonProperty(\"one_time_notify_flag\")\n    private int oneTimeNotifyFlag;  // 一次性通知标志\n\n    @JsonProperty(\"recommend_tickets\")\n    private List<Object> recommendTickets;  // 推荐票务\n\n    @JsonProperty(\"star_flag\")\n    private int starFlag;  // 星标标志\n\n    @JsonProperty(\"merchant_id\")\n    private int merchantId;  // 商户ID\n\n    @JsonProperty(\"notice_list\")\n    private List<String> noticeList;  // 通知列表\n\n    @JsonProperty(\"artist_list\")\n    private List<Object> artistList;  // 艺术家列表\n\n    @JsonProperty(\"withdraw_state\")\n    private int withdrawState;  // 提款状态\n\n    @JsonProperty(\"intro_list\")\n    private List<Intro> introList;  // 简介列表\n\n    @JsonProperty(\"type\")\n    private int type;  // 类型\n\n    @JsonProperty(\"music_type_text\")\n    private String musicTypeText;  // 音乐类型文本\n\n    @JsonProperty(\"ad_flag\")\n    private int adFlag;  // 广告标志\n\n    @JsonProperty(\"end_date\")\n    private String endDate;  // 结束日期\n\n    @JsonProperty(\"payment_method\")\n    private int paymentMethod;  // 支付方式\n\n    @JsonProperty(\"id_card_flag\")\n    private int idCardFlag;  // 身份证标志\n\n    @JsonProperty(\"payment_state\")\n    private int paymentState;  // 支付状态\n\n    @JsonProperty(\"offline_payment_flag\")\n    private int offlinePaymentFlag;  // 线下支付标志\n\n    @JsonProperty(\"name\")\n    private String name;  // 名称\n\n    @JsonProperty(\"organizer\")\n    private Organizer organizer;\n    @JsonProperty(\"longitude\")\n    private String longitude;  // 经度\n\n    @JsonProperty(\"price\")\n    private List<Price> price;  // 价格列表\n\n    @JsonProperty(\"start_date\")\n    private String startDate;  // 开始日期\n\n    @JsonProperty(\"sub_state\")\n    private int subState;  // 子状态\n\n    @JsonProperty(\"rec_flag\")\n    private int recFlag;  // 推荐标志\n\n    @JsonProperty(\"start_time\")\n    private String startTime;  // 开始时间\n\n    @JsonProperty(\"bytedance_code_image\")\n    private String bytedanceCodeImage;  // 字节跳动码图片\n\n    @JsonProperty(\"city_id\")\n    private int cityId;  // 城市ID\n\n    @JsonProperty(\"sale_end_time\")\n    private String innerImage;  // 活动详情主图\n\n    @JsonProperty(\"check_code_image\")\n    private String checkCodeImage;  // 检查码图片\n\n    @JsonProperty(\"show_time\")\n    private String showTime;  // 展示时间\n\n    @JsonProperty(\"latitude\")\n    private String latitude;  // 纬度\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/entity/FLBaseEntity.java",
    "content": "package top.flya.system.entity;\n\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n@Data\n@NoArgsConstructor\npublic class FLBaseEntity implements Serializable {\n    private static final long serialVersionUID = 1L;\n    /**\n     * 创建时间\n     */\n    @TableField(fill = FieldFill.INSERT)\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private Date updateTime;\n    /**\n     * 状态\n     */\n    private Integer state ; // 状态 默认正常状态\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/entity/Intro.java",
    "content": "package top.flya.system.entity;\n\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n\n/**\n *  活动介绍  设置 多对多 关联 方便 以后扩展\n */\n@Data\n@EqualsAndHashCode(callSuper = true) //活动介绍\n@TableName(\"pzc_intro\")\npublic class Intro extends FLBaseEntity{\n\n    @TableId(value = \"intro_id\", type = com.baomidou.mybatisplus.annotation.IdType.AUTO)\n    private Integer introId;  // ID\n\n    private String content;  // 内容\n\n    private String imageFullUrl;  // 完整图片URL\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/entity/Organizer.java",
    "content": "package top.flya.system.entity;\n\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport java.util.List;\n\n@Data\n@EqualsAndHashCode(callSuper = true) //活动主办方\n@TableName(\"pzc_organizer\")\npublic class Organizer extends FLBaseEntity{ //活动主办方\n\n    @TableId(value = \"organizer_id\", type = com.baomidou.mybatisplus.annotation.IdType.AUTO)\n    private Integer organizerId;  // ID\n\n    private String phone;  // 电话号码\n\n    private String name;  // 名称\n\n    private String logo;  // 组织者标志图片\n\n    private String content; //主办方介绍\n\n    @TableField(exist = false)\n    private List<OrganizerTicket> organizerTickets;  // 组织者票务列表\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/entity/OrganizerTicket.java",
    "content": "package top.flya.system.entity;\n\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n@Data\n@EqualsAndHashCode(callSuper = true) //主办方票务\n@TableName(\"pzc_organizer_ticket\")\npublic class OrganizerTicket extends FLBaseEntity{ // 主办方票务\n\n    @TableId(value = \"organizer_ticket_id\", type = com.baomidou.mybatisplus.annotation.IdType.AUTO)\n    private int organizerTicketId;  // ID\n\n    private String name;  // 名称\n\n    private String startDate;  // 开始日期\n\n    private String coverImage;  // 封面图片\n\n    private Integer organizerId;  // 关联主办方ID\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/entity/Price.java",
    "content": "package top.flya.system.entity;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\npublic class Price {\n    @JsonProperty(\"stock_num\")\n    private int stockNum;  // 库存数量\n\n    @JsonProperty(\"ticket_id\")\n    private int ticketId;  // 票务ID\n\n    @JsonProperty(\"id\")\n    private int id;  // ID\n\n    @JsonProperty(\"date_name\")\n    private String dateName;  // 日期名称\n\n    @JsonProperty(\"price\")\n    private String price;  // 价格\n\n    @JsonProperty(\"date\")\n    private List<Object> date;  // 日期列表\n\n    @JsonProperty(\"limit\")\n    private int limit;  // 限制\n\n    @JsonProperty(\"type\")\n    private int type;  // 类型\n\n    @JsonProperty(\"num\")\n    private int num;  // 数量\n\n    @JsonProperty(\"name\")\n    private String name;  // 名称\n\n    @JsonProperty(\"sold_out_flag\")\n    private int soldOutFlag;  // 售罄标志\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/entity/Region.java",
    "content": "package top.flya.system.entity;\n\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"pzc_region\") //地区\npublic class Region extends FLBaseEntity {\n\n    @TableId(value = \"regionId\", type = com.baomidou.mybatisplus.annotation.IdType.AUTO)\n    private Integer regionId; //地区id\n\n    private String name; //地区名称\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/entity/Tag.java",
    "content": "package top.flya.system.entity;\n\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n@Data\n@EqualsAndHashCode(callSuper = true) //活动标签\n@TableName(\"pzc_tag\")\npublic class Tag extends FLBaseEntity {\n\n    @TableId(value = \"tag_id\", type = com.baomidou.mybatisplus.annotation.IdType.AUTO)\n    private Integer tagId;  // ID\n\n    private String name;  // 名称\n\n    private String imageUrl;  // 图片\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/entity/ViewPager.java",
    "content": "package top.flya.system.entity;\n\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n@EqualsAndHashCode(callSuper = true)\n@Data\n@TableName(\"pzc_view_pager\") //轮播图\npublic class ViewPager extends FLBaseEntity {\n\n    @TableId(value = \"view_pager_id\", type = com.baomidou.mybatisplus.annotation.IdType.AUTO)\n    private Integer viewPagerId; //轮播图id\n\n    private String name; //轮播图名称\n\n    private String imageUrl; //轮播图图片Url\n\n    private String linkUrl; //轮播图链接Url\n\n    private Integer activityId; //活动id 如果关联活动则不为空\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/handel/MessageEventHandler.java",
    "content": "package top.flya.system.handel;\n\nimport cn.hutool.core.lang.Dict;\nimport cn.hutool.json.JSONUtil;\nimport com.corundumstudio.socketio.AckRequest;\nimport com.corundumstudio.socketio.SocketIOClient;\nimport com.corundumstudio.socketio.annotation.OnConnect;\nimport com.corundumstudio.socketio.annotation.OnDisconnect;\nimport com.corundumstudio.socketio.annotation.OnEvent;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.stereotype.Component;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.system.config.ClientCache;\nimport top.flya.system.config.Event;\nimport top.flya.system.domain.PzcUser;\nimport top.flya.system.domain.bo.PzcUserTalkBo;\nimport top.flya.system.domain.bo.WxzApplyBo;\nimport top.flya.system.mapper.PzcUserMapper;\nimport top.flya.system.service.IPzcUserTalkService;\n\nimport javax.annotation.Resource;\nimport java.util.HashMap;\nimport java.util.UUID;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * <p>\n * 消息事件处理\n * </p>\n *\n * @author yangkai.shen\n * @date Created in 2018-12-18 18:57\n */\n@Component\n@Slf4j\npublic class MessageEventHandler {\n\n    @Autowired\n    private ClientCache cache;\n    @Resource\n    private PzcUserMapper userMapper;\n\n    @Resource\n    private IPzcUserTalkService userTalkService;\n\n\n    @Resource\n    private StringRedisTemplate stringRedisTemplate;\n\n    private final ExecutorService newSingleThreadExecutor = new ThreadPoolExecutor(10, 20, 200L,\n        TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(100));\n\n    /**\n     * 添加connect事件，当客户端发起连接时调用\n     *\n     * @param client 客户端对象\n     */\n    @OnConnect\n    public void onConnect(SocketIOClient client) {\n        if (client != null) {\n            String userId = client.getHandshakeData().getSingleUrlParam(\"userId\"); //我的userId\n            UUID sessionId = client.getSessionId();\n            if (userId != null) {\n                cache.saveClient(userId, sessionId, client);\n                //查询当前用户是否存在 或者被封\n                PzcUser pzcUser = userMapper.selectById(userId);\n                if (pzcUser == null || pzcUser.getState() == 0) {\n                    log.error(\"无效连接 该用户不存在 或者被封禁\");\n                    client.disconnect();\n                }\n                log.info(\"用户上线,【userId】= {},【sessionId】= {}\", userId, sessionId);\n                String result = stringRedisTemplate.opsForValue().get(\"officialMessage:\" + userId);\n                if (result != null) {\n                    WxzApplyBo wxzApplyBo = JsonUtils.parseObject(result, WxzApplyBo.class);\n                    officialMessage(userId, wxzApplyBo);\n                    log.info(\"与对方建立联系时 推送官方消息成功\");\n                }\n\n            } else {\n                log.error(\"无效连接\");\n                client.disconnect();\n            }\n        } else {\n            log.error(\"客户端为空\");\n        }\n    }\n\n    /**\n     * 添加disconnect事件，客户端断开连接时调用，刷新客户端信息\n     *\n     * @param client 客户端对象\n     */\n    @OnDisconnect\n    public void onDisconnect(SocketIOClient client) {\n        if (client != null) {\n            String userId = client.getHandshakeData().getSingleUrlParam(\"userId\");\n            UUID sessionId = client.getSessionId();\n            if (userId != null) {\n                log.info(\"客户端断开连接,【userId】= {},【sessionId】= {}\", userId, sessionId);\n                cache.deleteSessionClient(userId, client.getSessionId());\n            }\n            client.disconnect();\n        } else {\n            log.error(\"客户端为空\");\n        }\n    }\n\n\n    /**\n     * 单聊\n     */\n    public boolean sendToSingle(String userId, PzcUserTalkBo message) {\n        HashMap<UUID, SocketIOClient> userClient = cache.getUserClient(userId);\n        if (userClient != null) {\n            userClient.forEach(((uuid, socketIOClient) ->\n            {\n                socketIOClient.sendEvent(Event.CHAT, message);\n            }));\n            return true;\n        }\n        return false;\n    }\n\n\n    /**\n     * 官方弹窗消息\n     *\n     * @return\n     */\n    public boolean officialMessage(String toUserId, WxzApplyBo wxzApplyBo) {\n        HashMap<UUID, SocketIOClient> userClient = cache.getUserClient(toUserId);\n        if (userClient != null) {\n            userClient.forEach(((uuid, socketIOClient) ->\n            {\n                socketIOClient.sendEvent(Event.OFFICIAL, wxzApplyBo);\n            }));\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 这里用户给对方发起 一条消息  请求无限制到达\n     *\n     * @param client\n     * @param request\n     */\n    @OnEvent(value = Event.OFFICIAL)\n    public void onOfficialEvent(SocketIOClient client, AckRequest request, WxzApplyBo wxzApplyBo) {\n        log.info(\"用户 {} 刚刚给用户 {} 发起了一条无限制确认到达弹窗\", wxzApplyBo.getFromUserId(), wxzApplyBo.getToUserId());\n        //这里分 用户是否在线\n        if (officialMessage(String.valueOf(wxzApplyBo.getToUserId()), wxzApplyBo)) { //测试时修改一下\n            log.info(\"弹窗消息发送成功\");\n            stringRedisTemplate.opsForValue().set(\"officialMessage:\" + wxzApplyBo.getToUserId(), JsonUtils.toJsonString(wxzApplyBo)); //这里也需要存储到redis\n            request.sendAckData(Dict.create().set(\"flag\", true).set(\"message\", \"发送成功\"));\n        } else {\n            log.info(\"弹窗消息发送到Redis 用户不在线\");\n            // 这里需要将消息存储到redis 等待用户上线后推送\n            stringRedisTemplate.opsForValue().set(\"officialMessage:\" + wxzApplyBo.getToUserId(), JsonUtils.toJsonString(wxzApplyBo));\n            request.sendAckData(Dict.create().set(\"flag\", false).set(\"message\", \"用户不在线~ 对方上线后可见 \"));\n        }\n\n    }\n\n\n    @OnEvent(value = Event.CHAT)\n    public void onChatEvent(SocketIOClient client, AckRequest request, PzcUserTalkBo data) {\n        log.info(\"用户 {} 刚刚私信了用户 {}：{}\", data.getFromUserId(), data.getToUserId(), data.getMessage());\n        //加异步\n        newSingleThreadExecutor.execute(() -> {\n            data.setUserId(data.getFromUserId());\n            userTalkService.insertByBo(data);\n            data.setTalkId(null);\n            data.setUserId(data.getToUserId());\n            userTalkService.insertByBo(data);\n        });\n\n        if (sendToSingle(String.valueOf(data.getToUserId()), data)) {\n            request.sendAckData(Dict.create().set(\"flag\", true).set(\"message\", \"发送成功\"));\n        } else {\n            request.sendAckData(Dict.create().set(\"flag\", false).set(\"message\", \"用户不在线~ 对方上线后可见 \"));\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/handel/WxPayInitHandel.java",
    "content": "package top.flya.system.handel;\n\nimport com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;\nimport com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;\nimport com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;\nimport com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;\nimport com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;\nimport com.wechat.pay.contrib.apache.httpclient.util.PemUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.codec.binary.Base64;\nimport org.apache.http.impl.client.CloseableHttpClient;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.math.BigInteger;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.cert.X509Certificate;\n\n/**\n * Created with IntelliJ IDEA.\n *\n * @author: 风离\n * @Date: 2022/06/03/23:16\n * @Description:\n */\n@Aspect\n@Component\n@Slf4j\npublic class WxPayInitHandel {\n\n\n    private String privateKey = \"-----BEGIN PRIVATE KEY-----\\n\" +\n        \"MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCpUtNYFC/SVijj\\n\" +\n        \"mOsW2vVIewgnOfB2YuUW8Q0LUbLtN8jfPJrv2BziCWxVciqNtMKO2ONerRtedzar\\n\" +\n        \"D4poTVgDzICefGL8Mxj0PUIGH9dslc9GSsPjc3iynrW0IMFmrx1ItI//7wbWJTCf\\n\" +\n        \"eNWkQa9WmywFUcMQy/oVsWITq7RaoCdYLVQ6w7CrusQ1DOOJ1OoCVA8HB+zqJGKW\\n\" +\n        \"X6dLTTKF14kYNABPHf7lPd8Zv2VIvxLMxqZuSDKZouQjaciXLI4LGdQje9N9j1JE\\n\" +\n        \"cInKIzQnI3AfKTd0TdWR1F+j8QAQCBrHuOLO6WQEQ+N82BZX7KFn+0SVzzH0QjpL\\n\" +\n        \"K+vjHSBZAgMBAAECggEBAKA6qZZC3BIdyGm/7k9dehlRm6CLGnrdEM7J4r8gW8JR\\n\" +\n        \"NLvTPQbUKljX8/VTqOMZ97Z3lYmlJC4bf9cWSLJ05mIJ5niTWpQvwmB1i4ICJbgy\\n\" +\n        \"d8ebvo0BW2kj+OxwxrNl6L9BZrcZOQ3yeXWfQgRCyCqbgmeyPHYroAdhKV9V78CF\\n\" +\n        \"HSHwZOMVPUo/y44w6ig5Fw6pKdGXSzZwjGUyZNpaj4IY3LYWWffuigHJduMzBut2\\n\" +\n        \"v4JJC2TPC9hB6MiR/sVqpKxtnM466BB8pv7hlZ9TJFzdfD5k1UZuheUgUiunajiC\\n\" +\n        \"FeHOEjtSsTWKsAmTg26+Bw5r1apdL3QsLwqH58IVPaUCgYEA1k//XKqWIWG73OKc\\n\" +\n        \"mOH5gjzb3Aq4c6yw5TGPhEkPn7D+7auKuwzuoUrzASeopzp0te7Ow299fNToFWIs\\n\" +\n        \"wEXi4g0gezO7UwpJ6wWNr0iL/wPgYx3jglrPQ+Pvmfoaj4N0mK/pfmXHF8RdIs/M\\n\" +\n        \"p2kn2oQLrJgdeGb4FZM0byX7UcsCgYEAykKJotProJqDogbqsNEIJpBfFlBvxFgb\\n\" +\n        \"IPAp33tBqBh9DiKIOOtISTjPZQIAkzkC8SMRK+HZbF1p2/20bjvTOsi8PKxm/sqS\\n\" +\n        \"v/oPLDfni8pkmhfNU6rr0iNMagpoT9kks4qk9h2dRij1fKJ6pa4TBahJx5wQ3Yu0\\n\" +\n        \"vMEmy8JdwesCgYEAoSGAg7GWMv8Cei6/QosUR4FuZGCDEiWS0p+Sogk0gAJZiWRi\\n\" +\n        \"aARvHkH1traUrTbcLTWhq3sVxFdnLzyjHOTukrr/4uGgQ+0GanfAcTuAVnoZqSv9\\n\" +\n        \"tDKGhyrHKOPMOH7DmVEZovju2cW/qL7Hxk7fsgF5rYipD6+Lct08nRzXekUCgYEA\\n\" +\n        \"s2muWYeOnhox5coo6MujZUHvdwXG/u4AsokXO6xEI24FkEJFf+gFaR5BqiHKjM2n\\n\" +\n        \"tGsc0kY27Y83VfOI17etuZlSkKeFfUIIRs70Io88j53q+11dv3gAU5kIMZAl056U\\n\" +\n        \"lcbIaaD/X7r5d6NRFCKDsSMEv1HLDBrfKghT967kKB0CgYBnynlp2NRtBgtLNURS\\n\" +\n        \"c2xQ7NrlhSiWyLKii/h55PRfxps07w/eqsNALgAqBVDvZt8TcD1IW8/FYv+ZcvMm\\n\" +\n        \"GUA4MNQonauQbLDbkwEHc8MWIpPMJnQYf9YAFlf73qknlikepc+s0mkNkYaZYQYZ\\n\" +\n        \"Q3uWw4iuk4iyxAzWDWrIRPoiRA==\\n\" +\n        \"-----END PRIVATE KEY-----\\n\";\n\n\n\n    @Value(\"${wx.merchantId}\")\n    private String merchantId;\n\n    @Value(\"${wx.merchantSerialNumber}\")\n    private String merchantSerialNumber;\n\n    @Value(\"${wx.api3}\")\n    private String api3;\n\n    public CloseableHttpClient setup() throws IOException {\n        log.info(\"createOrder()方法执行前\");\n        CloseableHttpClient httpClient=null;\n\n        // 加载商户私钥（privateKey：私钥字符串）\n        PrivateKey merchantPrivateKey = PemUtil\n                .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes(\"utf-8\")));\n\n        log.info(\"\"+merchantId+\"--\"+merchantSerialNumber+\"--\"+merchantPrivateKey+\"---\"+api3);\n\n        // 加载平台证书（mchId：商户号,mchSerialNo：商户证书序列号,apiV3Key：V3密钥）\n        AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(\n                new WechatPay2Credentials(merchantId, new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),api3.getBytes(\"utf-8\"));\n\n\n        //获取微信支付平台证书证书序列号\n        BigInteger serialNumber = verifier.getValidCertificate().getSerialNumber();\n        //转成16进制\n        String serialnumber =  serialNumber.toString(16);\n        //获取微信支付平台证书\n        X509Certificate validCertificate = verifier.getValidCertificate();\n        //获取微信支付平台证书公钥\n        PublicKey publicKey = validCertificate.getPublicKey();\n        //转成字符串\n        String prikeyStr = Base64.encodeBase64String(publicKey.getEncoded());//Base64:package\n        System.out.println(\"prikeyStr:\"+prikeyStr);\n\n\n\n\n        // 初始化httpClient\n        httpClient = WechatPayHttpClientBuilder.create()\n                .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)\n                .withValidator(new WechatPay2Validator(verifier)).build();\n\n        return httpClient;\n    }\n\n    public void after(CloseableHttpClient httpClient) throws IOException {\n        log.info(\"createOrder()方法执行后\");\n        httpClient.close();\n    }\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcActivityConnArtistMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcActivityConnArtist;\nimport top.flya.system.domain.vo.PzcActivityConnArtistVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 活动关联艺人Mapper接口\n *\n * @author ruoyi\n * @date 2023-06-02\n */\npublic interface PzcActivityConnArtistMapper extends BaseMapperPlus<PzcActivityConnArtistMapper, PzcActivityConnArtist, PzcActivityConnArtistVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcActivityConnIntroMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcActivityConnIntro;\nimport top.flya.system.domain.vo.PzcActivityConnIntroVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 活动介绍与活动关联Mapper接口\n *\n * @author ruoyi\n * @date 2023-06-02\n */\npublic interface PzcActivityConnIntroMapper extends BaseMapperPlus<PzcActivityConnIntroMapper, PzcActivityConnIntro, PzcActivityConnIntroVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcActivityConnTagMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcActivityConnTag;\nimport top.flya.system.domain.vo.PzcActivityConnTagVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 活动标签与活动关联Mapper接口\n *\n * @author ruoyi\n * @date 2023-06-03\n */\npublic interface PzcActivityConnTagMapper extends BaseMapperPlus<PzcActivityConnTagMapper, PzcActivityConnTag, PzcActivityConnTagVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcActivityGroupApplyMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcActivityGroupApply;\nimport top.flya.system.domain.vo.PzcActivityGroupApplyVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 活动组队申请列Mapper接口\n *\n * @author ruoyi\n * @date 2023-07-10\n */\npublic interface PzcActivityGroupApplyMapper extends BaseMapperPlus<PzcActivityGroupApplyMapper, PzcActivityGroupApply, PzcActivityGroupApplyVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcActivityGroupMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport top.flya.system.domain.PzcActivityGroup;\nimport top.flya.system.domain.bo.PzcActivityGroupBo;\nimport top.flya.system.domain.vo.PzcActivityGroupVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 活动组队Mapper接口\n *\n * @author ruoyi\n * @date 2023-07-10\n */\npublic interface PzcActivityGroupMapper extends BaseMapperPlus<PzcActivityGroupMapper, PzcActivityGroup, PzcActivityGroupVo> {\n\n    Page<PzcActivityGroupVo> selectDetailsList(Page<Object> build,@Param(\"bo\") PzcActivityGroupBo bo);\n\n    PzcActivityGroupVo selectVoByIdDIY(@Param(\"groupId\") Long groupId);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcActivityMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport org.apache.ibatis.annotations.Param;\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.system.domain.PzcActivity;\nimport top.flya.system.domain.vo.PzcActivityVo;\n\nimport java.util.List;\n\n/**\n * 活动Mapper接口\n *\n * @author ruoyi\n * @date 2023-06-02\n */\npublic interface PzcActivityMapper extends BaseMapperPlus<PzcActivityMapper, PzcActivity, PzcActivityVo> {\n\n    List<PzcActivity> selectActivityByActivityIds(@Param(\"activityIds\") List<String> collect, @Param(\"classify\") Integer type);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcArtistMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcArtist;\nimport top.flya.system.domain.vo.PzcArtistVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 艺人Mapper接口\n *\n * @author flya\n * @date 2023-06-01\n */\npublic interface PzcArtistMapper extends BaseMapperPlus<PzcArtistMapper, PzcArtist, PzcArtistVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcIntroMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcIntro;\nimport top.flya.system.domain.vo.PzcIntroVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 活动介绍Mapper接口\n *\n * @author ruoyi\n * @date 2023-08-04\n */\npublic interface PzcIntroMapper extends BaseMapperPlus<PzcIntroMapper, PzcIntro, PzcIntroVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcOfficialMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcOfficial;\nimport top.flya.system.domain.vo.PzcOfficialVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 官方消息Mapper接口\n *\n * @author ruoyi\n * @date 2023-07-27\n */\npublic interface PzcOfficialMapper extends BaseMapperPlus<PzcOfficialMapper, PzcOfficial, PzcOfficialVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcOrderMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcOrder;\nimport top.flya.system.domain.vo.PzcOrderVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 订单Mapper接口\n *\n * @author ruoyi\n * @date 2023-07-09\n */\npublic interface PzcOrderMapper extends BaseMapperPlus<PzcOrderMapper, PzcOrder, PzcOrderVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcOrganizerMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcOrganizer;\nimport top.flya.system.domain.vo.PzcOrganizerVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 活动主办方Mapper接口\n *\n * @author ruoyi\n * @date 2023-06-02\n */\npublic interface PzcOrganizerMapper extends BaseMapperPlus<PzcOrganizerMapper, PzcOrganizer, PzcOrganizerVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcOrganizerTicketMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcOrganizerTicket;\nimport top.flya.system.domain.vo.PzcOrganizerTicketVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 主办方票务Mapper接口\n *\n * @author ruoyi\n * @date 2023-07-22\n */\npublic interface PzcOrganizerTicketMapper extends BaseMapperPlus<PzcOrganizerTicketMapper, PzcOrganizerTicket, PzcOrganizerTicketVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcRegionMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcRegion;\nimport top.flya.system.domain.vo.PzcRegionVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 地区Mapper接口\n *\n * @author ruoyi\n * @date 2023-07-22\n */\npublic interface PzcRegionMapper extends BaseMapperPlus<PzcRegionMapper, PzcRegion, PzcRegionVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcTagMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcTag;\nimport top.flya.system.domain.vo.PzcTagVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 活动标签Mapper接口\n *\n * @author ruoyi\n * @date 2023-06-02\n */\npublic interface PzcTagMapper extends BaseMapperPlus<PzcTagMapper, PzcTag, PzcTagVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcUserCollectMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcUserCollect;\nimport top.flya.system.domain.vo.PzcUserCollectVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 用户收藏活动Mapper接口\n *\n * @author ruoyi\n * @date 2023-07-08\n */\npublic interface PzcUserCollectMapper extends BaseMapperPlus<PzcUserCollectMapper, PzcUserCollect, PzcUserCollectVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcUserHistoryMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcUserHistory;\nimport top.flya.system.domain.vo.PzcUserHistoryVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 用户操作历史记录Mapper接口\n *\n * @author ruoyi\n * @date 2023-07-06\n */\npublic interface PzcUserHistoryMapper extends BaseMapperPlus<PzcUserHistoryMapper, PzcUserHistory, PzcUserHistoryVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcUserMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport org.apache.ibatis.annotations.Param;\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.system.domain.PzcUser;\nimport top.flya.system.domain.bo.UpdateMoneyBo;\nimport top.flya.system.domain.vo.PzcUserVo;\n\n/**\n * 用户Mapper接口\n *\n * @author ruoyi\n * @date 2023-07-09\n */\npublic interface PzcUserMapper extends BaseMapperPlus<PzcUserMapper, PzcUser, PzcUserVo> {\n\n    int updateMoney(@Param(\"bo\") UpdateMoneyBo bo);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcUserPhotoMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcUserPhoto;\nimport top.flya.system.domain.vo.PzcUserPhotoVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 用户资料相册Mapper接口\n *\n * @author ruoyi\n * @date 2023-07-11\n */\npublic interface PzcUserPhotoMapper extends BaseMapperPlus<PzcUserPhotoMapper, PzcUserPhoto, PzcUserPhotoVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcUserTalkMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.apache.ibatis.annotations.Param;\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.system.domain.PzcUserTalk;\nimport top.flya.system.domain.bo.PzcUserTalkBo;\nimport top.flya.system.domain.vo.PzcUserTalkVo;\n\nimport java.util.List;\n\n/**\n * 用户聊天Mapper接口\n *\n * @author ruoyi\n * @date 2023-07-16\n */\npublic interface PzcUserTalkMapper extends BaseMapperPlus<PzcUserTalkMapper, PzcUserTalk, PzcUserTalkVo> {\n\n    PzcUserTalkVo selectVoPageV1 (@Param(\"otherUser\")Long otherUser, @Param(\"userId\") Long myUserId);\n\n    Integer selectNotReadCount(@Param(\"fromUserId\") Long userId, @Param(\"toUserId\") Long toUserId,@Param(\"userId\")Long myUserId);\n\n    Page<PzcUserTalkVo> selectVoPageV2(Page<Object> build, @Param(\"bo\") PzcUserTalkBo bo);\n\n    List<Long> selectMyTalkUserIds(@Param(\"my\") Long userId);\n\n    List<Long> selectMyTalkUserIdsV2(@Param(\"my\") Long userId);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcViewPagerMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.system.domain.PzcViewPager;\nimport top.flya.system.domain.vo.PzcViewPagerVo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 轮播图Mapper接口\n *\n * @author ruoyi\n * @date 2023-05-23\n */\npublic interface PzcViewPagerMapper extends BaseMapperPlus<PzcViewPagerMapper, PzcViewPager, PzcViewPagerVo> {\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/mapper/RegionTreeMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport org.springframework.stereotype.Component;\nimport top.flya.system.domain.vo.PzcRegionVo;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n@Component\npublic class RegionTreeMapper {\n    public  Map<String, List<PzcRegionVo>> buildTree(List<PzcRegionVo> pzcRegionVos) {\n        List<PzcRegionVo> tree = new ArrayList<>();\n        Map<String, PzcRegionVo> nodeMap = new HashMap<>();\n\n        for (PzcRegionVo node : pzcRegionVos) {\n            nodeMap.put(node.getBase(), node);\n        }\n\n        for (PzcRegionVo node : pzcRegionVos) {\n            String parentBase = getParentBase(node);\n            if (parentBase != null) {\n                PzcRegionVo parentNode = nodeMap.get(parentBase);\n                if (parentNode != null) {\n                    parentNode.getChildren().add(node);\n                }\n            } else {\n                tree.add(node);\n            }\n        }\n\n        Map<String, List<PzcRegionVo>> resultMap = new HashMap<>();\n\n        for (PzcRegionVo node : tree) {\n            String base = node.getBase();\n            if (resultMap.containsKey(base)) {\n                resultMap.get(base).add(node);\n            } else {\n                List<PzcRegionVo> nodeList = new ArrayList<>();\n                nodeList.add(node);\n                resultMap.put(base, nodeList);\n            }\n        }\n        resultMap.put(\"全部\",pzcRegionVos);\n\n        return resultMap;\n    }\n\n    private  String getParentBase(PzcRegionVo node) {\n        String base = node.getBase();\n        if (base != null && base.contains(\"-\")) {\n            return base.substring(0, base.lastIndexOf(\"-\"));\n        }\n        return null;\n    }\n\n//    public static void main(String[] args) {\n//        List<PzcRegionVo> pzcRegionVos = new ArrayList<>();\n//// 添加示例数据\n//        pzcRegionVos.add(new PzcRegionVo(1L, \"江苏省\", \"南京市\", \"\", null, null, null));\n//        pzcRegionVos.add(new PzcRegionVo(2L, \"江苏省\", \"苏州市\", \"\", null, null, null));\n//        pzcRegionVos.add(new PzcRegionVo(3L, \"上海市\", \"上海市\", \"\", null, null, null));\n//\n//\n//        System.out.println(JSONUtil.toJsonPrettyStr( buildTree(pzcRegionVos)));\n//    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcActivityConnArtistService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.system.domain.PzcActivityConnArtist;\nimport top.flya.system.domain.vo.PzcActivityConnArtistVo;\nimport top.flya.system.domain.bo.PzcActivityConnArtistBo;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 活动关联艺人Service接口\n *\n * @author ruoyi\n * @date 2023-06-02\n */\npublic interface IPzcActivityConnArtistService {\n\n    /**\n     * 查询活动关联艺人\n     */\n    PzcActivityConnArtistVo queryById(Integer activityConnArtistId);\n\n    /**\n     * 查询活动关联艺人列表\n     */\n    TableDataInfo<PzcActivityConnArtistVo> queryPageList(PzcActivityConnArtistBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询活动关联艺人列表\n     */\n    List<PzcActivityConnArtistVo> queryList(PzcActivityConnArtistBo bo);\n\n    /**\n     * 新增活动关联艺人\n     */\n    Boolean insertByBo(PzcActivityConnArtistBo bo);\n\n    /**\n     * 修改活动关联艺人\n     */\n    Boolean updateByBo(PzcActivityConnArtistBo bo);\n\n    /**\n     * 校验并批量删除活动关联艺人信息\n     */\n    Boolean deleteWithValidByIds(Collection<Integer> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcActivityConnIntroService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.system.domain.PzcActivityConnIntro;\nimport top.flya.system.domain.vo.PzcActivityConnIntroVo;\nimport top.flya.system.domain.bo.PzcActivityConnIntroBo;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 活动介绍与活动关联Service接口\n *\n * @author ruoyi\n * @date 2023-06-02\n */\npublic interface IPzcActivityConnIntroService {\n\n    /**\n     * 查询活动介绍与活动关联\n     */\n    PzcActivityConnIntroVo queryById(Integer activityConnIntroId);\n\n    /**\n     * 查询活动介绍与活动关联列表\n     */\n    TableDataInfo<PzcActivityConnIntroVo> queryPageList(PzcActivityConnIntroBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询活动介绍与活动关联列表\n     */\n    List<PzcActivityConnIntroVo> queryList(PzcActivityConnIntroBo bo);\n\n    /**\n     * 新增活动介绍与活动关联\n     */\n    Boolean insertByBo(PzcActivityConnIntroBo bo);\n\n    /**\n     * 修改活动介绍与活动关联\n     */\n    Boolean updateByBo(PzcActivityConnIntroBo bo);\n\n    /**\n     * 校验并批量删除活动介绍与活动关联信息\n     */\n    Boolean deleteWithValidByIds(Collection<Integer> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcActivityConnTagService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.system.domain.PzcActivityConnTag;\nimport top.flya.system.domain.vo.PzcActivityConnTagVo;\nimport top.flya.system.domain.bo.PzcActivityConnTagBo;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 活动标签与活动关联Service接口\n *\n * @author ruoyi\n * @date 2023-06-03\n */\npublic interface IPzcActivityConnTagService {\n\n    /**\n     * 查询活动标签与活动关联\n     */\n    PzcActivityConnTagVo queryById(Integer activityConnTagId);\n\n    /**\n     * 查询活动标签与活动关联列表\n     */\n    TableDataInfo<PzcActivityConnTagVo> queryPageList(PzcActivityConnTagBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询活动标签与活动关联列表\n     */\n    List<PzcActivityConnTagVo> queryList(PzcActivityConnTagBo bo);\n\n    /**\n     * 新增活动标签与活动关联\n     */\n    Boolean insertByBo(PzcActivityConnTagBo bo);\n\n    /**\n     * 修改活动标签与活动关联\n     */\n    Boolean updateByBo(PzcActivityConnTagBo bo);\n\n    /**\n     * 校验并批量删除活动标签与活动关联信息\n     */\n    Boolean deleteWithValidByIds(Collection<Integer> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcActivityGroupApplyService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.domain.bo.PzcActivityGroupApplyBo;\nimport top.flya.system.domain.vo.PzcActivityGroupApplyVo;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 活动组队申请列Service接口\n *\n * @author ruoyi\n * @date 2023-07-10\n */\npublic interface IPzcActivityGroupApplyService {\n\n    /**\n     * 查询活动组队申请列\n     */\n    PzcActivityGroupApplyVo queryById(Long applyId);\n\n    /**\n     * 查询活动组队申请列列表\n     */\n    TableDataInfo<PzcActivityGroupApplyVo> queryPageList(PzcActivityGroupApplyBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询活动组队申请列列表\n     */\n    List<PzcActivityGroupApplyVo> queryList(PzcActivityGroupApplyBo bo);\n\n    /**\n     * 新增活动组队申请列\n     */\n    Boolean insertByBo(PzcActivityGroupApplyBo bo);\n\n    /**\n     * 修改活动组队申请列\n     */\n    Boolean updateByBo(PzcActivityGroupApplyBo bo);\n\n    /**\n     * 校验并批量删除活动组队申请列信息\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n\n    boolean queryByUserIdAndActivityId(Long userId, Long activityId);\n\n    Integer updateStatus(Long applyId, int i);\n\n    List<PzcActivityGroupApplyVo> queryListByGroupIds(List<Long> groupIds);\n\n    PzcActivityGroupApplyVo queryByUserIdAndGroupId(Long userId, Long groupId);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcActivityGroupService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.system.domain.PzcActivityGroup;\nimport top.flya.system.domain.vo.PzcActivityGroupVo;\nimport top.flya.system.domain.bo.PzcActivityGroupBo;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 活动组队Service接口\n *\n * @author ruoyi\n * @date 2023-07-10\n */\npublic interface IPzcActivityGroupService {\n\n    /**\n     * 查询活动组队\n     */\n    PzcActivityGroupVo queryById(Long groupId);\n\n    /**\n     * 查询活动组队列表\n     */\n    TableDataInfo<PzcActivityGroupVo> queryPageList(PzcActivityGroupBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询活动组队列表\n     */\n    List<PzcActivityGroupVo> queryList(PzcActivityGroupBo bo);\n\n    /**\n     * 新增活动组队\n     */\n    Boolean insertByBo(PzcActivityGroupBo bo);\n\n    /**\n     * 修改活动组队\n     */\n    Boolean updateByBo(PzcActivityGroupBo bo);\n\n    /**\n     * 校验并批量删除活动组队信息\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n\n    boolean checkActivity(Long activityId);\n\n    boolean checkGroup(Long userId, Long activityId);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcActivityService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.system.domain.PzcActivity;\nimport top.flya.system.domain.vo.PzcActivityVo;\nimport top.flya.system.domain.bo.PzcActivityBo;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 活动Service接口\n *\n * @author ruoyi\n * @date 2023-06-02\n */\npublic interface IPzcActivityService {\n\n    /**\n     * 查询活动\n     */\n    PzcActivityVo queryById(Integer activityId);\n\n    /**\n     * 查询活动列表\n     */\n    TableDataInfo<PzcActivityVo> queryPageList(PzcActivityBo bo, PageQuery pageQuery);\n\n\n    /**\n     * 查询活动列表 微信\n     *\n     */\n  TableDataInfo<PzcActivityVo> queryPageListWx(PzcActivityBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询活动列表\n     */\n    List<PzcActivityVo> queryList(PzcActivityBo bo);\n\n    /**\n     * 新增活动\n     */\n    Boolean insertByBo(PzcActivityBo bo);\n\n    /**\n     * 修改活动\n     */\n    Boolean updateByBo(PzcActivityBo bo);\n\n    /**\n     * 校验并批量删除活动信息\n     */\n    Boolean deleteWithValidByIds(Collection<Integer> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcArtistService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.system.domain.PzcArtist;\nimport top.flya.system.domain.vo.PzcArtistVo;\nimport top.flya.system.domain.bo.PzcArtistBo;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 艺人Service接口\n *\n * @author flya\n * @date 2023-06-01\n */\npublic interface IPzcArtistService {\n\n    /**\n     * 查询艺人\n     */\n    PzcArtistVo queryById(Long artistId);\n\n    /**\n     * 查询艺人列表\n     */\n    TableDataInfo<PzcArtistVo> queryPageList(PzcArtistBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询艺人列表\n     */\n    List<PzcArtistVo> queryList(PzcArtistBo bo);\n\n    /**\n     * 新增艺人\n     */\n    Boolean insertByBo(PzcArtistBo bo);\n\n    /**\n     * 修改艺人\n     */\n    Boolean updateByBo(PzcArtistBo bo);\n\n    /**\n     * 校验并批量删除艺人信息\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcIntroService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.domain.bo.PzcIntroBo;\nimport top.flya.system.domain.vo.PzcIntroVo;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 活动介绍Service接口\n *\n * @author ruoyi\n * @date 2023-08-04\n */\npublic interface IPzcIntroService {\n\n    /**\n     * 查询活动介绍\n     */\n    PzcIntroVo queryById(Long introId);\n\n    /**\n     * 查询活动介绍列表\n     */\n    TableDataInfo<PzcIntroVo> queryPageList(PzcIntroBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询活动介绍列表\n     */\n    List<PzcIntroVo> queryList(PzcIntroBo bo);\n\n    /**\n     * 新增活动介绍\n     */\n    Boolean insertByBo(PzcIntroBo bo);\n\n    /**\n     * 修改活动介绍\n     */\n    Boolean updateByBo(PzcIntroBo bo);\n\n    /**\n     * 校验并批量删除活动介绍信息\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcOfficialService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.domain.bo.PzcOfficialBo;\nimport top.flya.system.domain.vo.PzcOfficialVo;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 官方消息Service接口\n *\n * @author ruoyi\n * @date 2023-07-27\n */\npublic interface IPzcOfficialService {\n\n    /**\n     * 查询官方消息\n     */\n    PzcOfficialVo queryById(Long officialId);\n\n    /**\n     * 查询官方消息列表\n     */\n    TableDataInfo<PzcOfficialVo> queryPageList(PzcOfficialBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询官方消息列表\n     */\n    List<PzcOfficialVo> queryList(PzcOfficialBo bo);\n\n    /**\n     * 新增官方消息\n     */\n    Boolean insertByBo(PzcOfficialBo bo);\n\n    /**\n     * 修改官方消息\n     */\n    Boolean updateByBo(PzcOfficialBo bo);\n\n    /**\n     * 校验并批量删除官方消息信息\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n\n    Integer read(Integer officialId);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcOrderService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.system.domain.PzcOrder;\nimport top.flya.system.domain.vo.PzcOrderVo;\nimport top.flya.system.domain.bo.PzcOrderBo;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 订单Service接口\n *\n * @author ruoyi\n * @date 2023-07-09\n */\npublic interface IPzcOrderService {\n\n    /**\n     * 查询订单\n     */\n    PzcOrderVo queryById(Long orderId);\n\n    /**\n     * 查询订单列表\n     */\n    TableDataInfo<PzcOrderVo> queryPageList(PzcOrderBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询订单列表\n     */\n    List<PzcOrderVo> queryList(PzcOrderBo bo);\n\n    /**\n     * 新增订单\n     */\n    Boolean insertByBo(PzcOrderBo bo);\n\n    /**\n     * 修改订单\n     */\n    Boolean updateByBo(PzcOrderBo bo);\n\n    /**\n     * 校验并批量删除订单信息\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcOrganizerService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.system.domain.PzcOrganizer;\nimport top.flya.system.domain.vo.PzcOrganizerVo;\nimport top.flya.system.domain.bo.PzcOrganizerBo;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 活动主办方Service接口\n *\n * @author ruoyi\n * @date 2023-06-02\n */\npublic interface IPzcOrganizerService {\n\n    /**\n     * 查询活动主办方\n     */\n    PzcOrganizerVo queryById(Long organizerId);\n\n    /**\n     * 查询活动主办方列表\n     */\n    TableDataInfo<PzcOrganizerVo> queryPageList(PzcOrganizerBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询活动主办方列表\n     */\n    List<PzcOrganizerVo> queryList(PzcOrganizerBo bo);\n\n    /**\n     * 新增活动主办方\n     */\n    Boolean insertByBo(PzcOrganizerBo bo);\n\n    /**\n     * 修改活动主办方\n     */\n    Boolean updateByBo(PzcOrganizerBo bo);\n\n    /**\n     * 校验并批量删除活动主办方信息\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcOrganizerTicketService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.system.domain.PzcOrganizerTicket;\nimport top.flya.system.domain.vo.PzcOrganizerTicketVo;\nimport top.flya.system.domain.bo.PzcOrganizerTicketBo;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 主办方票务Service接口\n *\n * @author ruoyi\n * @date 2023-07-22\n */\npublic interface IPzcOrganizerTicketService {\n\n    /**\n     * 查询主办方票务\n     */\n    PzcOrganizerTicketVo queryById(Long organizerTicketId);\n\n    /**\n     * 查询主办方票务列表\n     */\n    TableDataInfo<PzcOrganizerTicketVo> queryPageList(PzcOrganizerTicketBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询主办方票务列表\n     */\n    List<PzcOrganizerTicketVo> queryList(PzcOrganizerTicketBo bo);\n\n    /**\n     * 新增主办方票务\n     */\n    Boolean insertByBo(PzcOrganizerTicketBo bo);\n\n    /**\n     * 修改主办方票务\n     */\n    Boolean updateByBo(PzcOrganizerTicketBo bo);\n\n    /**\n     * 校验并批量删除主办方票务信息\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcRegionService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.domain.bo.PzcRegionBo;\nimport top.flya.system.domain.vo.PzcRegionVo;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 地区Service接口\n *\n * @author ruoyi\n * @date 2023-07-22\n */\npublic interface IPzcRegionService {\n\n    /**\n     * 查询地区\n     */\n    PzcRegionVo queryById(Long regionId);\n\n    /**\n     * 查询地区列表\n     */\n    TableDataInfo<PzcRegionVo> queryPageList(PzcRegionBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询地区列表\n     */\n    List<PzcRegionVo> queryList(PzcRegionBo bo);\n\n    /**\n     * 新增地区\n     */\n    Boolean insertByBo(PzcRegionBo bo);\n\n    /**\n     * 修改地区\n     */\n    Boolean updateByBo(PzcRegionBo bo);\n\n    /**\n     * 校验并批量删除地区信息\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcTagService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.system.domain.PzcTag;\nimport top.flya.system.domain.vo.PzcTagVo;\nimport top.flya.system.domain.bo.PzcTagBo;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 活动标签Service接口\n *\n * @author ruoyi\n * @date 2023-06-02\n */\npublic interface IPzcTagService {\n\n    /**\n     * 查询活动标签\n     */\n    PzcTagVo queryById(Long tagId);\n\n    /**\n     * 查询活动标签列表\n     */\n    TableDataInfo<PzcTagVo> queryPageList(PzcTagBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询活动标签列表\n     */\n    List<PzcTagVo> queryList(PzcTagBo bo);\n\n    /**\n     * 新增活动标签\n     */\n    Boolean insertByBo(PzcTagBo bo);\n\n    /**\n     * 修改活动标签\n     */\n    Boolean updateByBo(PzcTagBo bo);\n\n    /**\n     * 校验并批量删除活动标签信息\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcUserCollectService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.system.domain.PzcUserCollect;\nimport top.flya.system.domain.vo.PzcUserCollectVo;\nimport top.flya.system.domain.bo.PzcUserCollectBo;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 用户收藏活动Service接口\n *\n * @author ruoyi\n * @date 2023-07-08\n */\npublic interface IPzcUserCollectService {\n\n    /**\n     * 查询用户收藏活动\n     */\n    PzcUserCollectVo queryById(Long collectId);\n\n    /**\n     * 查询用户收藏活动列表\n     */\n    TableDataInfo<PzcUserCollectVo> queryPageList(PzcUserCollectBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询用户收藏活动列表\n     */\n    List<PzcUserCollectVo> queryList(PzcUserCollectBo bo);\n\n    /**\n     * 新增用户收藏活动\n     */\n    Boolean insertByBo(PzcUserCollectBo bo);\n\n    /**\n     * 修改用户收藏活动\n     */\n    Boolean updateByBo(PzcUserCollectBo bo);\n\n    /**\n     * 校验并批量删除用户收藏活动信息\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcUserHistoryService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.system.domain.PzcUserHistory;\nimport top.flya.system.domain.vo.PzcUserHistoryVo;\nimport top.flya.system.domain.bo.PzcUserHistoryBo;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 用户操作历史记录Service接口\n *\n * @author ruoyi\n * @date 2023-07-06\n */\npublic interface IPzcUserHistoryService {\n\n    /**\n     * 查询用户操作历史记录\n     */\n    PzcUserHistoryVo queryById(Long historyId);\n\n    /**\n     * 查询用户操作历史记录列表\n     */\n    TableDataInfo<PzcUserHistoryVo> queryPageList(PzcUserHistoryBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询用户操作历史记录列表\n     */\n    List<PzcUserHistoryVo> queryList(PzcUserHistoryBo bo);\n\n    /**\n     * 新增用户操作历史记录\n     */\n    Boolean insertByBo(PzcUserHistoryBo bo);\n\n    /**\n     * 修改用户操作历史记录\n     */\n    Boolean updateByBo(PzcUserHistoryBo bo);\n\n    /**\n     * 校验并批量删除用户操作历史记录信息\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcUserPhotoService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.system.domain.PzcUserPhoto;\nimport top.flya.system.domain.vo.PzcUserPhotoVo;\nimport top.flya.system.domain.bo.PzcUserPhotoBo;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 用户资料相册Service接口\n *\n * @author ruoyi\n * @date 2023-07-11\n */\npublic interface IPzcUserPhotoService {\n\n    /**\n     * 查询用户资料相册\n     */\n    PzcUserPhotoVo queryById(Long photoId);\n\n    /**\n     * 查询用户资料相册列表\n     */\n    TableDataInfo<PzcUserPhotoVo> queryPageList(PzcUserPhotoBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询用户资料相册列表\n     */\n    List<PzcUserPhotoVo> queryList(PzcUserPhotoBo bo);\n\n    /**\n     * 新增用户资料相册\n     */\n    Boolean insertByBo(PzcUserPhotoBo bo);\n\n    /**\n     * 修改用户资料相册\n     */\n    Boolean updateByBo(PzcUserPhotoBo bo);\n\n    /**\n     * 校验并批量删除用户资料相册信息\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcUserService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.domain.bo.PzcUserBo;\nimport top.flya.system.domain.bo.UpdateMoneyBo;\nimport top.flya.system.domain.vo.PzcUserVo;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 用户Service接口\n *\n * @author ruoyi\n * @date 2023-07-09\n */\npublic interface IPzcUserService {\n\n    /**\n     * 查询用户\n     */\n    PzcUserVo queryById(Long userId);\n\n    /**\n     * 查询用户列表\n     */\n    TableDataInfo<PzcUserVo> queryPageList(PzcUserBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询用户列表\n     */\n    List<PzcUserVo> queryList(PzcUserBo bo);\n\n    /**\n     * 新增用户\n     */\n    Boolean insertByBo(PzcUserBo bo);\n\n    /**\n     * 修改用户\n     */\n    Boolean updateByBo(PzcUserBo bo);\n\n    /**\n     * 校验并批量删除用户信息\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n\n    int updateMoney(UpdateMoneyBo bo);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcUserTalkService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.system.domain.PzcUserTalk;\nimport top.flya.system.domain.vo.PzcUserTalkVo;\nimport top.flya.system.domain.bo.PzcUserTalkBo;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 用户聊天Service接口\n *\n * @author ruoyi\n * @date 2023-07-16\n */\npublic interface IPzcUserTalkService {\n\n    /**\n     * 查询用户聊天\n     */\n    PzcUserTalkVo queryById(Long talkId);\n\n    /**\n     * 查询用户聊天列表\n     */\n    TableDataInfo<PzcUserTalkVo> queryPageList(PzcUserTalkBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询用户聊天列表\n     */\n    List<PzcUserTalkVo> queryList(PzcUserTalkBo bo);\n\n    /**\n     * 新增用户聊天\n     */\n    Boolean insertByBo(PzcUserTalkBo bo);\n\n    /**\n     * 修改用户聊天\n     */\n    Boolean updateByBo(PzcUserTalkBo bo);\n\n    /**\n     * 校验并批量删除用户聊天信息\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n\n\n    TableDataInfo<PzcUserTalkVo> queryMyPageList(PzcUserTalkBo bo, PageQuery pageQuery);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/IPzcViewPagerService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.system.domain.PzcViewPager;\nimport top.flya.system.domain.vo.PzcViewPagerVo;\nimport top.flya.system.domain.bo.PzcViewPagerBo;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 轮播图Service接口\n *\n * @author ruoyi\n * @date 2023-05-23\n */\npublic interface IPzcViewPagerService {\n\n    /**\n     * 查询轮播图\n     */\n    PzcViewPagerVo queryById(Integer viewPagerId);\n\n    /**\n     * 查询轮播图列表\n     */\n    TableDataInfo<PzcViewPagerVo> queryPageList(PzcViewPagerBo bo, PageQuery pageQuery);\n\n    /**\n     * 查询轮播图列表\n     */\n    List<PzcViewPagerVo> queryList(PzcViewPagerBo bo);\n\n    /**\n     * 新增轮播图\n     */\n    Boolean insertByBo(PzcViewPagerBo bo);\n\n    /**\n     * 修改轮播图\n     */\n    Boolean updateByBo(PzcViewPagerBo bo);\n\n    /**\n     * 校验并批量删除轮播图信息\n     */\n    Boolean deleteWithValidByIds(Collection<Integer> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcActivityConnArtistServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.system.domain.bo.PzcActivityConnArtistBo;\nimport top.flya.system.domain.vo.PzcActivityConnArtistVo;\nimport top.flya.system.domain.PzcActivityConnArtist;\nimport top.flya.system.mapper.PzcActivityConnArtistMapper;\nimport top.flya.system.service.IPzcActivityConnArtistService;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Collection;\n\n/**\n * 活动关联艺人Service业务层处理\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@RequiredArgsConstructor\n@Service\npublic class PzcActivityConnArtistServiceImpl implements IPzcActivityConnArtistService {\n\n    private final PzcActivityConnArtistMapper baseMapper;\n\n    /**\n     * 查询活动关联艺人\n     */\n    @Override\n    public PzcActivityConnArtistVo queryById(Integer activityConnArtistId){\n        return baseMapper.selectVoById(activityConnArtistId);\n    }\n\n    /**\n     * 查询活动关联艺人列表\n     */\n    @Override\n    public TableDataInfo<PzcActivityConnArtistVo> queryPageList(PzcActivityConnArtistBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcActivityConnArtist> lqw = buildQueryWrapper(bo);\n        Page<PzcActivityConnArtistVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询活动关联艺人列表\n     */\n    @Override\n    public List<PzcActivityConnArtistVo> queryList(PzcActivityConnArtistBo bo) {\n        LambdaQueryWrapper<PzcActivityConnArtist> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcActivityConnArtist> buildQueryWrapper(PzcActivityConnArtistBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcActivityConnArtist> lqw = Wrappers.lambdaQuery();\n        lqw.eq(bo.getActivityId() != null, PzcActivityConnArtist::getActivityId, bo.getActivityId());\n        lqw.eq(bo.getArtistId() != null, PzcActivityConnArtist::getArtistId, bo.getArtistId());\n        lqw.eq(bo.getCreateTime() != null, PzcActivityConnArtist::getCreateTime, bo.getCreateTime());\n        lqw.eq(bo.getUpdateTime() != null, PzcActivityConnArtist::getUpdateTime, bo.getUpdateTime());\n        return lqw;\n    }\n\n    /**\n     * 新增活动关联艺人\n     */\n    @Override\n    public Boolean insertByBo(PzcActivityConnArtistBo bo) {\n        PzcActivityConnArtist add = BeanUtil.toBean(bo, PzcActivityConnArtist.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setActivityConnArtistId(add.getActivityConnArtistId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改活动关联艺人\n     */\n    @Override\n    public Boolean updateByBo(PzcActivityConnArtistBo bo) {\n        PzcActivityConnArtist update = BeanUtil.toBean(bo, PzcActivityConnArtist.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcActivityConnArtist entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除活动关联艺人\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Integer> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcActivityConnIntroServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.system.domain.bo.PzcActivityConnIntroBo;\nimport top.flya.system.domain.vo.PzcActivityConnIntroVo;\nimport top.flya.system.domain.PzcActivityConnIntro;\nimport top.flya.system.mapper.PzcActivityConnIntroMapper;\nimport top.flya.system.service.IPzcActivityConnIntroService;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Collection;\n\n/**\n * 活动介绍与活动关联Service业务层处理\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@RequiredArgsConstructor\n@Service\npublic class PzcActivityConnIntroServiceImpl implements IPzcActivityConnIntroService {\n\n    private final PzcActivityConnIntroMapper baseMapper;\n\n    /**\n     * 查询活动介绍与活动关联\n     */\n    @Override\n    public PzcActivityConnIntroVo queryById(Integer activityConnIntroId){\n        return baseMapper.selectVoById(activityConnIntroId);\n    }\n\n    /**\n     * 查询活动介绍与活动关联列表\n     */\n    @Override\n    public TableDataInfo<PzcActivityConnIntroVo> queryPageList(PzcActivityConnIntroBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcActivityConnIntro> lqw = buildQueryWrapper(bo);\n        Page<PzcActivityConnIntroVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询活动介绍与活动关联列表\n     */\n    @Override\n    public List<PzcActivityConnIntroVo> queryList(PzcActivityConnIntroBo bo) {\n        LambdaQueryWrapper<PzcActivityConnIntro> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcActivityConnIntro> buildQueryWrapper(PzcActivityConnIntroBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcActivityConnIntro> lqw = Wrappers.lambdaQuery();\n        lqw.eq(bo.getActivityConnIntroId() != null, PzcActivityConnIntro::getActivityConnIntroId, bo.getActivityConnIntroId());\n        lqw.eq(bo.getActivityId() != null, PzcActivityConnIntro::getActivityId, bo.getActivityId());\n        lqw.eq(bo.getIntroId() != null, PzcActivityConnIntro::getIntroId, bo.getIntroId());\n        return lqw;\n    }\n\n    /**\n     * 新增活动介绍与活动关联\n     */\n    @Override\n    public Boolean insertByBo(PzcActivityConnIntroBo bo) {\n        PzcActivityConnIntro add = BeanUtil.toBean(bo, PzcActivityConnIntro.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setActivityConnIntroId(add.getActivityConnIntroId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改活动介绍与活动关联\n     */\n    @Override\n    public Boolean updateByBo(PzcActivityConnIntroBo bo) {\n        PzcActivityConnIntro update = BeanUtil.toBean(bo, PzcActivityConnIntro.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcActivityConnIntro entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除活动介绍与活动关联\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Integer> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcActivityConnTagServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.system.domain.bo.PzcActivityConnTagBo;\nimport top.flya.system.domain.vo.PzcActivityConnTagVo;\nimport top.flya.system.domain.PzcActivityConnTag;\nimport top.flya.system.mapper.PzcActivityConnTagMapper;\nimport top.flya.system.service.IPzcActivityConnTagService;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Collection;\n\n/**\n * 活动标签与活动关联Service业务层处理\n *\n * @author ruoyi\n * @date 2023-06-03\n */\n@RequiredArgsConstructor\n@Service\npublic class PzcActivityConnTagServiceImpl implements IPzcActivityConnTagService {\n\n    private final PzcActivityConnTagMapper baseMapper;\n\n    /**\n     * 查询活动标签与活动关联\n     */\n    @Override\n    public PzcActivityConnTagVo queryById(Integer activityConnTagId){\n        return baseMapper.selectVoById(activityConnTagId);\n    }\n\n    /**\n     * 查询活动标签与活动关联列表\n     */\n    @Override\n    public TableDataInfo<PzcActivityConnTagVo> queryPageList(PzcActivityConnTagBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcActivityConnTag> lqw = buildQueryWrapper(bo);\n        Page<PzcActivityConnTagVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询活动标签与活动关联列表\n     */\n    @Override\n    public List<PzcActivityConnTagVo> queryList(PzcActivityConnTagBo bo) {\n        LambdaQueryWrapper<PzcActivityConnTag> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcActivityConnTag> buildQueryWrapper(PzcActivityConnTagBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcActivityConnTag> lqw = Wrappers.lambdaQuery();\n        lqw.eq(bo.getActivityConnTagId() != null, PzcActivityConnTag::getActivityConnTagId, bo.getActivityConnTagId());\n        lqw.eq(bo.getActivityId() != null, PzcActivityConnTag::getActivityId, bo.getActivityId());\n        lqw.eq(bo.getTagId() != null, PzcActivityConnTag::getTagId, bo.getTagId());\n        lqw.eq(bo.getCreateTime() != null, PzcActivityConnTag::getCreateTime, bo.getCreateTime());\n        lqw.eq(bo.getUpdateTime() != null, PzcActivityConnTag::getUpdateTime, bo.getUpdateTime());\n        return lqw;\n    }\n\n    /**\n     * 新增活动标签与活动关联\n     */\n    @Override\n    public Boolean insertByBo(PzcActivityConnTagBo bo) {\n        PzcActivityConnTag add = BeanUtil.toBean(bo, PzcActivityConnTag.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setActivityConnTagId(add.getActivityConnTagId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改活动标签与活动关联\n     */\n    @Override\n    public Boolean updateByBo(PzcActivityConnTagBo bo) {\n        PzcActivityConnTag update = BeanUtil.toBean(bo, PzcActivityConnTag.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcActivityConnTag entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除活动标签与活动关联\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Integer> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcActivityGroupApplyServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.system.domain.bo.PzcActivityGroupApplyBo;\nimport top.flya.system.domain.vo.PzcActivityGroupApplyVo;\nimport top.flya.system.domain.PzcActivityGroupApply;\nimport top.flya.system.mapper.PzcActivityGroupApplyMapper;\nimport top.flya.system.service.IPzcActivityGroupApplyService;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Collection;\n\n/**\n * 活动组队申请列Service业务层处理\n *\n * @author ruoyi\n * @date 2023-07-10\n */\n@RequiredArgsConstructor\n@Service\npublic class PzcActivityGroupApplyServiceImpl implements IPzcActivityGroupApplyService {\n\n    private final PzcActivityGroupApplyMapper baseMapper;\n\n\n    /**\n     * 查询活动组队申请列\n     */\n    @Override\n    public PzcActivityGroupApplyVo queryById(Long applyId){\n        return baseMapper.selectVoById(applyId);\n    }\n\n    /**\n     * 查询活动组队申请列列表\n     */\n    @Override\n    public TableDataInfo<PzcActivityGroupApplyVo> queryPageList(PzcActivityGroupApplyBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcActivityGroupApply> lqw = buildQueryWrapper(bo);\n        Page<PzcActivityGroupApplyVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询活动组队申请列列表\n     */\n    @Override\n    public List<PzcActivityGroupApplyVo> queryList(PzcActivityGroupApplyBo bo) {\n        LambdaQueryWrapper<PzcActivityGroupApply> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcActivityGroupApply> buildQueryWrapper(PzcActivityGroupApplyBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcActivityGroupApply> lqw = Wrappers.lambdaQuery();\n        lqw.eq(bo.getApplyId() != null, PzcActivityGroupApply::getApplyId, bo.getApplyId());\n        lqw.eq(bo.getUserId() != null, PzcActivityGroupApply::getUserId, bo.getUserId());\n        lqw.eq(bo.getActivityId() != null, PzcActivityGroupApply::getActivityId, bo.getActivityId());\n        lqw.eq(bo.getGroupId() != null, PzcActivityGroupApply::getGroupId, bo.getGroupId());\n        lqw.eq(bo.getGroupType() != null, PzcActivityGroupApply::getGroupType, bo.getGroupType());\n        lqw.eq(bo.getMoney() != null, PzcActivityGroupApply::getMoney, bo.getMoney());\n//        lqw.eq(bo.getApplyStatus() != null, PzcActivityGroupApply::getApplyStatus, bo.getApplyStatus());\n        lqw.eq(StringUtils.isNotBlank(bo.getMessage()), PzcActivityGroupApply::getMessage, bo.getMessage());\n        lqw.eq(bo.getCreateTime() != null, PzcActivityGroupApply::getCreateTime, bo.getCreateTime());\n        lqw.eq(bo.getUpdateTime() != null, PzcActivityGroupApply::getUpdateTime, bo.getUpdateTime());\n        lqw.eq(bo.getWxz()!=null,PzcActivityGroupApply::getWxz,bo.getWxz());\n        return lqw;\n    }\n\n    /**\n     * 新增活动组队申请列\n     */\n    @Override\n    public Boolean insertByBo(PzcActivityGroupApplyBo bo) {\n        PzcActivityGroupApply add = BeanUtil.toBean(bo, PzcActivityGroupApply.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setApplyId(add.getApplyId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改活动组队申请列\n     */\n    @Override\n    public Boolean updateByBo(PzcActivityGroupApplyBo bo) {\n        PzcActivityGroupApply update = BeanUtil.toBean(bo, PzcActivityGroupApply.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcActivityGroupApply entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除活动组队申请列\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n\n    @Override\n    public boolean queryByUserIdAndActivityId(Long userId, Long activityId) {\n        LambdaQueryWrapper<PzcActivityGroupApply> lqw = Wrappers.lambdaQuery();\n        lqw.eq(PzcActivityGroupApply::getUserId, userId);\n        lqw.eq(PzcActivityGroupApply::getActivityId, activityId);\n        List<PzcActivityGroupApply> list = baseMapper.selectList(lqw);\n        if (list != null && list.size() > 0) {\n            return true;\n        }\n        return false;\n    }\n\n\n    @Override\n    public Integer updateStatus(Long applyId, int i) {\n        PzcActivityGroupApply apply = baseMapper.selectById(applyId);\n        if (apply != null) {\n            apply.setApplyStatus(i);\n            return baseMapper.updateById(apply);\n        }\n        return null;\n    }\n\n    @Override\n    public List<PzcActivityGroupApplyVo> queryListByGroupIds(List<Long> groupIds) {\n        LambdaQueryWrapper<PzcActivityGroupApply> lqw = Wrappers.lambdaQuery();\n        lqw.in(PzcActivityGroupApply::getGroupId, groupIds);\n        lqw.orderByDesc(PzcActivityGroupApply::getCreateTime);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    @Override\n    public PzcActivityGroupApplyVo queryByUserIdAndGroupId(Long userId, Long groupId) {\n        LambdaQueryWrapper<PzcActivityGroupApply> lqw = Wrappers.lambdaQuery();\n        lqw.eq(PzcActivityGroupApply::getUserId, userId);\n        lqw.eq(PzcActivityGroupApply::getGroupId, groupId);\n        return baseMapper.selectVoOne(lqw);\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcActivityGroupServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.system.domain.PzcActivityGroup;\nimport top.flya.system.domain.PzcActivityGroupApply;\nimport top.flya.system.domain.PzcRegion;\nimport top.flya.system.domain.PzcUserPhoto;\nimport top.flya.system.domain.bo.PzcActivityGroupBo;\nimport top.flya.system.domain.vo.PzcActivityGroupVo;\nimport top.flya.system.domain.vo.PzcUserVo;\nimport top.flya.system.mapper.*;\nimport top.flya.system.service.IPzcActivityGroupService;\nimport top.flya.system.utils.gaode.GaoDeMapUtil;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.util.*;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * 活动组队Service业务层处理\n *\n * @author ruoyi\n * @date 2023-07-10\n */\n@RequiredArgsConstructor\n@Service\n@Slf4j\npublic class PzcActivityGroupServiceImpl implements IPzcActivityGroupService {\n\n    private final PzcActivityGroupMapper baseMapper;\n\n    private final PzcActivityMapper pzcActivityMapper;\n\n    private final PzcUserPhotoMapper pzcUserPhotoMapper;\n\n\n    private final PzcActivityGroupApplyMapper pzcActivityGroupApplyMapper;\n\n    private final PzcRegionMapper pzcRegionMapper;\n\n    private final GaoDeMapUtil gaoDeMapUtil;\n\n    /**\n     * 查询活动组队\n     */\n    @Override\n    public PzcActivityGroupVo queryById(Long groupId) {\n        PzcActivityGroupVo pzcActivityGroupVo = baseMapper.selectVoByIdDIY(groupId);\n        if (pzcActivityGroupVo == null) {\n            return null;\n        }\n        if (pzcActivityGroupVo.getAuth() == 2) {\n            log.info(\"私密组队，不返回用户信息\");\n            pzcActivityGroupVo.setUser(getPrivateUser(pzcActivityGroupVo.getUser()));\n            pzcActivityGroupVo.setPhoto(null);\n        } else {\n            List<PzcUserPhoto> userPhotos = pzcUserPhotoMapper.selectList(new QueryWrapper<PzcUserPhoto>().eq(\"user_id\", pzcActivityGroupVo.getUserId()));\n            pzcActivityGroupVo.setPhoto(userPhotos);\n            if (pzcActivityGroupVo.getAuth() == 1) //权限 1只返回一张图片 即头像\n            {\n                pzcActivityGroupVo.setPhoto(null); //\n            }\n        }\n        if (pzcActivityMapper.selectVoById(pzcActivityGroupVo.getActivityId()) == null) {\n            Integer region = pzcActivityGroupVo.getRegion();\n            PzcRegion pzcRegion = pzcRegionMapper.selectById(region);\n            if (pzcRegion != null) {\n                pzcActivityGroupVo.setActivityTitle(\"【\" + pzcRegion.getName() + \"】\");\n            }\n\n        } else {\n            pzcActivityGroupVo.setActivityTitle(pzcActivityMapper.selectVoById(pzcActivityGroupVo.getActivityId()).getTitle());\n        }\n        pzcActivityGroupVo.setAddress(pzcActivityGroupVo.getAddress().substring(pzcActivityGroupVo.getAddress().indexOf(\"】\") + 1));\n\n\n        return pzcActivityGroupVo;\n    }\n\n    public PzcUserVo getPrivateUser(PzcUserVo pzcUser)\n    {\n        PzcUserVo pzcUserVo = new PzcUserVo();\n        pzcUserVo.setUserId(pzcUser.getUserId());\n        pzcUserVo.setOpenid(pzcUser.getOpenid());\n        pzcUserVo.setMoney(null);\n        pzcUserVo.setUserLevel(pzcUser.getUserLevel());\n        pzcUserVo.setIntegration(null);\n        pzcUserVo.setIntegrationNow(null);\n        pzcUserVo.setRealname(null);\n        pzcUserVo.setNickname(\"匿名用户\");\n        pzcUserVo.setSex(null);\n        pzcUserVo.setPhone(null);\n        pzcUserVo.setAvatar(\"https://img.flya.top/mac/202309261521392.png\");\n        pzcUserVo.setAddress(null);\n        pzcUserVo.setIntro(null);\n        pzcUserVo.setAge(null);\n        pzcUserVo.setConstellation(null);\n        pzcUserVo.setMbti(null);\n        pzcUserVo.setHobby(null);\n        pzcUserVo.setSchool(null);\n        pzcUserVo.setOccupation(null);\n        pzcUserVo.setMusicStyle(null);\n        pzcUserVo.setState(null);\n        pzcUserVo.setExemptCancel(null);\n        return pzcUserVo;\n    }\n\n    /**\n     * 查询活动组队列表\n     */\n    @Override\n    public TableDataInfo<PzcActivityGroupVo> queryPageList(PzcActivityGroupBo bo, PageQuery pageQuery) {\n\n        log.info(\"查询活动组队列表 bo is {}\", JsonUtils.toJsonString(bo));\n        if(bo.getUserLevel()!=null)\n        {\n            pageQuery.setIsAsc(\"desc\");\n            pageQuery.setOrderByColumn(\"u.user_level\");\n        }\n\n\n        Page<PzcActivityGroupVo> result = baseMapper.selectDetailsList(pageQuery.build(), bo);\n        ArrayList<PzcActivityGroupVo> pzcActivityGroupVos = new ArrayList<>();\n        Pattern pattern = Pattern.compile(\"【(.*?)】(.*?)\");\n        result.getRecords().forEach(\n            pzcActivityGroupVo -> {\n                pzcActivityGroupVos.add(pzcActivityGroupVo);\n                if (pzcActivityGroupVo.getAuth() == 2) {\n                    log.info(\"私密组队，返回用户信息的部分信息\");\n                    pzcActivityGroupVo.setUser(getPrivateUser(pzcActivityGroupVo.getUser()));\n                }//如果是私密组队，不返回用户信息\n                //查询当前组队 是否正在进行中 如果是 则 不进入组队大厅\n                Long groupId = pzcActivityGroupVo.getGroupId();\n                Long count = pzcActivityGroupApplyMapper.selectCount(new QueryWrapper<PzcActivityGroupApply>().eq(\"group_id\", groupId).notIn(\"apply_status\", -1, 0, 13, 14, 15));\n                if (count > 0) {\n                    if(bo.getUserId()==null) //只是不对大厅展示\n                    {\n                        //从列表中移除这个对象、\n                        log.info(\"当前组队正在进行中，不返回组队信息 groupId is {}\", pzcActivityGroupVo.getGroupId());\n                        pzcActivityGroupVos.remove(pzcActivityGroupVo);\n                    }\n\n                }\n                if (bo.getLongitudeAndLatitude() != null && !bo.getLongitudeAndLatitude().isEmpty()) {\n                    //计算距离\n\n                    String jwdFromAddress = pzcActivityGroupVo.getAddress();\n\n                    Matcher matcher = pattern.matcher(jwdFromAddress);\n                    if (matcher.find()) {\n                        // 获取经纬度\n                        log.info(\"从数据库地址经纬度 is {}\", jwdFromAddress);\n                        jwdFromAddress = matcher.group(1);\n                        pzcActivityGroupVo.setAddress(pzcActivityGroupVo.getAddress().substring(pzcActivityGroupVo.getAddress().indexOf(\"】\") + 1));\n                    } else {\n                        log.info(\"调用API获取经纬度 is {}\", jwdFromAddress);\n                        jwdFromAddress = gaoDeMapUtil.getLonLat(pzcActivityGroupVo.getAddress()).getData().toString(); //经纬度\n                    }\n                    String distance = gaoDeMapUtil.getDistance(bo.getLongitudeAndLatitude(), jwdFromAddress).getData().toString();\n                    log.info(\"离我【{}】米\", distance);\n                    //计算距离\n                    BigDecimal distanceStr = new BigDecimal(distance).divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP); //保留两位小数\n                    pzcActivityGroupVo.setDistance(distanceStr);\n                }\n            }\n        );\n        if (bo.getDistance() != null) {\n//            log.info(\"按照距离排序前： {}\",JSONUtil.toJsonPrettyStr(pzcActivityGroupVos));\n            pzcActivityGroupVos.sort(Comparator.comparing(PzcActivityGroupVo::getDistance)); //按照距离远到近排序\n//            log.info(\"按照距离排序后： {}\",JSONUtil.toJsonPrettyStr(pzcActivityGroupVos));\n        }\n\n        //查询当前组队 是否正在进行中 如果是 则 不进入组队大厅\n        return TableDataInfo.build(pzcActivityGroupVos);\n    }\n\n    /**\n     * 查询活动组队列表\n     */\n    @Override\n    public List<PzcActivityGroupVo> queryList(PzcActivityGroupBo bo) {\n        LambdaQueryWrapper<PzcActivityGroup> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcActivityGroup> buildQueryWrapper(PzcActivityGroupBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcActivityGroup> lqw = Wrappers.lambdaQuery();\n        lqw.eq(bo.getActivityId() != null, PzcActivityGroup::getActivityId, bo.getActivityId());\n        lqw.eq(bo.getUserId() != null, PzcActivityGroup::getUserId, bo.getUserId());\n        lqw.eq(StringUtils.isNotBlank(bo.getTitle()), PzcActivityGroup::getTitle, bo.getTitle());\n        lqw.eq(bo.getMoney() != null, PzcActivityGroup::getMoney, bo.getMoney());\n        lqw.eq(bo.getGroupType() != null, PzcActivityGroup::getGroupType, bo.getGroupType());\n        lqw.eq(StringUtils.isNotBlank(bo.getAddress()), PzcActivityGroup::getAddress, bo.getAddress());\n        lqw.eq(bo.getActivityTime() != null, PzcActivityGroup::getActivityTime, bo.getActivityTime());\n        lqw.eq(bo.getAuth() != null, PzcActivityGroup::getAuth, bo.getAuth());\n        lqw.eq(bo.getCreateTime() != null, PzcActivityGroup::getCreateTime, bo.getCreateTime());\n        lqw.eq(bo.getUpdateTime() != null, PzcActivityGroup::getUpdateTime, bo.getUpdateTime());\n        return lqw;\n    }\n\n    /**\n     * 新增活动组队\n     */\n    @Override\n    public Boolean insertByBo(PzcActivityGroupBo bo) {\n        PzcActivityGroup add = BeanUtil.toBean(bo, PzcActivityGroup.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setGroupId(add.getGroupId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改活动组队\n     */\n    @Override\n    public Boolean updateByBo(PzcActivityGroupBo bo) {\n        PzcActivityGroup update = BeanUtil.toBean(bo, PzcActivityGroup.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcActivityGroup entity) {\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除活动组队\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if (isValid) {\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n\n    @Override\n    public boolean checkActivity(Long activityId) {\n        return pzcActivityMapper.selectVoById(activityId) != null;\n    }\n\n    @Override\n    public boolean checkGroup(Long userId, Long activityId) {\n        LambdaQueryWrapper<PzcActivityGroup> lqw = Wrappers.lambdaQuery();\n        lqw.eq(PzcActivityGroup::getUserId, userId);\n        lqw.eq(PzcActivityGroup::getActivityId, activityId);\n        //\n        return baseMapper.selectCount(lqw) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcActivityServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.common.BatchUtils;\nimport top.flya.system.domain.*;\nimport top.flya.system.domain.bo.PzcActivityBo;\nimport top.flya.system.domain.vo.PzcActivityVo;\nimport top.flya.system.mapper.*;\nimport top.flya.system.service.IPzcActivityService;\nimport top.flya.system.utils.gaode.GaoDeMapUtil;\n\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 活动Service业务层处理\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@RequiredArgsConstructor\n@Service\n@Slf4j\npublic class PzcActivityServiceImpl implements IPzcActivityService {\n\n    private final PzcActivityMapper baseMapper;\n\n    private final PzcIntroMapper pzcIntroMapper;\n\n    private final PzcActivityConnIntroMapper pzcActivityConnIntroMapper;\n\n    private final PzcArtistMapper pzcArtistMapper;\n\n    private final PzcActivityConnArtistMapper pzcActivityConnArtistMapper;\n\n    private final PzcTagMapper pzcTagMapper;\n\n    private final PzcActivityConnTagMapper pzcActivityConnTagMapper;\n\n    private final PzcOrganizerMapper pzcOrganizerMapper;\n\n    private final BatchUtils batchUtils;\n\n    private final PzcOrganizerTicketMapper pzcOrganizerTicketMapper;\n\n    private final GaoDeMapUtil gaoDeMapUtil;\n\n    /**\n     * 查询活动\n     */\n    @Override\n    public PzcActivityVo queryById(Integer activityId) {\n        PzcActivityVo pzcActivityVo = baseMapper.selectVoById(activityId);\n        if(pzcActivityVo==null)\n        {\n            throw new RuntimeException(\"当前活动不存在或者已过期,请重新选择活动哦~\");\n        }\n        List<PzcActivityVo> pzcActivityVos = new ArrayList<>();\n        pzcActivityVos.add(pzcActivityVo);\n        pzcActivityVos.forEach(r->{\n            r.setInnerImage(pzcActivityVo.getInnerImage().contains(\"http\")?pzcActivityVo.getInnerImage():batchUtils.getNewImageUrls(Collections.singletonList(pzcActivityVo.getInnerImage())).get(Long.parseLong(pzcActivityVo.getInnerImage())));\n            r.setCoverImage(pzcActivityVo.getCoverImage().contains(\"http\")?pzcActivityVo.getCoverImage():batchUtils.getNewImageUrls(Collections.singletonList(pzcActivityVo.getCoverImage())).get(Long.parseLong(pzcActivityVo.getCoverImage())));\n            r.setShareImage(pzcActivityVo.getShareImage()==null?null:pzcActivityVo.getShareImage().contains(\"http\")?pzcActivityVo.getShareImage():batchUtils.getNewImageUrls(Collections.singletonList(pzcActivityVo.getShareImage())).get(Long.parseLong(pzcActivityVo.getShareImage())));\n\n            log.info(\"pzcActivityVo.getInnerImage() = {}\",pzcActivityVo.getInnerImage());\n            ArrayList<PzcIntro> introList = new ArrayList<>();\n            ArrayList<PzcArtist> artistList = new ArrayList<>();\n            ArrayList<PzcTag> tagList = new ArrayList<>();\n            pzcActivityConnIntroMapper.selectList(Wrappers.<PzcActivityConnIntro>lambdaQuery().eq(PzcActivityConnIntro::getActivityId, r.getActivityId())).forEach(c->{\n                PzcIntro pzcIntro = pzcIntroMapper.selectById(c.getIntroId());\n                if(pzcIntro == null){\n                    return;\n                }\n                if(StringUtils.isEmpty(pzcIntro.getImageFullUrl()))\n                {\n                    introList.add(pzcIntro);\n                    return;\n                }\n                Map<Long, String> newImageUrls = batchUtils.getNewImageUrls(Collections.singletonList(pzcIntro.getImageFullUrl()));\n                pzcIntro.setImageFullUrl(pzcIntro.getImageFullUrl().contains(\"http\")?pzcIntro.getImageFullUrl():newImageUrls.get(Long.parseLong(pzcIntro.getImageFullUrl())));\n                introList.add(pzcIntro);\n            });\n\n            //筛选introList 中元素 PzcIntro 的type==1\n            r.setIntroList(introList.stream().filter(intro -> intro.getType() == 1).collect(Collectors.toList()));\n            r.setStageList(introList.stream().filter(intro -> intro.getType() == 0).collect(Collectors.toList()));\n            pzcActivityConnArtistMapper.selectList(Wrappers.<PzcActivityConnArtist>lambdaQuery().eq(PzcActivityConnArtist::getActivityId, r.getActivityId())).forEach(c->{\n                PzcArtist pzcArtist = pzcArtistMapper.selectById(c.getArtistId());\n                if(pzcArtist == null){\n                    return;\n                }\n                if(StringUtils.isEmpty(pzcArtist.getImageUrl()))\n                {\n                    artistList.add(pzcArtist);\n                    return;\n                }\n                Map<Long, String> newImageUrls = batchUtils.getNewImageUrls(Collections.singletonList(pzcArtist.getImageUrl()));\n                pzcArtist.setImageUrl(pzcArtist.getImageUrl().contains(\"http\")?pzcArtist.getImageUrl():newImageUrls.get(Long.parseLong(pzcArtist.getImageUrl())));\n                artistList.add(pzcArtist);\n            });\n            r.setArtistList(artistList);\n            pzcActivityConnTagMapper.selectVoList(Wrappers.<PzcActivityConnTag>lambdaQuery().eq(PzcActivityConnTag::getActivityId, r.getActivityId())).forEach(c->{\n                PzcTag pzcTag = pzcTagMapper.selectById(c.getTagId());\n                if(pzcTag == null){\n                    return;\n                }\n                if(StringUtils.isEmpty(pzcTag.getImageUrl()))\n                {\n                    tagList.add(pzcTag);\n                    return;\n                }\n                Map<Long, String> newImageUrls = batchUtils.getNewImageUrls(Collections.singletonList(pzcTag.getImageUrl()));\n                pzcTag.setImageUrl(pzcTag.getImageUrl().contains(\"http\")?pzcTag.getImageUrl():newImageUrls.get(Long.parseLong(pzcTag.getImageUrl())));\n                tagList.add(pzcTag);\n            });\n            r.setTagList(tagList);\n            PzcOrganizer pzcOrganizer = pzcOrganizerMapper.selectOne(Wrappers.<PzcOrganizer>lambdaQuery().eq(PzcOrganizer::getOrganizerId, r.getOrganizerId()));\n\n            pzcOrganizer.setLogo(pzcOrganizer.getLogo().contains(\"http\")?pzcOrganizer.getLogo():batchUtils.getNewImageUrls(Collections.singletonList(pzcOrganizer.getLogo())).get(Long.parseLong(pzcOrganizer.getLogo())));\n            List<PzcOrganizerTicket> pzcOrganizerTickets = pzcOrganizerTicketMapper.selectList(Wrappers.<PzcOrganizerTicket>lambdaQuery().eq(PzcOrganizerTicket::getOrganizerId, pzcOrganizer.getOrganizerId()));\n            pzcOrganizerTickets.forEach(\n                p->{\n                    p.setLogoImage(p.getLogoImage().contains(\"http\")?p.getLogoImage():batchUtils.getNewImageUrls(Collections.singletonList(p.getLogoImage())).get(Long.parseLong(p.getLogoImage())));\n                    p.setQrImage(p.getQrImage().contains(\"http\")?p.getQrImage():batchUtils.getNewImageUrls(Collections.singletonList(p.getQrImage())).get(Long.parseLong(p.getQrImage())));\n                }\n            );\n            pzcOrganizer.setOrganizerTickets(pzcOrganizerTickets);\n            r.setOrganizerList(pzcOrganizer);\n\n//            r.setDistance();\n        });\n        return pzcActivityVos.get(0);\n    }\n\n    /**\n     * 查询活动列表\n     */\n    @Override\n    public TableDataInfo<PzcActivityVo> queryPageList(PzcActivityBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcActivity> lqw = buildQueryWrapper(bo);\n        Page<PzcActivityVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        result.setRecords(batchUtils.transformToPzcActivityVo(result.getRecords()));\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询活动列表\n     */\n    @Override\n    public TableDataInfo<PzcActivityVo> queryPageListWx(PzcActivityBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcActivity> lqw = buildQueryWrapper(bo);\n        pageQuery.setIsAsc(\"asc\");\n        pageQuery.setOrderByColumn(\"start_time\");\n        lqw.ge(PzcActivity::getShowTime, new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\").format(new Date()));\n        Page<PzcActivityVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        result.setRecords(batchUtils.transformToPzcActivityVo(result.getRecords()));\n        return TableDataInfo.build(result);\n    }\n\n\n    /**\n     * 查询活动列表\n     */\n    @Override\n    public List<PzcActivityVo> queryList(PzcActivityBo bo) {\n        LambdaQueryWrapper<PzcActivity> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcActivity> buildQueryWrapper(PzcActivityBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcActivity> lqw = Wrappers.lambdaQuery();\n        lqw.eq(StringUtils.isNotBlank(bo.getAddress()), PzcActivity::getAddress, bo.getAddress());\n        lqw.eq(bo.getRegionId() != null, PzcActivity::getRegionId, bo.getRegionId());\n        lqw.like(StringUtils.isNotBlank(bo.getTitle()), PzcActivity::getTitle, bo.getTitle());\n        lqw.eq(StringUtils.isNotBlank(bo.getStartTime()), PzcActivity::getStartTime, bo.getStartTime());\n        lqw.eq(StringUtils.isNotBlank(bo.getEndDate()), PzcActivity::getEndDate, bo.getEndDate());\n        lqw.eq(bo.getInnerImage() != null, PzcActivity::getInnerImage, bo.getInnerImage());\n        lqw.eq(StringUtils.isNotBlank(bo.getShowTime()), PzcActivity::getShowTime, bo.getShowTime());\n        lqw.eq(StringUtils.isNotBlank(bo.getCoverImage()), PzcActivity::getCoverImage, bo.getCoverImage());\n        lqw.between(params.get(\"beginCreateTime\") != null && params.get(\"endCreateTime\") != null,\n            PzcActivity::getCreateTime, params.get(\"beginCreateTime\"), params.get(\"endCreateTime\"));\n        lqw.between(params.get(\"beginUpdateTime\") != null && params.get(\"endUpdateTime\") != null,\n            PzcActivity::getUpdateTime, params.get(\"beginUpdateTime\"), params.get(\"endUpdateTime\"));\n        lqw.eq(bo.getState() != null, PzcActivity::getState, bo.getState());\n       if(bo.getOrganizerList()!=null)\n       {\n           lqw.eq(bo.getOrganizerList().getOrganizerId() != null, PzcActivity::getOrganizerId, bo.getOrganizerList().getOrganizerId());\n       }\n       if(bo.getClassify()!=null)\n       {\n              lqw.eq(true, PzcActivity::getClassify, bo.getClassify());\n       }\n       lqw.eq(bo.getRegion()!=null, PzcActivity::getRegion, bo.getRegion());\n\n       return lqw;\n    }\n\n    /**\n     * 新增活动\n     */\n    @Override\n    @Transactional\n    public Boolean insertByBo(PzcActivityBo bo) {\n        PzcActivity add = BeanUtil.toBean(bo, PzcActivity.class);\n        if (bo.getActivityId() != null) {\n            throw new RuntimeException(\"活动id在创建时不能填写\");\n        }\n        log.info(\"新增活动 主办方Id为: {}\", bo.getOrganizerList().getOrganizerId());\n        if(bo.getOrganizerList().getOrganizerId()==null)\n        {\n           //就根据输入的内容来新建主办方\n            PzcOrganizer organizer = new PzcOrganizer();\n            organizer.setName(bo.getOrganizerList().getName());\n            organizer.setPhone(bo.getOrganizerList().getPhone());\n            organizer.setContent(bo.getOrganizerList().getContent());\n            organizer.setLogo(bo.getOrganizerList().getLogo());\n            pzcOrganizerMapper.insert(organizer);\n            log.info(\"新增活动 新建主办方Id为: {}\", organizer.getOrganizerId());\n            bo.getOrganizerList().setOrganizerId(organizer.getOrganizerId());\n        }\n        add.setOrganizerId(bo.getOrganizerList().getOrganizerId());\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setActivityId(add.getActivityId());\n        }\n        saveActivityConfigs(bo);\n        return flag;\n    }\n\n\n    @Transactional //这里关联其他表的保存\n    public void saveActivityConfigs(PzcActivityBo bo)\n    {\n\n        if(bo.getOrganizerList()!=null) //主办方票务\n        {\n            List<PzcOrganizerTicket> organizerTickets = bo.getOrganizerList().getOrganizerTickets();\n            if(organizerTickets!=null&&organizerTickets.size()!=0)\n            {\n                List<Integer> ids = new ArrayList<>();\n               //校验PzcOrganizerTicket 的 \"organizerId\": 是否 是当前组织下的\n                organizerTickets.forEach(o->{\n                    if(o.getOrganizerTicketId()==null)\n                    {\n                        //新建\n                        PzcOrganizerTicket pzcOrganizerTicket = new PzcOrganizerTicket();\n                        pzcOrganizerTicket.setOrganizerId(bo.getOrganizerList().getOrganizerId());\n                        pzcOrganizerTicket.setName(o.getName());\n                        pzcOrganizerTicket.setLogoImage(o.getLogoImage());\n                        pzcOrganizerTicket.setQrImage(o.getQrImage());\n\n                        pzcOrganizerTicketMapper.insert(pzcOrganizerTicket);\n                        o.setOrganizerTicketId(pzcOrganizerTicket.getOrganizerTicketId());\n                        o.setOrganizerId(pzcOrganizerTicket.getOrganizerId());\n                        ids.add(Math.toIntExact(o.getOrganizerTicketId()));\n                        return;\n                    }\n                    ids.add(Math.toIntExact(o.getOrganizerTicketId()));\n                    PzcOrganizerTicket pzcOrganizerTicket = pzcOrganizerTicketMapper.selectById(o.getOrganizerTicketId());\n                    pzcOrganizerTicketMapper.update(o, Wrappers.<PzcOrganizerTicket>lambdaQuery().eq(PzcOrganizerTicket::getOrganizerTicketId,o.getOrganizerTicketId()));\n                    if(!Objects.equals(bo.getOrganizerList().getOrganizerId(),pzcOrganizerTicket.getOrganizerId()))\n                    {\n                        log.info(\"bo.getOrganizerList().getOrganizerId() is {} , pzcOrganizerTicket.getOrganizerId() is {}\",bo.getOrganizerList().getOrganizerId(),pzcOrganizerTicket.getOrganizerId());\n                        throw new RuntimeException(\"票务组织者id不是当前组织者id\");\n                    }\n                });\n                //删除\n                if(ids.size()>0)\n                {\n                    LambdaQueryWrapper<PzcOrganizerTicket> lqw = Wrappers.lambdaQuery();\n                    lqw.eq(PzcOrganizerTicket::getOrganizerId,bo.getOrganizerList().getOrganizerId());\n                    lqw.notIn(PzcOrganizerTicket::getOrganizerTicketId,ids);\n                    pzcOrganizerTicketMapper.delete(lqw);\n                }\n\n            }\n        }\n\n        if(bo.getStageList().size()!=0) //舞台介绍\n        {\n            bo.getStageList().forEach(stage-> {\n                if(stage.getIntroId()==null)\n                {\n                    //新建\n                    PzcIntro pzcIntro = new PzcIntro();\n                    pzcIntro.setContent(stage.getContent());\n                    pzcIntro.setImageFullUrl(stage.getImageFullUrl());\n                    pzcIntro.setType(stage.getType());\n                    pzcIntro.setTitle(stage.getTitle());\n                    pzcIntroMapper.insert(pzcIntro);\n\n                    stage.setIntroId(pzcIntro.getIntroId());\n                }\n\n                //首先查询这个介绍是否存在\n                LambdaQueryWrapper<PzcIntro> lqw = Wrappers.lambdaQuery();\n                lqw.eq(PzcIntro::getIntroId, stage.getIntroId());\n                PzcIntro pzcIntro = pzcIntroMapper.selectOne(lqw);\n                if (pzcIntro == null) {\n                    throw new RuntimeException(\"舞台介绍不存在 id is \" + stage.getIntroId());\n                }\n                LambdaQueryWrapper<PzcActivityConnIntro> lqw3 = Wrappers.lambdaQuery();\n                lqw3.eq(PzcActivityConnIntro::getActivityId, bo.getActivityId());\n                List<PzcActivityConnIntro> pzcActivityConnIntros = pzcActivityConnIntroMapper.selectList(lqw3);\n                if(pzcActivityConnIntros.size()!=0)\n                {\n                    pzcActivityConnIntros.forEach(\n                        p->{\n                            Integer introId = p.getIntroId();\n                            PzcIntro pzcIntro1 = pzcIntroMapper.selectById(introId);\n                            if(pzcIntro1==null)\n                            {\n                                return;\n                            }\n                            if(pzcIntro1.getType()==0)\n                            {\n                                pzcActivityConnIntroMapper.delete(new QueryWrapper<PzcActivityConnIntro>().eq(\"intro_id\",introId).eq(\"activity_id\",bo.getActivityId())); //删除之后重新添加\n                            }\n                        }\n                    );\n                }\n\n                //介绍表关联活动表 先查询关联关系是否存在\n                LambdaQueryWrapper<PzcActivityConnIntro> lqw2 = Wrappers.lambdaQuery();\n                lqw2.eq(PzcActivityConnIntro::getActivityId, bo.getActivityId());\n                lqw2.eq(PzcActivityConnIntro::getIntroId, stage.getIntroId());\n                PzcActivityConnIntro pzcActivityConnIntro1 = pzcActivityConnIntroMapper.selectOne(lqw2);\n                if (pzcActivityConnIntro1 != null) {\n                    return;\n                }\n\n\n\n                PzcActivityConnIntro pzcActivityConnIntro = new PzcActivityConnIntro();\n                pzcActivityConnIntro.setActivityId(bo.getActivityId());\n                pzcActivityConnIntro.setIntroId(Math.toIntExact(stage.getIntroId()));\n                pzcActivityConnIntroMapper.insert(pzcActivityConnIntro);\n            });\n        }\n\n        if (bo.getIntroList().size() != 0) { //介绍\n            bo.getIntroList().forEach(intro -> {\n\n                if(intro.getIntroId()==null)\n                {\n                    //新建\n                    PzcIntro pzcIntro = new PzcIntro();\n                    pzcIntro.setContent(intro.getContent());\n                    pzcIntro.setImageFullUrl(intro.getImageFullUrl());\n                    pzcIntro.setType(intro.getType());\n                    pzcIntro.setTitle(intro.getTitle());\n                    pzcIntroMapper.insert(pzcIntro);\n\n                    intro.setIntroId(pzcIntro.getIntroId());\n                }\n\n\n                //首先查询这个介绍是否存在\n                LambdaQueryWrapper<PzcIntro> lqw = Wrappers.lambdaQuery();\n                lqw.eq(PzcIntro::getIntroId, intro.getIntroId());\n                PzcIntro pzcIntro = pzcIntroMapper.selectOne(lqw);\n                if (pzcIntro == null) {\n                    throw new RuntimeException(\"介绍不存在 id is \" + intro.getIntroId());\n                }\n\n\n                LambdaQueryWrapper<PzcActivityConnIntro> lqw3 = Wrappers.lambdaQuery();\n                lqw3.eq(PzcActivityConnIntro::getActivityId, bo.getActivityId());\n                List<PzcActivityConnIntro> pzcActivityConnIntros = pzcActivityConnIntroMapper.selectList(lqw3);\n                if(pzcActivityConnIntros.size()!=0)\n                {\n                    pzcActivityConnIntros.forEach(\n                        p->{\n                            Integer introId = p.getIntroId();\n                            PzcIntro pzcIntro1 = pzcIntroMapper.selectById(introId);\n                            if(pzcIntro1==null)\n                            {\n                                return;\n                            }\n                            if(pzcIntro1.getType()==1)\n                            {\n                                pzcActivityConnIntroMapper.delete(new QueryWrapper<PzcActivityConnIntro>().eq(\"intro_id\",introId).eq(\"activity_id\",bo.getActivityId())); //删除之后重新添加\n                            }\n                        }\n                    );\n                }\n\n                //介绍表关联活动表 先查询关联关系是否存在\n                LambdaQueryWrapper<PzcActivityConnIntro> lqw2 = Wrappers.lambdaQuery();\n                lqw2.eq(PzcActivityConnIntro::getActivityId, bo.getActivityId());\n                lqw2.eq(PzcActivityConnIntro::getIntroId, intro.getIntroId());\n                PzcActivityConnIntro pzcActivityConnIntro1 = pzcActivityConnIntroMapper.selectOne(lqw2);\n                if (pzcActivityConnIntro1 != null) {\n                    return;\n                }\n\n\n                PzcActivityConnIntro pzcActivityConnIntro = new PzcActivityConnIntro();\n                pzcActivityConnIntro.setActivityId(bo.getActivityId());\n                pzcActivityConnIntro.setIntroId(Math.toIntExact(intro.getIntroId()));\n                pzcActivityConnIntroMapper.insert(pzcActivityConnIntro);\n\n            });\n\n        }\n        if (bo.getArtistList().size() != 0) { //艺人\n            List<Integer> artistIds = new ArrayList<>();\n            bo.getArtistList().forEach(artist -> {\n                artistIds.add(Math.toIntExact(artist.getArtistId()));\n                if(artist.getArtistId()==null)\n                {\n                    //新建\n                    PzcArtist pzcArtist = new PzcArtist();\n\n                    pzcArtist.setName(artist.getName());\n                    pzcArtist.setImageUrl(artist.getImageUrl());\n                    pzcArtist.setDescription(artist.getDescription());\n                    pzcArtistMapper.insert(pzcArtist);\n\n                    artist.setArtistId(pzcArtist.getArtistId());\n                }\n                //首先查询这个艺人是否存在\n                LambdaQueryWrapper<PzcArtist> lqw = Wrappers.lambdaQuery();\n                lqw.eq(PzcArtist::getArtistId, artist.getArtistId());\n                PzcArtist pzcArtist = pzcArtistMapper.selectOne(lqw);\n                if (pzcArtist == null) {\n                    throw new RuntimeException(\"艺术家不存在 id is \" + artist.getArtistId());\n                }\n                //介绍表关联活动表 先查询关联关系是否存在\n                LambdaQueryWrapper<PzcActivityConnArtist> lqw2 = Wrappers.lambdaQuery();\n                lqw2.eq(PzcActivityConnArtist::getActivityId, bo.getActivityId());\n                lqw2.eq(PzcActivityConnArtist::getArtistId, artist.getArtistId());\n                PzcActivityConnArtist pzcActivityConnArtist1 = pzcActivityConnArtistMapper.selectOne(lqw2);\n                if (pzcActivityConnArtist1 != null) {\n                    return;\n                }\n                PzcActivityConnArtist pzcActivityConnArtist = new PzcActivityConnArtist();\n                pzcActivityConnArtist.setActivityId(bo.getActivityId());\n                pzcActivityConnArtist.setArtistId(Math.toIntExact(artist.getArtistId()));\n                pzcActivityConnArtistMapper.insert(pzcActivityConnArtist);\n            });\n\n            if(artistIds.size()>0)\n            {\n                //这里删除多余的\n                LambdaQueryWrapper<PzcActivityConnArtist> lqw3 = Wrappers.lambdaQuery();\n                lqw3.eq(PzcActivityConnArtist::getActivityId, bo.getActivityId());\n                lqw3.notIn(PzcActivityConnArtist::getArtistId, artistIds);\n                pzcActivityConnArtistMapper.delete(lqw3);\n            }\n\n\n        }\n        if (bo.getTagList().size() != 0) { //标签\n            List<Integer> tagIds = new ArrayList<>();\n            bo.getTagList().forEach(tag -> {\n                tagIds.add(Math.toIntExact(tag.getTagId()));\n                if(tag.getTagId()==null)\n                {\n                    //新建\n                    PzcTag pzcTag = new PzcTag();\n                    pzcTag.setName(tag.getName());\n                    pzcTag.setImageUrl(tag.getImageUrl());\n                    pzcTagMapper.insert(pzcTag);\n\n                    tag.setTagId(pzcTag.getTagId());\n                }\n\n                //首先查询这个标签是否存在\n                LambdaQueryWrapper<PzcTag> lqw = Wrappers.lambdaQuery();\n                lqw.eq(PzcTag::getTagId, tag.getTagId());\n                PzcTag pzcTag = pzcTagMapper.selectOne(lqw);\n                if (pzcTag == null) {\n                    throw new RuntimeException(\"标签不存在 id is \" + tag.getTagId());\n                }\n                //介绍表关联活动表 先查询关联关系是否存在\n                LambdaQueryWrapper<PzcActivityConnTag> lqw2 = Wrappers.lambdaQuery();\n                lqw2.eq(PzcActivityConnTag::getActivityId, bo.getActivityId());\n                lqw2.eq(PzcActivityConnTag::getTagId, tag.getTagId());\n                PzcActivityConnTag pzcActivityConnTag1 = pzcActivityConnTagMapper.selectOne(lqw2);\n                if (pzcActivityConnTag1 != null) {\n//                    throw new RuntimeException(\"标签已经关联 id is \" + tag.getTagId() + \"无需重复关联\");\n                    return;\n                }\n\n                PzcActivityConnTag pzcActivityConnTag = new PzcActivityConnTag();\n                pzcActivityConnTag.setActivityId(bo.getActivityId());\n                pzcActivityConnTag.setTagId(Math.toIntExact(tag.getTagId()));\n                pzcActivityConnTagMapper.insert(pzcActivityConnTag);\n\n            });\n            if(tagIds.size()>0)\n            {\n                //这里删除多余的\n                LambdaQueryWrapper<PzcActivityConnTag> lqw3 = Wrappers.lambdaQuery();\n                lqw3.eq(PzcActivityConnTag::getActivityId, bo.getActivityId());\n                lqw3.notIn(PzcActivityConnTag::getTagId, tagIds);\n                pzcActivityConnTagMapper.delete(lqw3);\n            }\n\n\n        }\n    }\n\n    /**\n     * 修改活动\n     */\n    @Override\n    @Transactional\n    public Boolean updateByBo(PzcActivityBo bo) {\n        PzcActivity update = BeanUtil.toBean(bo, PzcActivity.class);\n        update.setOrganizerId(bo.getOrganizerList().getOrganizerId());\n\n        validEntityBeforeSave(update);\n\n        boolean flag = baseMapper.updateById(update) > 0;\n        saveActivityConfigs(bo);\n        return flag;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcActivity entity) {\n        log.info(\"数据校验开始entity.getOrganizerId is {}\", entity.getOrganizerId());\n        //TODO 做一些数据校验,如唯一约束\n        if (entity.getOrganizerId() != null) {\n            //首先查询这个组织是否存在\n            LambdaQueryWrapper<PzcOrganizer> lqw = Wrappers.lambdaQuery();\n            lqw.eq(PzcOrganizer::getOrganizerId, entity.getOrganizerId());\n            PzcOrganizer pzcOrganizer = pzcOrganizerMapper.selectOne(lqw);\n            if (pzcOrganizer == null) {\n                throw new RuntimeException(\"活动组织者不存在 id is \" + entity.getOrganizerId());\n            }\n\n        }\n    }\n\n    /**\n     * 批量删除活动\n     */\n    @Override\n    @Transactional\n    public Boolean deleteWithValidByIds(Collection<Integer> ids, Boolean isValid) {\n        if (isValid) {\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        //删除活动与其他表的关联关系\n            LambdaQueryWrapper<PzcActivityConnIntro> lqw = Wrappers.lambdaQuery();\n            lqw.in(PzcActivityConnIntro::getActivityId, ids);\n            pzcActivityConnIntroMapper.delete(lqw);\n\n            LambdaQueryWrapper<PzcActivityConnArtist> lqw2 = Wrappers.lambdaQuery();\n            lqw2.in(PzcActivityConnArtist::getActivityId, ids);\n            pzcActivityConnArtistMapper.delete(lqw2);\n\n            LambdaQueryWrapper<PzcActivityConnTag> lqw3 = Wrappers.lambdaQuery();\n            lqw3.in(PzcActivityConnTag::getActivityId, ids);\n            pzcActivityConnTagMapper.delete(lqw3);\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcArtistServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.system.common.BatchUtils;\nimport top.flya.system.domain.bo.PzcArtistBo;\nimport top.flya.system.domain.vo.PzcArtistVo;\nimport top.flya.system.domain.PzcArtist;\nimport top.flya.system.mapper.PzcArtistMapper;\nimport top.flya.system.service.IPzcArtistService;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Collection;\n\n\n/**\n * 艺人Service业务层处理\n *\n * @author flya\n * @date 2023-06-01\n */\n@RequiredArgsConstructor\n@Service\n@Slf4j\npublic class PzcArtistServiceImpl implements IPzcArtistService {\n\n    private final PzcArtistMapper baseMapper;\n\n    private final BatchUtils batchUtils;\n\n\n    /**\n     * 查询艺人\n     */\n    @Override\n    public PzcArtistVo queryById(Long artistId){\n        return baseMapper.selectVoById(artistId);\n    }\n\n    /**\n     * 查询艺人列表\n     */\n    @Override\n    public TableDataInfo<PzcArtistVo> queryPageList(PzcArtistBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcArtist> lqw = buildQueryWrapper(bo);\n        Page<PzcArtistVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        result.setRecords(batchUtils.transformToPzcArtistVo(result.getRecords()));\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询艺人列表\n     */\n    @Override\n    public List<PzcArtistVo> queryList(PzcArtistBo bo) {\n        LambdaQueryWrapper<PzcArtist> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcArtist> buildQueryWrapper(PzcArtistBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcArtist> lqw = Wrappers.lambdaQuery();\n        lqw.like(StringUtils.isNotBlank(bo.getName()), PzcArtist::getName, bo.getName());\n        lqw.eq(StringUtils.isNotBlank(bo.getImageUrl()), PzcArtist::getImageUrl, bo.getImageUrl());\n        lqw.eq(StringUtils.isNotBlank(bo.getDescription()), PzcArtist::getDescription, bo.getDescription());\n        return lqw;\n    }\n\n    /**\n     * 新增艺人\n     */\n    @Override\n    public Boolean insertByBo(PzcArtistBo bo) {\n        PzcArtist add = BeanUtil.toBean(bo, PzcArtist.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setArtistId(add.getArtistId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改艺人\n     */\n    @Override\n    public Boolean updateByBo(PzcArtistBo bo) {\n        PzcArtist update = BeanUtil.toBean(bo, PzcArtist.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcArtist entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除艺人\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcIntroServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.common.BatchUtils;\nimport top.flya.system.domain.PzcIntro;\nimport top.flya.system.domain.bo.PzcIntroBo;\nimport top.flya.system.domain.vo.PzcIntroVo;\nimport top.flya.system.mapper.PzcIntroMapper;\nimport top.flya.system.service.IPzcIntroService;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 活动介绍Service业务层处理\n *\n * @author ruoyi\n * @date 2023-08-04\n */\n@RequiredArgsConstructor\n@Service\npublic class PzcIntroServiceImpl implements IPzcIntroService {\n\n    private final PzcIntroMapper baseMapper;\n\n    private final BatchUtils batchUtils;\n    /**\n     * 查询活动介绍\n     */\n    @Override\n    public PzcIntroVo queryById(Long introId){\n        return baseMapper.selectVoById(introId);\n    }\n\n    /**\n     * 查询活动介绍列表\n     */\n    @Override\n    public TableDataInfo<PzcIntroVo> queryPageList(PzcIntroBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcIntro> lqw = buildQueryWrapper(bo);\n        Page<PzcIntroVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        result.setRecords(batchUtils.transformToPzcIntroVo(result.getRecords()));\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询活动介绍列表\n     */\n    @Override\n    public List<PzcIntroVo> queryList(PzcIntroBo bo) {\n        LambdaQueryWrapper<PzcIntro> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcIntro> buildQueryWrapper(PzcIntroBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcIntro> lqw = Wrappers.lambdaQuery();\n        lqw.eq(StringUtils.isNotBlank(bo.getTitle()), PzcIntro::getTitle, bo.getTitle());\n        lqw.eq(StringUtils.isNotBlank(bo.getContent()), PzcIntro::getContent, bo.getContent());\n        lqw.eq(bo.getType() != null, PzcIntro::getType, bo.getType());\n        lqw.eq(StringUtils.isNotBlank(bo.getImageFullUrl()), PzcIntro::getImageFullUrl, bo.getImageFullUrl());\n        lqw.eq(bo.getCreateTime() != null, PzcIntro::getCreateTime, bo.getCreateTime());\n        lqw.eq(bo.getUpdateTime() != null, PzcIntro::getUpdateTime, bo.getUpdateTime());\n        return lqw;\n    }\n\n    /**\n     * 新增活动介绍\n     */\n    @Override\n    public Boolean insertByBo(PzcIntroBo bo) {\n        PzcIntro add = BeanUtil.toBean(bo, PzcIntro.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setIntroId(add.getIntroId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改活动介绍\n     */\n    @Override\n    public Boolean updateByBo(PzcIntroBo bo) {\n        PzcIntro update = BeanUtil.toBean(bo, PzcIntro.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcIntro entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除活动介绍\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcOfficialServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.system.domain.PzcOfficial;\nimport top.flya.system.domain.bo.PzcOfficialBo;\nimport top.flya.system.domain.vo.PzcOfficialVo;\nimport top.flya.system.mapper.PzcOfficialMapper;\nimport top.flya.system.service.IPzcOfficialService;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 官方消息Service业务层处理\n *\n * @author ruoyi\n * @date 2023-07-27\n */\n@RequiredArgsConstructor\n@Service\npublic class PzcOfficialServiceImpl implements IPzcOfficialService {\n\n    private final PzcOfficialMapper baseMapper;\n\n    /**\n     * 查询官方消息\n     */\n    @Override\n    public PzcOfficialVo queryById(Long officialId){\n        return baseMapper.selectVoById(officialId);\n    }\n\n    /**\n     * 查询官方消息列表\n     */\n    @Override\n    public TableDataInfo<PzcOfficialVo> queryPageList(PzcOfficialBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcOfficial> lqw = buildQueryWrapper(bo);\n        lqw.eq(true,PzcOfficial::getIsRead,0L);\n        Page<PzcOfficialVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询官方消息列表\n     */\n    @Override\n    public List<PzcOfficialVo> queryList(PzcOfficialBo bo) {\n        LambdaQueryWrapper<PzcOfficial> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcOfficial> buildQueryWrapper(PzcOfficialBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcOfficial> lqw = Wrappers.lambdaQuery();\n        lqw.eq(bo.getFromUserId() != null, PzcOfficial::getFromUserId, bo.getFromUserId());\n        lqw.in(bo.getToUserId() != null, PzcOfficial::getToUserId, bo.getToUserId(),0); //官方消息 和给我的消息\n        lqw.eq(StringUtils.isNotBlank(bo.getTitle()), PzcOfficial::getTitle, bo.getTitle());\n        lqw.eq(StringUtils.isNotBlank(bo.getContent()), PzcOfficial::getContent, bo.getContent());\n        lqw.eq(bo.getIsRead() != null, PzcOfficial::getIsRead, bo.getIsRead());\n        lqw.eq(bo.getGroupId() != null, PzcOfficial::getGroupId, bo.getGroupId());\n        lqw.eq(bo.getActivityId() != null, PzcOfficial::getActivityId, bo.getActivityId());\n        lqw.eq(bo.getCreateTime() != null, PzcOfficial::getCreateTime, bo.getCreateTime());\n        lqw.eq(bo.getUpdateTime() != null, PzcOfficial::getUpdateTime, bo.getUpdateTime());\n        return lqw;\n    }\n\n    /**\n     * 新增官方消息\n     */\n    @Override\n    public Boolean insertByBo(PzcOfficialBo bo) {\n        PzcOfficial add = BeanUtil.toBean(bo, PzcOfficial.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setOfficialId(add.getOfficialId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改官方消息\n     */\n    @Override\n    public Boolean updateByBo(PzcOfficialBo bo) {\n        PzcOfficial update = BeanUtil.toBean(bo, PzcOfficial.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcOfficial entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除官方消息\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n\n\n    @Override\n    public Integer read(Integer officialId) {\n        if(officialId==null)\n        {\n            UpdateWrapper<PzcOfficial> set = new UpdateWrapper<PzcOfficial>().eq(\"to_user_id\", LoginHelper.getUserId()).set(\"is_read\", 1);\n            return baseMapper.update(new PzcOfficial(),set);\n        }else {\n            PzcOfficial pzcOfficial = baseMapper.selectById(officialId);\n            pzcOfficial.setIsRead(1L);\n            return baseMapper.updateById(pzcOfficial);\n        }\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcOrderServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.system.domain.bo.PzcOrderBo;\nimport top.flya.system.domain.vo.PzcOrderVo;\nimport top.flya.system.domain.PzcOrder;\nimport top.flya.system.mapper.PzcOrderMapper;\nimport top.flya.system.service.IPzcOrderService;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Collection;\n\n/**\n * 订单Service业务层处理\n *\n * @author ruoyi\n * @date 2023-07-09\n */\n@RequiredArgsConstructor\n@Service\npublic class PzcOrderServiceImpl implements IPzcOrderService {\n\n    private final PzcOrderMapper baseMapper;\n\n    /**\n     * 查询订单\n     */\n    @Override\n    public PzcOrderVo queryById(Long orderId){\n        return baseMapper.selectVoById(orderId);\n    }\n\n    /**\n     * 查询订单列表\n     */\n    @Override\n    public TableDataInfo<PzcOrderVo> queryPageList(PzcOrderBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcOrder> lqw = buildQueryWrapper(bo);\n        Page<PzcOrderVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询订单列表\n     */\n    @Override\n    public List<PzcOrderVo> queryList(PzcOrderBo bo) {\n        LambdaQueryWrapper<PzcOrder> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcOrder> buildQueryWrapper(PzcOrderBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcOrder> lqw = Wrappers.lambdaQuery();\n        lqw.eq(bo.getUserId() != null, PzcOrder::getUserId, bo.getUserId());\n        lqw.eq(bo.getActivityId() != null, PzcOrder::getActivityId, bo.getActivityId());\n        lqw.eq(bo.getMoney() != null, PzcOrder::getMoney, bo.getMoney());\n        lqw.eq(bo.getOrderStatus() != null, PzcOrder::getOrderStatus, bo.getOrderStatus());\n        lqw.eq(bo.getType() != null, PzcOrder::getType, bo.getType());\n        lqw.eq(StringUtils.isNotBlank(bo.getOutOrderNum()), PzcOrder::getOutOrderNum, bo.getOutOrderNum());\n        lqw.eq(StringUtils.isNotBlank(bo.getIntro()), PzcOrder::getIntro, bo.getIntro());\n        lqw.eq(StringUtils.isNotBlank(bo.getTitle()), PzcOrder::getTitle, bo.getTitle());\n        lqw.eq(bo.getCreateTime() != null, PzcOrder::getCreateTime, bo.getCreateTime());\n        lqw.eq(bo.getUpdateTime() != null, PzcOrder::getUpdateTime, bo.getUpdateTime());\n        return lqw;\n    }\n\n    /**\n     * 新增订单\n     */\n    @Override\n    public Boolean insertByBo(PzcOrderBo bo) {\n        PzcOrder add = BeanUtil.toBean(bo, PzcOrder.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setOrderId(add.getOrderId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改订单\n     */\n    @Override\n    public Boolean updateByBo(PzcOrderBo bo) {\n        PzcOrder update = BeanUtil.toBean(bo, PzcOrder.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcOrder entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除订单\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcOrganizerServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.system.common.BatchUtils;\nimport top.flya.system.domain.bo.PzcOrganizerBo;\nimport top.flya.system.domain.vo.PzcOrganizerVo;\nimport top.flya.system.domain.PzcOrganizer;\nimport top.flya.system.mapper.PzcOrganizerMapper;\nimport top.flya.system.service.IPzcOrganizerService;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Collection;\n\n/**\n * 活动主办方Service业务层处理\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@RequiredArgsConstructor\n@Service\npublic class PzcOrganizerServiceImpl implements IPzcOrganizerService {\n\n    private final PzcOrganizerMapper baseMapper;\n    private final BatchUtils batchUtils;\n\n    /**\n     * 查询活动主办方\n     */\n    @Override\n    public PzcOrganizerVo queryById(Long organizerId){\n        return baseMapper.selectVoById(organizerId);\n    }\n\n    /**\n     * 查询活动主办方列表\n     */\n    @Override\n    public TableDataInfo<PzcOrganizerVo> queryPageList(PzcOrganizerBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcOrganizer> lqw = buildQueryWrapper(bo);\n        Page<PzcOrganizerVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        result.setRecords(batchUtils.transformToPzcOrganizerVo(result.getRecords()));\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询活动主办方列表\n     */\n    @Override\n    public List<PzcOrganizerVo> queryList(PzcOrganizerBo bo) {\n        LambdaQueryWrapper<PzcOrganizer> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcOrganizer> buildQueryWrapper(PzcOrganizerBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcOrganizer> lqw = Wrappers.lambdaQuery();\n        lqw.eq(StringUtils.isNotBlank(bo.getPhone()), PzcOrganizer::getPhone, bo.getPhone());\n        lqw.like(StringUtils.isNotBlank(bo.getName()), PzcOrganizer::getName, bo.getName());\n        lqw.eq(StringUtils.isNotBlank(bo.getLogo()), PzcOrganizer::getLogo, bo.getLogo());\n        lqw.eq(StringUtils.isNotBlank(bo.getContent()), PzcOrganizer::getContent, bo.getContent());\n        return lqw;\n    }\n\n    /**\n     * 新增活动主办方\n     */\n    @Override\n    public Boolean insertByBo(PzcOrganizerBo bo) {\n        PzcOrganizer add = BeanUtil.toBean(bo, PzcOrganizer.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setOrganizerId(add.getOrganizerId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改活动主办方\n     */\n    @Override\n    public Boolean updateByBo(PzcOrganizerBo bo) {\n        PzcOrganizer update = BeanUtil.toBean(bo, PzcOrganizer.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcOrganizer entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除活动主办方\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcOrganizerTicketServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.domain.PzcOrganizerTicket;\nimport top.flya.system.domain.bo.PzcOrganizerTicketBo;\nimport top.flya.system.domain.vo.PzcOrganizerTicketVo;\nimport top.flya.system.mapper.PzcOrganizerTicketMapper;\nimport top.flya.system.service.IPzcOrganizerTicketService;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 主办方票务Service业务层处理\n *\n * @author ruoyi\n * @date 2023-07-22\n */\n@RequiredArgsConstructor\n@Service\npublic class PzcOrganizerTicketServiceImpl implements IPzcOrganizerTicketService {\n\n    private final PzcOrganizerTicketMapper baseMapper;\n\n    /**\n     * 查询主办方票务\n     */\n    @Override\n    public PzcOrganizerTicketVo queryById(Long organizerTicketId){\n        return baseMapper.selectVoById(organizerTicketId);\n    }\n\n    /**\n     * 查询主办方票务列表\n     */\n    @Override\n    public TableDataInfo<PzcOrganizerTicketVo> queryPageList(PzcOrganizerTicketBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcOrganizerTicket> lqw = buildQueryWrapper(bo);\n        Page<PzcOrganizerTicketVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询主办方票务列表\n     */\n    @Override\n    public List<PzcOrganizerTicketVo> queryList(PzcOrganizerTicketBo bo) {\n        LambdaQueryWrapper<PzcOrganizerTicket> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcOrganizerTicket> buildQueryWrapper(PzcOrganizerTicketBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcOrganizerTicket> lqw = Wrappers.lambdaQuery();\n        lqw.like(StringUtils.isNotBlank(bo.getName()), PzcOrganizerTicket::getName, bo.getName());\n        lqw.eq(StringUtils.isNotBlank(bo.getQrImage()), PzcOrganizerTicket::getQrImage, bo.getQrImage());\n        lqw.eq(StringUtils.isNotBlank(bo.getLogoImage()), PzcOrganizerTicket::getLogoImage, bo.getLogoImage());\n        lqw.eq(bo.getOrganizerId() != null, PzcOrganizerTicket::getOrganizerId, bo.getOrganizerId());\n        return lqw;\n    }\n\n    /**\n     * 新增主办方票务\n     */\n    @Override\n    public Boolean insertByBo(PzcOrganizerTicketBo bo) {\n        PzcOrganizerTicket add = BeanUtil.toBean(bo, PzcOrganizerTicket.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setOrganizerTicketId(add.getOrganizerTicketId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改主办方票务\n     */\n    @Override\n    public Boolean updateByBo(PzcOrganizerTicketBo bo) {\n        PzcOrganizerTicket update = BeanUtil.toBean(bo, PzcOrganizerTicket.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcOrganizerTicket entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除主办方票务\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcRegionServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.domain.PzcRegion;\nimport top.flya.system.domain.bo.PzcRegionBo;\nimport top.flya.system.domain.vo.PzcRegionVo;\nimport top.flya.system.mapper.PzcRegionMapper;\nimport top.flya.system.service.IPzcRegionService;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 地区Service业务层处理\n *\n * @author ruoyi\n * @date 2023-07-22\n */\n@RequiredArgsConstructor\n@Service\npublic class PzcRegionServiceImpl implements IPzcRegionService {\n\n    private final PzcRegionMapper baseMapper;\n\n    /**\n     * 查询地区\n     */\n    @Override\n    public PzcRegionVo queryById(Long regionId){\n        return baseMapper.selectVoById(regionId);\n    }\n\n    /**\n     * 查询地区列表\n     */\n    @Override\n    public TableDataInfo<PzcRegionVo> queryPageList(PzcRegionBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcRegion> lqw = buildQueryWrapper(bo);\n        Page<PzcRegionVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(result);\n    }\n\n\n    /**\n     *  不分页全查\n     */\n\n    /**\n     * 查询地区列表\n     */\n    @Override\n    public List<PzcRegionVo> queryList(PzcRegionBo bo) {\n        LambdaQueryWrapper<PzcRegion> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcRegion> buildQueryWrapper(PzcRegionBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcRegion> lqw = Wrappers.lambdaQuery();\n        lqw.eq(StringUtils.isNotBlank(bo.getBase()), PzcRegion::getBase, bo.getBase());\n        lqw.like(StringUtils.isNotBlank(bo.getName()), PzcRegion::getName, bo.getName());\n        lqw.eq(StringUtils.isNotBlank(bo.getImgUrl()), PzcRegion::getImgUrl, bo.getImgUrl());\n        lqw.eq(bo.getCreateTime() != null, PzcRegion::getCreateTime, bo.getCreateTime());\n        lqw.eq(bo.getUpdateTime() != null, PzcRegion::getUpdateTime, bo.getUpdateTime());\n        return lqw;\n    }\n\n    /**\n     * 新增地区\n     */\n    @Override\n    public Boolean insertByBo(PzcRegionBo bo) {\n        PzcRegion add = BeanUtil.toBean(bo, PzcRegion.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setRegionId(add.getRegionId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改地区\n     */\n    @Override\n    public Boolean updateByBo(PzcRegionBo bo) {\n        PzcRegion update = BeanUtil.toBean(bo, PzcRegion.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcRegion entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除地区\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcTagServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.system.common.BatchUtils;\nimport top.flya.system.domain.bo.PzcTagBo;\nimport top.flya.system.domain.vo.PzcTagVo;\nimport top.flya.system.domain.PzcTag;\nimport top.flya.system.mapper.PzcTagMapper;\nimport top.flya.system.service.IPzcTagService;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Collection;\n\n/**\n * 活动标签Service业务层处理\n *\n * @author ruoyi\n * @date 2023-06-02\n */\n@RequiredArgsConstructor\n@Service\npublic class PzcTagServiceImpl implements IPzcTagService {\n\n    private final PzcTagMapper baseMapper;\n    private final BatchUtils batchUtils;\n\n    /**\n     * 查询活动标签\n     */\n    @Override\n    public PzcTagVo queryById(Long tagId){\n        return baseMapper.selectVoById(tagId);\n    }\n\n    /**\n     * 查询活动标签列表\n     */\n    @Override\n    public TableDataInfo<PzcTagVo> queryPageList(PzcTagBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcTag> lqw = buildQueryWrapper(bo);\n        Page<PzcTagVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        result.setRecords(batchUtils.transformToPzcTagVo(result.getRecords()));\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询活动标签列表\n     */\n    @Override\n    public List<PzcTagVo> queryList(PzcTagBo bo) {\n        LambdaQueryWrapper<PzcTag> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcTag> buildQueryWrapper(PzcTagBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcTag> lqw = Wrappers.lambdaQuery();\n        lqw.like(StringUtils.isNotBlank(bo.getName()), PzcTag::getName, bo.getName());\n        lqw.eq(StringUtils.isNotBlank(bo.getImageUrl()), PzcTag::getImageUrl, bo.getImageUrl());\n        lqw.eq(bo.getCreateTime() != null, PzcTag::getCreateTime, bo.getCreateTime());\n        lqw.eq(bo.getUpdateTime() != null, PzcTag::getUpdateTime, bo.getUpdateTime());\n        return lqw;\n    }\n\n    /**\n     * 新增活动标签\n     */\n    @Override\n    public Boolean insertByBo(PzcTagBo bo) {\n        PzcTag add = BeanUtil.toBean(bo, PzcTag.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setTagId(add.getTagId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改活动标签\n     */\n    @Override\n    public Boolean updateByBo(PzcTagBo bo) {\n        PzcTag update = BeanUtil.toBean(bo, PzcTag.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcTag entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除活动标签\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcUserCollectServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.system.common.BatchUtils;\nimport top.flya.system.domain.bo.PzcUserCollectBo;\nimport top.flya.system.domain.vo.PzcUserCollectVo;\nimport top.flya.system.domain.PzcUserCollect;\nimport top.flya.system.mapper.PzcActivityMapper;\nimport top.flya.system.mapper.PzcUserCollectMapper;\nimport top.flya.system.service.IPzcUserCollectService;\n\nimport java.util.*;\n\n/**\n * 用户收藏活动Service业务层处理\n *\n * @author ruoyi\n * @date 2023-07-08\n */\n@RequiredArgsConstructor\n@Service\n@Slf4j\npublic class PzcUserCollectServiceImpl implements IPzcUserCollectService {\n\n    private final PzcUserCollectMapper baseMapper;\n\n    private final PzcActivityMapper pzcActivityMapper;\n\n\n    private final BatchUtils batchUtils;\n    /**\n     * 查询用户收藏活动\n     */\n    @Override\n    public PzcUserCollectVo queryById(Long collectId){\n        return baseMapper.selectVoById(collectId);\n    }\n\n    /**\n     * 查询用户收藏活动列表\n     */\n    @Override\n    public TableDataInfo<PzcUserCollectVo> queryPageList(PzcUserCollectBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcUserCollect> lqw = buildQueryWrapper(bo);\n        Page<PzcUserCollectVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        List<PzcUserCollectVo> records = new ArrayList<>();\n\n\n            result.getRecords().forEach(\n                vo -> {\n                    vo.setActivity(pzcActivityMapper.selectById(vo.getActivityId()));\n\n                    if(vo.getActivity()!=null)\n                    {\n                        log.info(\"qaq: {} vo.getActivity().getClassify() is {} bo.getType() is {}\",vo.getActivity().getClassify().equals(bo.getType()),vo.getActivity().getClassify(),bo.getType());\n                        if(vo.getActivity().getClassify().equals(bo.getType()))\n                        {\n                            log.info(\"vo.getActivity() is:\"+vo.getActivity().getClassify());\n                            vo.getActivity().setCoverImage(vo.getActivity().getCoverImage().contains(\"http\")?vo.getActivity().getCoverImage(): batchUtils.getNewImageUrls(Collections.singletonList(vo.getActivity().getCoverImage())).get(Long.parseLong(vo.getActivity().getCoverImage())));\n//                            vo.getActivity().setShareImage(vo.getActivity().getShareImage().contains(\"http\")?vo.getActivity().getShareImage(): batchUtils.getNewImageUrls(Collections.singletonList(vo.getActivity().getShareImage())).get(Long.parseLong(vo.getActivity().getShareImage())));\n                            records.add(vo);\n                        }\n\n                    }\n                }\n            );\n\n        return TableDataInfo.build(records);\n    }\n\n    /**\n     * 查询用户收藏活动列表\n     */\n    @Override\n    public List<PzcUserCollectVo> queryList(PzcUserCollectBo bo) {\n        LambdaQueryWrapper<PzcUserCollect> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcUserCollect> buildQueryWrapper(PzcUserCollectBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcUserCollect> lqw = Wrappers.lambdaQuery();\n        lqw.eq(bo.getUserId() != null, PzcUserCollect::getUserId, bo.getUserId());\n        lqw.eq(bo.getActivityId() != null, PzcUserCollect::getActivityId, bo.getActivityId());\n        return lqw;\n    }\n\n    /**\n     * 新增用户收藏活动\n     */\n    @Override\n    public Boolean insertByBo(PzcUserCollectBo bo) {\n        PzcUserCollect add = BeanUtil.toBean(bo, PzcUserCollect.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setCollectId(add.getCollectId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改用户收藏活动\n     */\n    @Override\n    public Boolean updateByBo(PzcUserCollectBo bo) {\n        PzcUserCollect update = BeanUtil.toBean(bo, PzcUserCollect.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcUserCollect entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除用户收藏活动\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcUserHistoryServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.common.utils.DateUtils;\nimport top.flya.system.domain.bo.PzcUserHistoryBo;\nimport top.flya.system.domain.vo.PzcUserHistoryVo;\nimport top.flya.system.domain.PzcUserHistory;\nimport top.flya.system.mapper.PzcUserHistoryMapper;\nimport top.flya.system.service.IPzcUserHistoryService;\n\nimport java.text.ParseException;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Collection;\n\n/**\n * 用户操作历史记录Service业务层处理\n *\n * @author ruoyi\n * @date 2023-07-06\n */\n@RequiredArgsConstructor\n@Service\npublic class PzcUserHistoryServiceImpl implements IPzcUserHistoryService {\n\n    private final PzcUserHistoryMapper baseMapper;\n\n    /**\n     * 查询用户操作历史记录\n     */\n    @Override\n    public PzcUserHistoryVo queryById(Long historyId){\n        return baseMapper.selectVoById(historyId);\n    }\n\n    /**\n     * 查询用户操作历史记录列表\n     */\n    @Override\n    public TableDataInfo<PzcUserHistoryVo> queryPageList(PzcUserHistoryBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcUserHistory> lqw = buildQueryWrapper(bo);\n        Page<PzcUserHistoryVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询用户操作历史记录列表\n     */\n    @Override\n    public List<PzcUserHistoryVo> queryList(PzcUserHistoryBo bo) {\n        LambdaQueryWrapper<PzcUserHistory> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcUserHistory> buildQueryWrapper(PzcUserHistoryBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcUserHistory> lqw = Wrappers.lambdaQuery();\n        lqw.eq(bo.getUserId() != null, PzcUserHistory::getUserId, bo.getUserId());\n        lqw.eq(bo.getActivityId() != null, PzcUserHistory::getActivityId, bo.getActivityId());\n        lqw.in(bo.getType() != null, PzcUserHistory::getType, bo.getType());\n        lqw.eq(StringUtils.isNotBlank(bo.getMessage()), PzcUserHistory::getMessage, bo.getMessage());\n        //获取本月开始时间和结束时间\n        if(StringUtils.isNotBlank(bo.getNowTime())&&bo.getNowTime().length()>0)\n        {\n            try {\n                lqw.between(true, PzcUserHistory::getCreateTime,DateUtils.getMonthStartAndEnd(bo.getNowTime())[0], DateUtils.getMonthStartAndEnd(bo.getNowTime())[1]);\n            } catch (ParseException e) {\n                throw new RuntimeException(e);\n            }\n        }\n\n        return lqw;\n    }\n\n    /**\n     * 新增用户操作历史记录\n     */\n    @Override\n    public Boolean insertByBo(PzcUserHistoryBo bo) {\n        PzcUserHistory add = BeanUtil.toBean(bo, PzcUserHistory.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setHistoryId(add.getHistoryId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改用户操作历史记录\n     */\n    @Override\n    public Boolean updateByBo(PzcUserHistoryBo bo) {\n        PzcUserHistory update = BeanUtil.toBean(bo, PzcUserHistory.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcUserHistory entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除用户操作历史记录\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcUserPhotoServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.system.domain.bo.PzcUserPhotoBo;\nimport top.flya.system.domain.vo.PzcUserPhotoVo;\nimport top.flya.system.domain.PzcUserPhoto;\nimport top.flya.system.mapper.PzcUserPhotoMapper;\nimport top.flya.system.service.IPzcUserPhotoService;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Collection;\n\n/**\n * 用户资料相册Service业务层处理\n *\n * @author ruoyi\n * @date 2023-07-11\n */\n@RequiredArgsConstructor\n@Service\npublic class PzcUserPhotoServiceImpl implements IPzcUserPhotoService {\n\n    private final PzcUserPhotoMapper baseMapper;\n\n    /**\n     * 查询用户资料相册\n     */\n    @Override\n    public PzcUserPhotoVo queryById(Long photoId){\n        return baseMapper.selectVoById(photoId);\n    }\n\n    /**\n     * 查询用户资料相册列表\n     */\n    @Override\n    public TableDataInfo<PzcUserPhotoVo> queryPageList(PzcUserPhotoBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcUserPhoto> lqw = buildQueryWrapper(bo);\n        Page<PzcUserPhotoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询用户资料相册列表\n     */\n    @Override\n    public List<PzcUserPhotoVo> queryList(PzcUserPhotoBo bo) {\n        LambdaQueryWrapper<PzcUserPhoto> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcUserPhoto> buildQueryWrapper(PzcUserPhotoBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcUserPhoto> lqw = Wrappers.lambdaQuery();\n        lqw.eq(bo.getUserId() != null, PzcUserPhoto::getUserId, bo.getUserId());\n        lqw.eq(StringUtils.isNotBlank(bo.getUrl()), PzcUserPhoto::getUrl, bo.getUrl());\n        lqw.eq(StringUtils.isNotBlank(bo.getMessage()), PzcUserPhoto::getMessage, bo.getMessage());\n        lqw.eq(bo.getCreateTime() != null, PzcUserPhoto::getCreateTime, bo.getCreateTime());\n        lqw.eq(bo.getUpdateTime() != null, PzcUserPhoto::getUpdateTime, bo.getUpdateTime());\n        return lqw;\n    }\n\n    /**\n     * 新增用户资料相册\n     */\n    @Override\n    public Boolean insertByBo(PzcUserPhotoBo bo) {\n        PzcUserPhoto add = BeanUtil.toBean(bo, PzcUserPhoto.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setPhotoId(add.getPhotoId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改用户资料相册\n     */\n    @Override\n    public Boolean updateByBo(PzcUserPhotoBo bo) {\n        PzcUserPhoto update = BeanUtil.toBean(bo, PzcUserPhoto.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcUserPhoto entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除用户资料相册\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcUserServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.common.BatchUtils;\nimport top.flya.system.domain.PzcUser;\nimport top.flya.system.domain.bo.PzcUserBo;\nimport top.flya.system.domain.bo.UpdateMoneyBo;\nimport top.flya.system.domain.vo.PzcUserVo;\nimport top.flya.system.mapper.PzcUserMapper;\nimport top.flya.system.service.IPzcUserService;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 用户Service业务层处理\n *\n * @author ruoyi\n * @date 2023-07-09\n */\n@RequiredArgsConstructor\n@Service\npublic class PzcUserServiceImpl implements IPzcUserService {\n\n    private final PzcUserMapper baseMapper;\n\n    private final BatchUtils batchUtils;\n\n    /**\n     * 查询用户\n     */\n    @Override\n    public PzcUserVo queryById(Long userId){\n        return baseMapper.selectVoById(userId);\n    }\n\n    /**\n     * 查询用户列表\n     */\n    @Override\n    public TableDataInfo<PzcUserVo> queryPageList(PzcUserBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcUser> lqw = buildQueryWrapper(bo);\n        Page<PzcUserVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        result.setRecords(batchUtils.transformToPzcUserVo(result.getRecords()));\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询用户列表\n     */\n    @Override\n    public List<PzcUserVo> queryList(PzcUserBo bo) {\n        LambdaQueryWrapper<PzcUser> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcUser> buildQueryWrapper(PzcUserBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcUser> lqw = Wrappers.lambdaQuery();\n        lqw.eq(StringUtils.isNotBlank(bo.getOpenid()), PzcUser::getOpenid, bo.getOpenid());\n        lqw.eq(bo.getMoney() != null, PzcUser::getMoney, bo.getMoney());\n        lqw.eq(bo.getUserLevel() != null, PzcUser::getUserLevel, bo.getUserLevel());\n        lqw.eq(bo.getIntegration() != null, PzcUser::getIntegration, bo.getIntegration());\n        lqw.eq(bo.getIntegrationNow() != null, PzcUser::getIntegrationNow, bo.getIntegrationNow());\n        lqw.like(StringUtils.isNotBlank(bo.getRealname()), PzcUser::getRealname, bo.getRealname());\n        lqw.like(StringUtils.isNotBlank(bo.getNickname()), PzcUser::getNickname, bo.getNickname());\n        lqw.eq(bo.getSex() != null, PzcUser::getSex, bo.getSex());\n        lqw.eq(StringUtils.isNotBlank(bo.getPhone()), PzcUser::getPhone, bo.getPhone());\n        lqw.eq(StringUtils.isNotBlank(bo.getAvatar()), PzcUser::getAvatar, bo.getAvatar());\n        lqw.eq(StringUtils.isNotBlank(bo.getAddress()), PzcUser::getAddress, bo.getAddress());\n        lqw.eq(StringUtils.isNotBlank(bo.getIntro()), PzcUser::getIntro, bo.getIntro());\n        lqw.eq(bo.getAge() != null, PzcUser::getAge, bo.getAge());\n        lqw.eq(StringUtils.isNotBlank(bo.getConstellation()), PzcUser::getConstellation, bo.getConstellation());\n        lqw.eq(StringUtils.isNotBlank(bo.getMbti()), PzcUser::getMbti, bo.getMbti());\n        lqw.eq(bo.getHobby() != null, PzcUser::getHobby, bo.getHobby());\n        lqw.eq(StringUtils.isNotBlank(bo.getSchool()), PzcUser::getSchool, bo.getSchool());\n        lqw.eq(StringUtils.isNotBlank(bo.getOccupation()), PzcUser::getOccupation, bo.getOccupation());\n        lqw.eq(StringUtils.isNotBlank(bo.getMusicStyle()), PzcUser::getMusicStyle, bo.getMusicStyle());\n        lqw.eq(bo.getState() != null, PzcUser::getState, bo.getState());\n        return lqw;\n    }\n\n    /**\n     * 新增用户\n     */\n    @Override\n    public Boolean insertByBo(PzcUserBo bo) {\n        PzcUser add = BeanUtil.toBean(bo, PzcUser.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setUserId(add.getUserId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改用户\n     */\n    @Override\n    public Boolean updateByBo(PzcUserBo bo) {\n        PzcUser update = BeanUtil.toBean(bo, PzcUser.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcUser entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除用户\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n\n    @Override\n    public int updateMoney(UpdateMoneyBo bo) {\n\n        return baseMapper.updateMoney(bo);\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcUserTalkServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.system.domain.PzcUser;\nimport top.flya.system.domain.PzcUserTalk;\nimport top.flya.system.domain.bo.PzcUserTalkBo;\nimport top.flya.system.domain.vo.PzcUserTalkVo;\nimport top.flya.system.mapper.PzcUserMapper;\nimport top.flya.system.mapper.PzcUserTalkMapper;\nimport top.flya.system.service.IPzcUserTalkService;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 用户聊天Service业务层处理\n *\n * @author ruoyi\n * @date 2023-07-16\n */\n@RequiredArgsConstructor\n@Service\n@Slf4j\npublic class PzcUserTalkServiceImpl implements IPzcUserTalkService {\n\n    private final PzcUserTalkMapper baseMapper;\n\n    private final PzcUserMapper pzcUserMapper;\n\n    /**\n     * 查询用户聊天\n     */\n    @Override\n    public PzcUserTalkVo queryById(Long talkId) {\n        return baseMapper.selectVoById(talkId);\n    }\n\n    /**\n     * 查询用户聊天列表\n     */\n    @Override\n    public TableDataInfo<PzcUserTalkVo> queryPageList(PzcUserTalkBo bo, PageQuery pageQuery) {\n        Page<PzcUserTalkVo> result = baseMapper.selectVoPageV2(pageQuery.build(), bo);\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询我 与朋友的聊天列表\n     *\n     * @param bo\n     * @param pageQuery\n     * @return\n     */\n    @Override\n    public TableDataInfo<PzcUserTalkVo> queryMyPageList(PzcUserTalkBo bo, PageQuery pageQuery) {\n        List<Long> userIds = baseMapper.selectMyTalkUserIds(LoginHelper.getUserId());\n        List<Long> userIds2 = baseMapper.selectMyTalkUserIdsV2(LoginHelper.getUserId());\n        List<PzcUserTalkVo> result =new ArrayList<>();\n        userIds.addAll(userIds2);\n\n        userIds=userIds.stream().distinct().collect(Collectors.toList());\n        log.info(\"聊天列表 对方 userIds:{}\", userIds);\n        userIds.forEach(\n            userId -> {\n                PzcUserTalkVo item = baseMapper.selectVoPageV1(userId, LoginHelper.getUserId());\n//                if(item==null)\n//                {\n//                    return; //TODO\n//                }\n                Long fromUserId = item.getFromUserId().equals(LoginHelper.getUserId()) ? item.getToUserId() : item.getFromUserId();\n                item.setNotReadCount(baseMapper.selectNotReadCount(fromUserId, LoginHelper.getUserId(),LoginHelper.getUserId())); //\n                PzcUser pzcUser = pzcUserMapper.selectById(Objects.equals(item.getToUserId(), LoginHelper.getUserId()) ? item.getFromUserId() : item.getToUserId());\n                item.setUsername(pzcUser.getNickname());\n                item.setAvatar(pzcUser.getAvatar());\n                item.setToUserId(item.getToUserId().equals(LoginHelper.getUserId()) ? item.getFromUserId() : item.getToUserId());\n                result.add(item);\n                }\n        );\n        List<PzcUserTalkVo> collect = result.stream().sorted(Comparator.comparing(PzcUserTalkVo::getCreateTime).reversed()).collect(Collectors.toList());\n\n        return TableDataInfo.build(collect);\n    }\n\n\n    /**\n     * 查询用户聊天列表\n     */\n    @Override\n    public List<PzcUserTalkVo> queryList(PzcUserTalkBo bo) {\n        LambdaQueryWrapper<PzcUserTalk> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcUserTalk> buildQueryWrapper(PzcUserTalkBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcUserTalk> lqw = Wrappers.lambdaQuery();\n        lqw.eq(bo.getFromUserId() != null, PzcUserTalk::getFromUserId, bo.getFromUserId());\n        lqw.eq(bo.getToUserId() != null, PzcUserTalk::getToUserId, bo.getToUserId());\n        lqw.eq(StringUtils.isNotBlank(bo.getMessage()), PzcUserTalk::getMessage, bo.getMessage());\n        lqw.eq(bo.getMessageStatus() != null, PzcUserTalk::getMessageStatus, bo.getMessageStatus());\n        lqw.eq(bo.getMessageType() != null, PzcUserTalk::getMessageType, bo.getMessageType());\n        lqw.eq(bo.getCreateTime() != null, PzcUserTalk::getCreateTime, bo.getCreateTime());\n        lqw.eq(bo.getUpdateTime() != null, PzcUserTalk::getUpdateTime, bo.getUpdateTime());\n        return lqw;\n    }\n\n    /**\n     * 新增用户聊天\n     */\n    @Override\n    public Boolean insertByBo(PzcUserTalkBo bo) {\n        PzcUserTalk add = BeanUtil.toBean(bo, PzcUserTalk.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setTalkId(add.getTalkId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改用户聊天\n     */\n    @Override\n    public Boolean updateByBo(PzcUserTalkBo bo) {\n        PzcUserTalk update = BeanUtil.toBean(bo, PzcUserTalk.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcUserTalk entity) {\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除用户聊天\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if (isValid) {\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcViewPagerServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport top.flya.system.common.BatchUtils;\nimport top.flya.system.domain.bo.PzcViewPagerBo;\nimport top.flya.system.domain.vo.PzcViewPagerVo;\nimport top.flya.system.domain.PzcViewPager;\nimport top.flya.system.mapper.PzcViewPagerMapper;\nimport top.flya.system.service.IPzcViewPagerService;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Collection;\n\n/**\n * 轮播图Service业务层处理\n *\n * @author ruoyi\n * @date 2023-05-23\n */\n@RequiredArgsConstructor\n@Service\npublic class PzcViewPagerServiceImpl implements IPzcViewPagerService {\n\n    private final PzcViewPagerMapper baseMapper;\n\n    private final BatchUtils batchUtils;\n\n    /**\n     * 查询轮播图\n     */\n    @Override\n    public PzcViewPagerVo queryById(Integer viewPagerId){\n        return baseMapper.selectVoById(viewPagerId);\n    }\n\n    /**\n     * 查询轮播图列表\n     */\n    @Override\n    public TableDataInfo<PzcViewPagerVo> queryPageList(PzcViewPagerBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<PzcViewPager> lqw = buildQueryWrapper(bo);\n        Page<PzcViewPagerVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        result.setRecords(batchUtils.transformToPzcViewPagerVo(result.getRecords()));\n        return TableDataInfo.build(result);\n    }\n\n    /**\n     * 查询轮播图列表\n     */\n    @Override\n    public List<PzcViewPagerVo> queryList(PzcViewPagerBo bo) {\n        LambdaQueryWrapper<PzcViewPager> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<PzcViewPager> buildQueryWrapper(PzcViewPagerBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<PzcViewPager> lqw = Wrappers.lambdaQuery();\n        lqw.like(StringUtils.isNotBlank(bo.getName()), PzcViewPager::getName, bo.getName());\n        lqw.eq(StringUtils.isNotBlank(bo.getImageUrl()), PzcViewPager::getImageUrl, bo.getImageUrl());\n        lqw.eq(StringUtils.isNotBlank(bo.getLinkUrl()), PzcViewPager::getLinkUrl, bo.getLinkUrl());\n        lqw.eq(bo.getState() != null, PzcViewPager::getState, bo.getState());\n        lqw.eq(bo.getActivityId() != null, PzcViewPager::getActivityId, bo.getActivityId());\n        return lqw;\n    }\n\n    /**\n     * 新增轮播图\n     */\n    @Override\n    public Boolean insertByBo(PzcViewPagerBo bo) {\n        PzcViewPager add = BeanUtil.toBean(bo, PzcViewPager.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n        if (flag) {\n            bo.setViewPagerId(add.getViewPagerId());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改轮播图\n     */\n    @Override\n    public Boolean updateByBo(PzcViewPagerBo bo) {\n        PzcViewPager update = BeanUtil.toBean(bo, PzcViewPager.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(PzcViewPager entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除轮播图\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Integer> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/ActivityUtils.java",
    "content": "package top.flya.system.utils;\n\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.DateUtils;\nimport top.flya.system.domain.vo.PzcActivityGroupVo;\nimport top.flya.system.domain.vo.PzcActivityVo;\nimport top.flya.system.mapper.PzcActivityGroupMapper;\nimport top.flya.system.mapper.PzcActivityMapper;\nimport top.flya.system.service.IPzcActivityGroupService;\nimport top.flya.system.service.IPzcActivityService;\n\nimport java.util.Date;\n\n@Component\n@Slf4j\n@RequiredArgsConstructor\npublic class ActivityUtils {\n\n    private final IPzcActivityService iPzcActivityService;\n\n    private final PzcActivityMapper pzcActivityMapper;\n\n    private final IPzcActivityGroupService iPzcActivityGroupService;\n\n    private final PzcActivityGroupMapper pzcActivityGroupMapper;\n\n\n    /**\n     * 检查活动相关问题  活动是否存在 活动是否结束\n     * @param activityId\n     * @return\n     */\n    public Boolean checkActivity(Integer activityId) {\n        log.info(\"checkActivity: activityId = {}\", activityId);\n        if(activityId==0)\n        {\n            return true;//城市活动 无需检查\n        }\n        PzcActivityVo pzcActivityVo = iPzcActivityService.queryById(activityId);\n        if (pzcActivityVo == null) {\n            log.error(\"活动不存在\");\n            throw  new RuntimeException(\"活动不存在\");\n        }\n        String endDate = pzcActivityVo.getEndDate();\n        Date now = new Date();\n        Date end = DateUtils.parseDate(endDate);\n        if(now.after(end))\n        {\n            log.error(\"活动已结束\");\n            throw  new RuntimeException(\"活动已结束\");\n        }\n        return true;\n    }\n\n    /**\n     * 检查活动组是否存在 和当前组是否在当前活动下\n     * @param groupId\n     * @return\n     */\n    public Boolean checkGroup(Integer activityId,Long groupId) {\n        log.info(\"checkGroup: groupId = {}\", groupId);\n        PzcActivityGroupVo pzcActivityGroupVo = iPzcActivityGroupService.queryById(groupId);\n        if(pzcActivityGroupVo == null) {\n            log.error(\"组队不存在\");\n           throw new RuntimeException(\"组队不存在\");\n        }\n        //不可以参与自己发起的组队\n        if(pzcActivityGroupVo.getUserId().equals(LoginHelper.getUserId()))\n        {\n            log.error(\"不可以参与自己发起的组队\");\n            throw new RuntimeException(\"不可以参与自己发起的组队\");\n        }\n\n        Long activityId1 = pzcActivityGroupVo.getActivityId();\n        if(activityId.equals(Math.toIntExact(activityId1)))\n        {\n            return true;\n        }else {\n            log.error(\"组队不在当前活动下\");\n            throw new RuntimeException(\"组队不在当前活动下\");\n        }\n\n    }\n\n\n    public Boolean allCheck(Integer activityId, Long groupId) {\n        return checkActivity(activityId) && checkGroup(activityId,groupId);\n    }\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/CreateSign.java",
    "content": "package top.flya.system.utils;\n\n/**\n * Created with IntelliJ IDEA.\n *\n * @author: 风离\n * @Date: 2022/06/04/14:06\n * @Description:\n */\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport java.security.Signature;\nimport java.util.Base64;\n\n@Slf4j\n@Component\npublic class CreateSign {\n\n\n    /**\n     * 作用：使用字段appId、timeStamp、nonceStr、package计算得出的签名值\n     * 场景：根据微信统一下单接口返回的 prepay_id 生成调启支付所需的签名值\n     * @param appId\n     * @param timestamp\n     * @param nonceStr\n     * @param pack package\n     * @return\n     * @throws Exception\n     */\n    public String getSign(String appId, long timestamp, String nonceStr, String pack) throws Exception{\n        String message = buildMessage(appId, timestamp, nonceStr, pack);\n        log.info(\"message: \\n\"+message);\n        log.info(\"======end======\");\n        String paySign= sign(message.getBytes(\"utf-8\"));\n        return paySign;\n    }\n\n    private String buildMessage(String appId, long timestamp, String nonceStr, String pack) {\n        return   appId + \"\\n\"\n                + timestamp + \"\\n\"\n                + nonceStr + \"\\n\"\n                + pack + \"\\n\";\n    }\n    private String sign(byte[] message) throws Exception{\n        Signature sign = Signature.getInstance(\"SHA256withRSA\");\n        sign.initSign(MyPrivateKey.getPrivateKey(\"apiclient_key.pem\"));\n        sign.update(message);\n\n        return Base64.getEncoder().encodeToString(sign.sign());\n    }\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/MyPrivateKey.java",
    "content": "package top.flya.system.utils;\n\n/**\n * Created with IntelliJ IDEA.\n *\n * @author: 风离\n * @Date: 2022/06/04/14:57\n * @Description:\n */\n\nimport com.wechat.pay.contrib.apache.httpclient.util.PemUtil;\nimport org.springframework.core.io.ClassPathResource;\n\nimport java.io.IOException;\nimport java.security.PrivateKey;\n\npublic class MyPrivateKey {\n    /**\n     * 获取私钥。\n     *  这是个静态方法，可以直接用类名调用\n     * @param filename 私钥文件路径  (required)\n     * @return 私钥对象\n     *\n     * 完全不需要修改，注意此方法也是去掉了头部和尾部，注意文件路径名\n     */\n    public static PrivateKey getPrivateKey(String filename) throws IOException {\n\n       ClassPathResource resource = new ClassPathResource(filename);\n\n        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(\n                resource.getInputStream());\n\n        return merchantPrivateKey;\n    }\n}\n\n\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/WxUtils.java",
    "content": "package top.flya.system.utils;\n\nimport cn.hutool.http.HttpUtil;\nimport cn.hutool.json.JSONUtil;\nimport com.alibaba.fastjson.JSONObject;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.system.domain.PzcOfficial;\nimport top.flya.system.domain.PzcUser;\nimport top.flya.system.domain.PzcUserHistory;\nimport top.flya.system.domain.vo.PzcActivityGroupApplyVo;\nimport top.flya.system.mapper.PzcOfficialMapper;\nimport top.flya.system.mapper.PzcUserHistoryMapper;\nimport top.flya.system.mapper.PzcUserMapper;\nimport top.flya.system.service.IPzcActivityGroupApplyService;\nimport top.flya.system.utils.sensitivewordsfiliter.WorldsFilterUtils;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.util.HashMap;\nimport java.util.Map;\n\n@Component\n@Slf4j\npublic class WxUtils {\n\n    @Value(\"${wx.appId}\")\n    private String appId;\n\n    @Value(\"${wx.appSecret}\")\n    private String secret;\n\n    @Resource\n    private PzcUserMapper userMapper;\n\n    @Resource\n    private PzcUserHistoryMapper userHistoryMapper;\n\n    @Resource\n    private IPzcActivityGroupApplyService iPzcActivityGroupApplyService;\n\n    @Resource\n    private PzcOfficialMapper pzcOfficialMapper;\n\n    public void checkMgc(String msg)\n    {\n        if(msg==null|| msg.isEmpty())\n        {\n            return;\n        }\n       if(WorldsFilterUtils.checkBySystemWords(msg))\n       {\n           throw new RuntimeException(\"输入内容包含敏感词汇,请重新输入\");\n       }\n    }\n\n\n\n    public R sendArriveMsg(String toUserOpenId, Map data) {\n        String getTokenUrl = \"https://api.weixin.qq.com/cgi-bin/token?\" + \"grant_type=client_credential&appid=\" + appId + \"&secret=\" + secret;\n        String response = HttpUtil.get(getTokenUrl);\n        log.info(\"微信小程序获取token url : {}，response is {}\", getTokenUrl, response);\n        JSONObject wxUser = JSONObject.parseObject(response);\n        if (com.baomidou.mybatisplus.core.toolkit.StringUtils.checkValNull(wxUser) || wxUser.get(\"errcode\") != null) {\n            throw new RuntimeException(\"微信登录失败 可能是code过期了\");\n        }\n        String accessToken = wxUser.get(\"access_token\").toString();\n        String msgUrl= \"https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=\"+accessToken;\n        Map<String, Object> map = new HashMap<>();\n        map.put(\"template_id\",\"MMHCiz9Z5faTwbDI9ywE0ScIvGMeDduTxXm00wdLxmw\");\n        map.put(\"touser\",toUserOpenId);\n        map.put(\"data\",data);\n        map.put(\"miniprogram_state\",\"formal\");//developer为开发版；trial为体验版；formal为正式版；默认为正式版\n        map.put(\"lang\",\"zh_CN\");\n        log.info(\"request is {}\",JSONUtil.toJsonStr(map));\n        String msgResponse = HttpUtil.post(msgUrl, JSONUtil.toJsonStr(map));\n        log.info(\"response is {}\",msgResponse);\n        JSONObject msgJson = JSONObject.parseObject(msgResponse);\n        if (msgJson.getInteger(\"errcode\") != 0) {\n            throw new RuntimeException(\"微信小程序推送消息失败\");\n        }\n\n        return R.ok(msgJson.get(\"errcode\").toString());\n    }\n\n\n    public PzcActivityGroupApplyVo  checkApplyConfirm(Long applyId)\n    {\n\n        //首先判断 这个applyId 的状态 以及是否存在\n        PzcActivityGroupApplyVo pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryById(applyId);\n        if(pzcActivityGroupApplyVo==null)\n        {\n            throw new RuntimeException(\"申请不存在\");\n//            return R.fail(\"申请不存在\");\n        }\n        Integer applyStatus = pzcActivityGroupApplyVo.getApplyStatus();\n        if(applyStatus==-1||applyStatus==0||applyStatus==3)\n        {\n            throw  new RuntimeException(\"该订单位于【\"+applyStatus(applyStatus)+\"】状态，不可确认\");\n        }\n\n\n        return pzcActivityGroupApplyVo;\n    }\n\n    public PzcActivityGroupApplyVo checkApplyPj(long applyId) {\n        //首先判断 这个applyId 的状态 以及是否存在\n        PzcActivityGroupApplyVo pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryById(applyId);\n        if(pzcActivityGroupApplyVo==null)\n        {\n            throw new RuntimeException(\"申请不存在\");\n        }\n        Integer applyStatus = pzcActivityGroupApplyVo.getApplyStatus();\n        //-1 已取消 0 位于申请列表中 1 申请通过待确认时\n        //2 确认通过进行中 3 组队结束  9发起方已确认\n        // 10申请方已确认 11 发起方已打卡 12 申请方已打卡\n        //13 申请方已评价 14 发起方已评价 15 双方已评价\n        if(applyStatus==3||applyStatus==13||applyStatus==14)\n        {\n            return pzcActivityGroupApplyVo;\n        }else {\n            throw  new RuntimeException(\"该订单位于【\"+applyStatus(applyStatus)+\"】状态，不可评价\");\n        }\n\n    }\n\n\n\n\n    public String applyStatus(Integer applyStatus)\n    {\n        if(applyStatus==-1)\n        {\n            return \"已取消\";\n        }\n        if(applyStatus==0)\n        {\n            return \"位于申请列表中\";\n        }\n        if(applyStatus==1)\n        {\n            return \"申请通过待确认\";\n        }\n        if(applyStatus==2)\n        {\n            return \"已确认，进行中\";\n        }\n        if(applyStatus==3)\n        {\n            return \"已完成\";\n        }\n        if(applyStatus==9)\n        {\n            return \"发起方已确认\";\n        }\n        if(applyStatus==10)\n        {\n            return \"申请方已确认\";\n        }\n        if(applyStatus==11)\n        {\n            return \"发起方已打卡\";\n        }\n        if (applyStatus==12)\n        {\n            return \"申请方已打卡\";\n        }\n        if (applyStatus==13)\n        {\n            return \"发起方已评价\";\n        }\n        if (applyStatus==14)\n        {\n            return \"申请方已评价\";\n        }\n        if (applyStatus==15)\n        {\n            return \"双方已评价\";\n        }\n\n        return null;\n    }\n\n    public String getAccessToken() {\n        String url = \"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=\" + appId +\n            \"&secret=\" + secret;\n        String result = HttpUtil.get(url);\n        return JSONUtil.parseObj(result).getStr(\"access_token\");\n    }\n    public PzcUser checkUser()\n    {\n        Long userId = LoginHelper.getUserId();\n        PzcUser user = userMapper.selectById(userId);\n        if (user == null || StringUtils.isEmpty(user.getOpenid())|| user.getState() == 0) {\n            throw new RuntimeException(\"用户不存在 或者已被禁用\");\n        }\n        return user;\n    }\n\n    public void insertPzcOfficialMsg(Long fromUserId,Long toUserId,String title,String content,Long groupId,Long activityId)\n    {\n        PzcOfficial pzcOfficial = new PzcOfficial();\n        pzcOfficial.setIsRead(0L);\n\n        pzcOfficial.setFromUserId(fromUserId);\n        pzcOfficial.setTitle(title);\n        pzcOfficial.setContent(content);\n        pzcOfficial.setToUserId(toUserId);\n        pzcOfficial.setGroupId(groupId);\n        pzcOfficial.setActivityId(activityId);\n        int insert = pzcOfficialMapper.insert(pzcOfficial);\n        log.info(\"插入官方消息条数：{}\\n内容为：{}\", insert,JSONUtil.toJsonPrettyStr(pzcOfficial));\n    }\n\n\n    public void insertUserHistory(Long userId, Long activityId, Long type, String message, BigDecimal money)\n    {\n        PzcUserHistory userHistory = new PzcUserHistory();\n        userHistory.setUserId(userId);\n        userHistory.setActivityId(activityId);\n        userHistory.setType(type);\n        userHistory.setMessage(message);\n        userHistory.setMoney(money);\n        int insert = userHistoryMapper.insert(userHistory);\n        log.info(\"插入用户历史记录 信息为： {} 条数为： {}\",message,insert);\n    }\n\n\n    public void checkApplyScore(Integer score) {\n        if(score!=0&&score!=3&&score!=-3)\n        {\n            throw new RuntimeException(\"评分只能为差评（-3 积分） 中评（+0 积分） 好评（+3 积分）\");\n        }\n    }\n\n    public void updateUserMsg(PzcUser otherUser) {\n        if(otherUser.getIntegrationNow()>=30)\n        {\n            otherUser.setUserLevel(2L);\n        }\n        if(otherUser.getIntegrationNow()>=70)\n        {\n            otherUser.setUserLevel(3L);\n        }\n        if(otherUser.getIntegrationNow()>=150)\n        {\n            otherUser.setUserLevel(4L);\n        }\n        if(otherUser.getIntegrationNow()>=300)\n        {\n            otherUser.setUserLevel(5L);\n        }\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/gaode/GaoDeEnum.java",
    "content": "package top.flya.system.utils.gaode;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * @Description: 高德地图枚举类\n * @Author: isymikasan\n * @Date: 2022-01-26 09:36:55\n */\n@AllArgsConstructor\n@Getter\npublic enum GaoDeEnum {\n\n    // 高德地图固定字段\n    STATUS(\"status\"),\n    INT_ONE(\"1\"),\n    RE_GEO_CODE(\"regeocode\"),\n    GEO_CODES(\"geocodes\"),\n    LOCATION(\"location\"),\n    FORMATTED_ADDRESS(\"formatted_address\"),\n    RESULTS(\"results\"),\n    DISTANCE(\"distance\");\n\n    private String code;\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/gaode/GaoDeMapUtil.java",
    "content": "package top.flya.system.utils.gaode;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.alibaba.fastjson.JSONObject;\nimport com.baomidou.mybatisplus.core.toolkit.ObjectUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\nimport top.flya.common.core.domain.R;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLConnection;\n\n/**\n * @Description: 高德地图工具类\n * @Author: isymikasan\n * @Date: 2021-12-22 09:19:02\n */\n@Component\n@Slf4j\n\npublic class GaoDeMapUtil {\n\n    /**\n     * 功能描述: 高德地图Key\n     *\n     * @param null\n     * @return\n     * @author 周兆宇\n     * @date 2022-01-26 09:13:40\n     */\n    private static final String GAO_DE_KEY = \"112049d76fe83e408d4ecceafb2ad4e3\";\n\n    //申请的账户Key\n\n    /**\n     * 功能描述: 根据地址名称得到两个地址间的距离\n     *\n     * @param start 起始位置\n     * @param end   结束位置\n     * @return long 两个地址间的距离\n     * @author isymikasan\n     * @date 2022-01-26 09:16:04\n     */\n    public Long getDistanceByAddress(String start, String end) {\n        String startLonLat = getLonLat(start).getData().toString();\n        String endLonLat = getLonLat(end).getData().toString();\n        Long distance = Long.valueOf(getDistance(startLonLat, endLonLat).getData().toString());\n        return distance;\n    }\n\n    /**\n     * 功能描述: 地址转换为经纬度\n     *\n     * @param address 地址\n     * @return java.lang.String 经纬度\n     * @author isymikasan\n     * @date 2022-01-26 09:17:13\n     */\n    public R getLonLat(String address) {\n            log.info(\"地址为：\" + address);\n            // 返回输入地址address的经纬度信息, 格式是 经度,纬度\n            String queryUrl = \"http://restapi.amap.com/v3/geocode/geo?key=\" + GAO_DE_KEY + \"&address=\" + address;\n            // 高德接口返回的是JSON格式的字符串\n            String queryResult = getResponse(queryUrl);\n            JSONObject job = JSONObject.parseObject(queryResult);\n            log.info(\"高德接口返回的是JSON格式的字符串：\" + queryResult);\n            JSONObject jobJSON = JSONObject\n                .parseObject(\n                    job.get(\"geocodes\").toString().substring(1, job.get(\"geocodes\").toString().length() - 1));\n            String LngAndLat = jobJSON.get(\"location\").toString();\n            log.info(\"经纬度为：\" + LngAndLat);\n            return R.ok(\"经纬度转换成功！\",LngAndLat);\n    }\n\n    /**\n     * 将经纬度 转换为 地址\n     *\n     * @param longitude 经度\n     * @param latitude  纬度\n     * @return 地址名称\n     * @throws Exception\n     */\n    public static R getAddress(String longitude, String latitude) throws Exception {\n        String url;\n        try {\n            url = \"http://restapi.amap.com/v3/geocode/regeo?output=JSON&location=\" + longitude + \",\" + latitude\n                + \"&key=\" + GAO_DE_KEY + \"&radius=0&extensions=base\";\n\n            log.info(\"经度 \" + longitude);\n            log.info(\"纬度：\" + latitude);\n            log.info(\"url:\" + url);\n\n            // 高德接口返回的是JSON格式的字符串\n            String queryResult = getResponse(url);\n            if (ObjectUtils.isNull(queryResult)) {\n                return R.fail(\"查询结果为空\");\n            }\n\n            // 将获取结果转为json 数据\n            JSONObject obj = JSONObject.parseObject(queryResult);\n            if (obj.get(GaoDeEnum.STATUS.getCode()).toString().equals(GaoDeEnum.INT_ONE.getCode())) {\n                // 如果没有返回-1\n                JSONObject reGeoCode = obj.getJSONObject(GaoDeEnum.RE_GEO_CODE.getCode());\n                if (reGeoCode.size() > 0) {\n                    log.info(\"reGeoCode:\" + reGeoCode);\n                    // 在regeocode中拿到 formatted_address 具体位置\n                    String formatted = reGeoCode.get(\"formatted_address\").toString();\n                    return R.ok( \"地址获取成功！\",formatted);\n\n                } else {\n                    return R.fail(\"未找到相匹配的地址！\");\n                }\n            } else {\n                return R.fail(\"请求错误！\");\n            }\n        } catch (Exception e) {\n            // TODO Auto-generated catch block\n            e.printStackTrace();\n            return R.fail(\"系统未知异常，请稍后再试\");\n        }\n    }\n\n    /**\n     * 功能描述: 根据两个定位点的经纬度算出两点间的距离\n     *\n     * @param startLonLat 起始经纬度\n     * @param endLonLat   结束经纬度（目标经纬度）\n     * @return java.lang.Long 两个定位点之间的距离\n     * @author isymikasan\n     * @date 2022-01-26 09:47:42\n     */\n    public R<Long> getDistance(String startLonLat, String endLonLat) {\n        try {\n            // 返回起始地startAddr与目的地endAddr之间的距离，单位：米\n            Long result = new Long(0);\n            String queryUrl =\n                \"http://restapi.amap.com/v3/distance?key=\" + GAO_DE_KEY + \"&origins=\" + startLonLat\n                    + \"&destination=\"\n                    + endLonLat;\n            String queryResult = getResponse(queryUrl);\n            log.info(\"请求url is {} \\n高德接口返回的是JSON格式的字符串：{}\" ,queryUrl,queryResult);\n            JSONObject job = JSONObject.parseObject(queryResult);\n            JSONArray ja = job.getJSONArray(\"results\");\n            if(ja.size() == 0){\n                return R.ok(0L); //距离计算失败\n            }\n            JSONObject jobO = JSONObject.parseObject(ja.getString(0));\n            result = Long.parseLong(jobO.get(\"distance\").toString());\n            return R.ok(\"距离计算成功！\",result);\n        } catch (Exception e) {\n            return R.fail(e.toString());\n        }\n\n\n    }\n\n    /**\n     * 功能描述: 发送请求\n     *\n     * @param serverUrl 请求地址\n     * @return java.lang.String\n     * @author isymikasan\n     * @date 2022-01-26 09:15:01\n     */\n    private static String getResponse(String serverUrl) {\n        // 用JAVA发起http请求，并返回json格式的结果\n        StringBuffer result = new StringBuffer();\n        try {\n            URL url = new URL(serverUrl);\n            URLConnection conn = url.openConnection();\n            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));\n            String line;\n            while ((line = in.readLine()) != null) {\n                result.append(line);\n            }\n            in.close();\n        } catch (MalformedURLException e) {\n            e.printStackTrace();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n        return result.toString();\n    }\n\n    public static void main(String[] args) throws Exception {\n        System.out.println(getAddress( \"118.73145\",\"32.00335\"));\n    }\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/map/City.java",
    "content": "package top.flya.system.utils.map;\n\npublic class City {\n    private String name;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        if(name.endsWith(\"市\"))\n        {\n            name = name.substring(0, name.length() - 1);\n        }\n        this.name = name;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/map/CitySql.java",
    "content": "package top.flya.system.utils.map;\n\nimport com.alibaba.fastjson.JSONObject;\nimport lombok.Data;\nimport org.apache.commons.io.FileUtils;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.List;\n\n@Data\npublic class CitySql {\n\n    public static void main(String[] args) throws IOException {\n\n        try {\n            File file = new File(\"J:\\\\ReStudyVue\\\\paizhi-city\\\\PaiZhiCheng\\\\src\\\\main\\\\java\\\\top\\\\flya\\\\system\\\\utils\\\\map\\\\city.json\");\n            String content = FileUtils.readFileToString(file, \"UTF-8\");\n            // Do something with the data\n            System.out.println(\"Hello World!\");\n            List<Maps> maps = JSONObject.parseArray(content, Maps.class);\n//            List<Maps> maps = JsonUtils.parseArray(content, Maps.class);\n            String sql = \"INSERT INTO paizhicheng.pzc_region (base, name, img_url, create_time, update_time, state)\\n\" +\n                \"VALUES ('江苏省', '扬州', DEFAULT, DEFAULT, DEFAULT, DEFAULT);\";\n\n            StringBuilder sb = new StringBuilder();\n            for (Maps map : maps) {\n                String base = map.getName();\n                List<City> city = map.getCity();\n                for (City maps1 : city) {\n                    String name = maps1.getName();\n                    String sql1 = sql.replace(\"江苏省\", base).replace(\"扬州\", name);\n                    System.out.println(sql1);\n                    sb.append(sql1).append(\"\\n\");\n                }\n            }\n            System.out.println(sb.toString());\n\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n\n\n\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/map/Maps.java",
    "content": "package top.flya.system.utils.map;\n\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\npublic class Maps {\n    private String name;\n    private List<City> city;\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/map/city.json",
    "content": "[\n  {\n    \"name\": \"北京市\",\n    \"city\": [\n      {\n        \"name\": \"北京市\",\n        \"area\": [\n          \"东城区\",\n          \"西城区\",\n          \"崇文区\",\n          \"宣武区\",\n          \"朝阳区\",\n          \"丰台区\",\n          \"石景山区\",\n          \"海淀区\",\n          \"门头沟区\",\n          \"房山区\",\n          \"通州区\",\n          \"顺义区\",\n          \"昌平区\",\n          \"大兴区\",\n          \"平谷区\",\n          \"怀柔区\",\n          \"密云县\",\n          \"延庆县\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"天津市\",\n    \"city\": [\n      {\n        \"name\": \"天津市\",\n        \"area\": [\n          \"和平区\",\n          \"河东区\",\n          \"河西区\",\n          \"南开区\",\n          \"河北区\",\n          \"红桥区\",\n          \"塘沽区\",\n          \"汉沽区\",\n          \"大港区\",\n          \"东丽区\",\n          \"西青区\",\n          \"津南区\",\n          \"北辰区\",\n          \"武清区\",\n          \"宝坻区\",\n          \"宁河县\",\n          \"静海县\",\n          \"蓟  县\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"河北省\",\n    \"city\": [\n      {\n        \"name\": \"石家庄市\",\n        \"area\": [\n          \"长安区\",\n          \"桥东区\",\n          \"桥西区\",\n          \"新华区\",\n          \"郊  区\",\n          \"井陉矿区\",\n          \"井陉县\",\n          \"正定县\",\n          \"栾城县\",\n          \"行唐县\",\n          \"灵寿县\",\n          \"高邑县\",\n          \"深泽县\",\n          \"赞皇县\",\n          \"无极县\",\n          \"平山县\",\n          \"元氏县\",\n          \"赵  县\",\n          \"辛集市\",\n          \"藁\",\n          \"晋州市\",\n          \"新乐市\",\n          \"鹿泉市\"\n        ]\n      },\n      {\n        \"name\": \"唐山市\",\n        \"area\": [\n          \"路南区\",\n          \"路北区\",\n          \"古冶区\",\n          \"开平区\",\n          \"新  区\",\n          \"丰润县\",\n          \"滦  县\",\n          \"滦南县\",\n          \"乐亭县\",\n          \"迁西县\",\n          \"玉田县\",\n          \"唐海县\",\n          \"遵化市\",\n          \"丰南市\",\n          \"迁安市\"\n        ]\n      },\n      {\n        \"name\": \"秦皇岛市\",\n        \"area\": [\n          \"海港区\",\n          \"山海关区\",\n          \"北戴河区\",\n          \"青龙满族自治县\",\n          \"昌黎县\",\n          \"抚宁县\",\n          \"卢龙县\"\n        ]\n      },\n      {\n        \"name\": \"邯郸市\",\n        \"area\": [\n          \"邯山区\",\n          \"丛台区\",\n          \"复兴区\",\n          \"峰峰矿区\",\n          \"邯郸县\",\n          \"临漳县\",\n          \"成安县\",\n          \"大名县\",\n          \"涉  县\",\n          \"磁  县\",\n          \"肥乡县\",\n          \"永年县\",\n          \"邱  县\",\n          \"鸡泽县\",\n          \"广平县\",\n          \"馆陶县\",\n          \"魏  县\",\n          \"曲周县\",\n          \"武安市\"\n        ]\n      },\n      {\n        \"name\": \"邢台市\",\n        \"area\": [\n          \"桥东区\",\n          \"桥西区\",\n          \"邢台县\",\n          \"临城县\",\n          \"内丘县\",\n          \"柏乡县\",\n          \"隆尧县\",\n          \"任  县\",\n          \"南和县\",\n          \"宁晋县\",\n          \"巨鹿县\",\n          \"新河县\",\n          \"广宗县\",\n          \"平乡县\",\n          \"威  县\",\n          \"清河县\",\n          \"临西县\",\n          \"南宫市\",\n          \"沙河市\"\n        ]\n      },\n      {\n        \"name\": \"保定市\",\n        \"area\": [\n          \"新市区\",\n          \"北市区\",\n          \"南市区\",\n          \"满城县\",\n          \"清苑县\",\n          \"涞水县\",\n          \"阜平县\",\n          \"徐水县\",\n          \"定兴县\",\n          \"唐  县\",\n          \"高阳县\",\n          \"容城县\",\n          \"涞源县\",\n          \"望都县\",\n          \"安新县\",\n          \"易  县\",\n          \"曲阳县\",\n          \"蠡  县\",\n          \"顺平县\",\n          \"博野\",\n          \"雄县\",\n          \"涿州市\",\n          \"定州市\",\n          \"安国市\",\n          \"高碑店市\"\n        ]\n      },\n      {\n        \"name\": \"张家口\",\n        \"area\": [\n          \"桥东区\",\n          \"桥西区\",\n          \"宣化区\",\n          \"下花园区\",\n          \"宣化县\",\n          \"张北县\",\n          \"康保县\",\n          \"沽源县\",\n          \"尚义县\",\n          \"蔚  县\",\n          \"阳原县\",\n          \"怀安县\",\n          \"万全县\",\n          \"怀来县\",\n          \"涿鹿县\",\n          \"赤城县\",\n          \"崇礼县\"\n        ]\n      },\n      {\n        \"name\": \"承德市\",\n        \"area\": [\n          \"双桥区\",\n          \"双滦区\",\n          \"鹰手营子矿区\",\n          \"承德县\",\n          \"兴隆县\",\n          \"平泉县\",\n          \"滦平县\",\n          \"隆化县\",\n          \"丰宁满族自治县\",\n          \"宽城满族自治县\",\n          \"围场满族蒙古族自治县\"\n        ]\n      },\n      {\n        \"name\": \"沧州市\",\n        \"area\": [\n          \"新华区\",\n          \"运河区\",\n          \"沧  县\",\n          \"青  县\",\n          \"东光县\",\n          \"海兴县\",\n          \"盐山县\",\n          \"肃宁县\",\n          \"南皮县\",\n          \"吴桥县\",\n          \"献  县\",\n          \"孟村回族自治县\",\n          \"泊头市\",\n          \"任丘市\",\n          \"黄骅市\",\n          \"河间市\"\n        ]\n      },\n      {\n        \"name\": \"廊坊市\",\n        \"area\": [\n          \"安次区\",\n          \"固安县\",\n          \"永清县\",\n          \"香河县\",\n          \"大城县\",\n          \"文安县\",\n          \"大厂回族自治县\",\n          \"霸州市\",\n          \"三河市\"\n        ]\n      },\n      {\n        \"name\": \"衡水市\",\n        \"area\": [\n          \"桃城区\",\n          \"枣强县\",\n          \"武邑县\",\n          \"武强县\",\n          \"饶阳县\",\n          \"安平县\",\n          \"故城县\",\n          \"景  县\",\n          \"阜城县\",\n          \"冀州市\",\n          \"深州市\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"山西省\",\n    \"city\": [\n      {\n        \"name\": \"太原市\",\n        \"area\": [\n          \"小店区\",\n          \"迎泽区\",\n          \"杏花岭区\",\n          \"尖草坪区\",\n          \"万柏林区\",\n          \"晋源区\",\n          \"清徐县\",\n          \"阳曲县\",\n          \"娄烦县\",\n          \"古交市\"\n        ]\n      },\n      {\n        \"name\": \"大同市\",\n        \"area\": [\n          \"城  区\",\n          \"矿  区\",\n          \"南郊区\",\n          \"新荣区\",\n          \"阳高县\",\n          \"天镇县\",\n          \"广灵县\",\n          \"灵丘县\",\n          \"浑源县\",\n          \"左云县\",\n          \"大同县\"\n        ]\n      },\n      {\n        \"name\": \"阳泉市\",\n        \"area\": [\n          \"城  区\",\n          \"矿  区\",\n          \"郊  区\",\n          \"平定县\",\n          \"盂  县\"\n        ]\n      },\n      {\n        \"name\": \"长治市\",\n        \"area\": [\n          \"城  区\",\n          \"郊  区\",\n          \"长治县\",\n          \"襄垣县\",\n          \"屯留县\",\n          \"平顺县\",\n          \"黎城县\",\n          \"壶关县\",\n          \"长子县\",\n          \"武乡县\",\n          \"沁  县\",\n          \"沁源县\",\n          \"潞城市\"\n        ]\n      },\n      {\n        \"name\": \"晋城市\",\n        \"area\": [\n          \"城  区\",\n          \"沁水县\",\n          \"阳城县\",\n          \"陵川县\",\n          \"泽州县\",\n          \"高平市\"\n        ]\n      },\n      {\n        \"name\": \"朔州市\",\n        \"area\": [\n          \"朔城区\",\n          \"平鲁区\",\n          \"山阴县\",\n          \"应  县\",\n          \"右玉县\",\n          \"怀仁县\"\n        ]\n      },\n      {\n        \"name\": \"忻州市\",\n        \"area\": [\n          \"忻府区\",\n          \"原平市\",\n          \"定襄县\",\n          \"五台县\",\n          \"代  县\",\n          \"繁峙县\",\n          \"宁武县\",\n          \"静乐县\",\n          \"神池县\",\n          \"五寨县\",\n          \"岢岚县\",\n          \"河曲县\",\n          \"保德县\",\n          \"偏关县\"\n        ]\n      },\n      {\n        \"name\": \"吕梁市\",\n        \"area\": [\n          \"离石区\",\n          \"孝义市\",\n          \"汾阳市\",\n          \"文水县\",\n          \"交城县\",\n          \"兴  县\",\n          \"临  县\",\n          \"柳林县\",\n          \"石楼县\",\n          \"岚  县\",\n          \"方山县\",\n          \"中阳县\",\n          \"交口县\"\n        ]\n      },\n      {\n        \"name\": \"晋中市\",\n        \"area\": [\n          \"榆次市\",\n          \"介休市\",\n          \"榆社县\",\n          \"左权县\",\n          \"和顺县\",\n          \"昔阳县\",\n          \"寿阳县\",\n          \"太谷县\",\n          \"祁  县\",\n          \"平遥县\",\n          \"灵石县\"\n        ]\n      },\n      {\n        \"name\": \"临汾市\",\n        \"area\": [\n          \"临汾市\",\n          \"侯马市\",\n          \"霍州市\",\n          \"曲沃县\",\n          \"翼城县\",\n          \"襄汾县\",\n          \"洪洞县\",\n          \"古  县\",\n          \"安泽县\",\n          \"浮山县\",\n          \"吉  县\",\n          \"乡宁县\",\n          \"蒲  县\",\n          \"大宁县\",\n          \"永和县\",\n          \"隰  县\",\n          \"汾西县\"\n        ]\n      },\n      {\n        \"name\": \"运城市\",\n        \"area\": [\n          \"运城市\",\n          \"永济市\",\n          \"河津市\",\n          \"芮城县\",\n          \"临猗县\",\n          \"万荣县\",\n          \"新绛县\",\n          \"稷山县\",\n          \"闻喜县\",\n          \"夏  县\",\n          \"绛  县\",\n          \"平陆县\",\n          \"垣曲县\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"内蒙古\",\n    \"city\": [\n      {\n        \"name\": \"呼和浩特市\",\n        \"area\": [\n          \"新城区\",\n          \"回民区\",\n          \"玉泉区\",\n          \"郊  区\",\n          \"土默特左旗\",\n          \"托克托县\",\n          \"和林格尔县\",\n          \"清水河县\",\n          \"武川县\"\n        ]\n      },\n      {\n        \"name\": \"包头市\",\n        \"area\": [\n          \"东河区\",\n          \"昆都伦区\",\n          \"青山区\",\n          \"石拐矿区\",\n          \"白云矿区\",\n          \"郊  区\",\n          \"土默特右旗\",\n          \"固阳县\",\n          \"达尔罕茂明安联合旗\"\n        ]\n      },\n      {\n        \"name\": \"乌海市\",\n        \"area\": [\n          \"海勃湾区\",\n          \"海南区\",\n          \"乌达区\"\n        ]\n      },\n      {\n        \"name\": \"赤峰市\",\n        \"area\": [\n          \"红山区\",\n          \"元宝山区\",\n          \"松山区\",\n          \"阿鲁科尔沁旗\",\n          \"巴林左旗\",\n          \"巴林右旗\",\n          \"林西县\",\n          \"克什克腾旗\",\n          \"翁牛特旗\",\n          \"喀喇沁旗\",\n          \"宁城县\",\n          \"敖汉旗\"\n        ]\n      },\n      {\n        \"name\": \"呼伦贝尔市\",\n        \"area\": [\n          \"海拉尔市\",\n          \"满洲里市\",\n          \"扎兰屯市\",\n          \"牙克石市\",\n          \"根河市\",\n          \"额尔古纳市\",\n          \"阿荣旗\",\n          \"莫力达瓦达斡尔族自治旗\",\n          \"鄂伦春自治旗\",\n          \"鄂温克族自治旗\",\n          \"新巴尔虎右旗\",\n          \"新巴尔虎左旗\",\n          \"陈巴尔虎旗\"\n        ]\n      },\n      {\n        \"name\": \"兴安盟\",\n        \"area\": [\n          \"乌兰浩特市\",\n          \"阿尔山市\",\n          \"科尔沁右翼前旗\",\n          \"科尔沁右翼中旗\",\n          \"扎赉特旗\",\n          \"突泉县\"\n        ]\n      },\n      {\n        \"name\": \"通辽市\",\n        \"area\": [\n          \"科尔沁区\",\n          \"霍林郭勒市\",\n          \"科尔沁左翼中旗\",\n          \"科尔沁左翼后旗\",\n          \"开鲁县\",\n          \"库伦旗\",\n          \"奈曼旗\",\n          \"扎鲁特旗\"\n        ]\n      },\n      {\n        \"name\": \"锡林郭勒盟\",\n        \"area\": [\n          \"二连浩特市\",\n          \"锡林浩特市\",\n          \"阿巴嘎旗\",\n          \"苏尼特左旗\",\n          \"苏尼特右旗\",\n          \"东乌珠穆沁旗\",\n          \"西乌珠穆沁旗\",\n          \"太仆寺旗\",\n          \"镶黄旗\",\n          \"正镶白旗\",\n          \"正蓝旗\",\n          \"多伦县\"\n        ]\n      },\n      {\n        \"name\": \"乌兰察布盟\",\n        \"area\": [\n          \"集宁市\",\n          \"丰镇市\",\n          \"卓资县\",\n          \"化德县\",\n          \"商都县\",\n          \"兴和县\",\n          \"凉城县\",\n          \"察哈尔右翼前旗\",\n          \"察哈尔右翼中旗\",\n          \"察哈尔右翼后旗\",\n          \"四子王旗\"\n        ]\n      },\n      {\n        \"name\": \"伊克昭盟\",\n        \"area\": [\n          \"东胜市\",\n          \"达拉特旗\",\n          \"准格尔旗\",\n          \"鄂托克前旗\",\n          \"鄂托克旗\",\n          \"杭锦旗\",\n          \"乌审旗\",\n          \"伊金霍洛旗\"\n        ]\n      },\n      {\n        \"name\": \"巴彦淖尔盟\",\n        \"area\": [\n          \"临河市\",\n          \"五原县\",\n          \"磴口县\",\n          \"乌拉特前旗\",\n          \"乌拉特中旗\",\n          \"乌拉特后旗\",\n          \"杭锦后旗\"\n        ]\n      },\n      {\n        \"name\": \"阿拉善盟\",\n        \"area\": [\n          \"阿拉善左旗\",\n          \"阿拉善右旗\",\n          \"额济纳旗\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"辽宁省\",\n    \"city\": [\n      {\n        \"name\": \"沈阳市\",\n        \"area\": [\n          \"沈河区\",\n          \"皇姑区\",\n          \"和平区\",\n          \"大东区\",\n          \"铁西区\",\n          \"苏家屯区\",\n          \"东陵区\",\n          \"于洪区\",\n          \"新民市\",\n          \"法库县\",\n          \"辽中县\",\n          \"康平县\",\n          \"新城子区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"大连市\",\n        \"area\": [\n          \"西岗区\",\n          \"中山区\",\n          \"沙河口区\",\n          \"甘井子区\",\n          \"旅顺口区\",\n          \"金州区\",\n          \"瓦房店市\",\n          \"普兰店市\",\n          \"庄河市\",\n          \"长海县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"鞍山市\",\n        \"area\": [\n          \"铁东区\",\n          \"铁西区\",\n          \"立山区\",\n          \"千山区\",\n          \"海城市\",\n          \"台安县\",\n          \"岫岩满族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"抚顺市\",\n        \"area\": [\n          \"顺城区\",\n          \"新抚区\",\n          \"东洲区\",\n          \"望花区\",\n          \"抚顺县\",\n          \"清原满族自治县\",\n          \"新宾满族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"本溪市\",\n        \"area\": [\n          \"平山区\",\n          \"明山区\",\n          \"溪湖区\",\n          \"南芬区\",\n          \"本溪满族自治县\",\n          \"桓仁满族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"丹东市\",\n        \"area\": [\n          \"振兴区\",\n          \"元宝区\",\n          \"振安区\",\n          \"东港市\",\n          \"凤城市\",\n          \"宽甸满族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"锦州市\",\n        \"area\": [\n          \"太和区\",\n          \"古塔区\",\n          \"凌河区\",\n          \"凌海市\",\n          \"黑山县\",\n          \"义县\",\n          \"北宁市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"营口市\",\n        \"area\": [\n          \"站前区\",\n          \"西市区\",\n          \"鲅鱼圈区\",\n          \"老边区\",\n          \"大石桥市\",\n          \"盖州市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"阜新市\",\n        \"area\": [\n          \"海州区\",\n          \"新邱区\",\n          \"太平区\",\n          \"清河门区\",\n          \"细河区\",\n          \"彰武县\",\n          \"阜新蒙古族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"辽阳市\",\n        \"area\": [\n          \"白塔区\",\n          \"文圣区\",\n          \"宏伟区\",\n          \"太子河区\",\n          \"弓长岭区\",\n          \"灯塔市\",\n          \"辽阳县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"盘锦\",\n        \"area\": [\n          \"双台子区\",\n          \"兴隆台区\",\n          \"盘山县\",\n          \"大洼县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"铁岭市\",\n        \"area\": [\n          \"银州区\",\n          \"清河区\",\n          \"调兵山市\",\n          \"开原市\",\n          \"铁岭县\",\n          \"昌图县\",\n          \"西丰县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"朝阳市\",\n        \"area\": [\n          \"双塔区\",\n          \"龙城区\",\n          \"凌源市\",\n          \"北票市\",\n          \"朝阳县\",\n          \"建平县\",\n          \"喀喇沁左翼蒙古族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"葫芦岛市\",\n        \"area\": [\n          \"龙港区\",\n          \"南票区\",\n          \"连山区\",\n          \"兴城市\",\n          \"绥中县\",\n          \"建昌县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"吉林省\",\n    \"city\": [\n      {\n        \"name\": \"长春市\",\n        \"area\": [\n          \"朝阳区\",\n          \"宽城区\",\n          \"二道区\",\n          \"南关区\",\n          \"绿园区\",\n          \"双阳区\",\n          \"九台市\",\n          \"榆树市\",\n          \"德惠市\",\n          \"农安县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"吉林市\",\n        \"area\": [\n          \"船营区\",\n          \"昌邑区\",\n          \"龙潭区\",\n          \"丰满区\",\n          \"舒兰市\",\n          \"桦甸市\",\n          \"蛟河市\",\n          \"磐石市\",\n          \"永吉县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"四平\",\n        \"area\": [\n          \"铁西区\",\n          \"铁东区\",\n          \"公主岭市\",\n          \"双辽市\",\n          \"梨树县\",\n          \"伊通满族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"辽源市\",\n        \"area\": [\n          \"龙山区\",\n          \"西安区\",\n          \"东辽县\",\n          \"东丰县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"通化市\",\n        \"area\": [\n          \"东昌区\",\n          \"二道江区\",\n          \"梅河口市\",\n          \"集安市\",\n          \"通化县\",\n          \"辉南县\",\n          \"柳河县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"白山市\",\n        \"area\": [\n          \"八道江区\",\n          \"江源区\",\n          \"临江市\",\n          \"靖宇县\",\n          \"抚松县\",\n          \"长白朝鲜族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"松原市\",\n        \"area\": [\n          \"宁江区\",\n          \"乾安县\",\n          \"长岭县\",\n          \"扶余县\",\n          \"前郭尔罗斯蒙古族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"白城市\",\n        \"area\": [\n          \"洮北区\",\n          \"大安市\",\n          \"洮南市\",\n          \"镇赉县\",\n          \"通榆县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"延边朝鲜族自治州\",\n        \"area\": [\n          \"延吉市\",\n          \"图们市\",\n          \"敦化市\",\n          \"龙井市\",\n          \"珲春市\",\n          \"和龙市\",\n          \"安图县\",\n          \"汪清县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"黑龙江省\",\n    \"city\": [\n      {\n        \"name\": \"哈尔滨市\",\n        \"area\": [\n          \"松北区\",\n          \"道里区\",\n          \"南岗区\",\n          \"平房区\",\n          \"香坊区\",\n          \"道外区\",\n          \"呼兰区\",\n          \"阿城区\",\n          \"双城市\",\n          \"尚志市\",\n          \"五常市\",\n          \"宾县\",\n          \"方正县\",\n          \"通河县\",\n          \"巴彦县\",\n          \"延寿县\",\n          \"木兰县\",\n          \"依兰县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"齐齐哈尔市\",\n        \"area\": [\n          \"龙沙区\",\n          \"昂昂溪区\",\n          \"铁锋区\",\n          \"建华区\",\n          \"富拉尔基区\",\n          \"碾子山区\",\n          \"梅里斯达斡尔族区\",\n          \"讷河市\",\n          \"富裕县\",\n          \"拜泉县\",\n          \"甘南县\",\n          \"依安县\",\n          \"克山县\",\n          \"泰来县\",\n          \"克东县\",\n          \"龙江县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"鹤岗市\",\n        \"area\": [\n          \"兴山区\",\n          \"工农区\",\n          \"南山区\",\n          \"兴安区\",\n          \"向阳区\",\n          \"东山区\",\n          \"萝北县\",\n          \"绥滨县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"双鸭山\",\n        \"area\": [\n          \"尖山区\",\n          \"岭东区\",\n          \"四方台区\",\n          \"宝山区\",\n          \"集贤县\",\n          \"宝清县\",\n          \"友谊县\",\n          \"饶河县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"鸡西市\",\n        \"area\": [\n          \"鸡冠区\",\n          \"恒山区\",\n          \"城子河区\",\n          \"滴道区\",\n          \"梨树区\",\n          \"麻山区\",\n          \"密山市\",\n          \"虎林市\",\n          \"鸡东县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"大庆市\",\n        \"area\": [\n          \"萨尔图区\",\n          \"红岗区\",\n          \"龙凤区\",\n          \"让胡路区\",\n          \"大同区\",\n          \"林甸县\",\n          \"肇州县\",\n          \"肇源县\",\n          \"杜尔伯特蒙古族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"伊春市\",\n        \"area\": [\n          \"伊春区\",\n          \"带岭区\",\n          \"南岔区\",\n          \"金山屯区\",\n          \"西林区\",\n          \"美溪区\",\n          \"乌马河区\",\n          \"翠峦区\",\n          \"友好区\",\n          \"上甘岭区\",\n          \"五营区\",\n          \"红星区\",\n          \"新青区\",\n          \"汤旺河区\",\n          \"乌伊岭区\",\n          \"铁力市\",\n          \"嘉荫县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"牡丹江市\",\n        \"area\": [\n          \"爱民区\",\n          \"东安区\",\n          \"阳明区\",\n          \"西安区\",\n          \"绥芬河市\",\n          \"宁安市\",\n          \"海林市\",\n          \"穆棱市\",\n          \"林口县\",\n          \"东宁县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"佳木斯市\",\n        \"area\": [\n          \"向阳区\",\n          \"前进区\",\n          \"东风区\",\n          \"郊区\",\n          \"同江市\",\n          \"富锦市\",\n          \"桦川县\",\n          \"抚远县\",\n          \"桦南县\",\n          \"汤原县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"七台河市\",\n        \"area\": [\n          \"桃山区\",\n          \"新兴区\",\n          \"茄子河区\",\n          \"勃利县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"黑河市\",\n        \"area\": [\n          \"爱辉区\",\n          \"北安市\",\n          \"五大连池市\",\n          \"逊克县\",\n          \"嫩江县\",\n          \"孙吴县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"绥化市\",\n        \"area\": [\n          \"北林区\",\n          \"安达市\",\n          \"肇东市\",\n          \"海伦市\",\n          \"绥棱县\",\n          \"兰西县\",\n          \"明水县\",\n          \"青冈县\",\n          \"庆安县\",\n          \"望奎县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"大兴安岭地区\",\n        \"area\": [\n          \"呼玛县\",\n          \"塔河县\",\n          \"漠河县\",\n          \"大兴安岭辖区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"上海市\",\n    \"city\": [\n      {\n        \"name\": \"上海市\",\n        \"area\": [\n          \"黄浦区\",\n          \"卢湾区\",\n          \"徐汇区\",\n          \"长宁区\",\n          \"静安区\",\n          \"普陀区\",\n          \"闸北区\",\n          \"虹口区\",\n          \"杨浦区\",\n          \"宝山区\",\n          \"闵行区\",\n          \"嘉定区\",\n          \"松江区\",\n          \"金山区\",\n          \"青浦区\",\n          \"南汇区\",\n          \"奉贤区\",\n          \"浦东新区\",\n          \"崇明县\",\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"江苏省\",\n    \"city\": [\n      {\n        \"name\": \"南京市\",\n        \"area\": [\n          \"玄武区\",\n          \"白下区\",\n          \"秦淮区\",\n          \"建邺区\",\n          \"鼓楼区\",\n          \"下关区\",\n          \"栖霞区\",\n          \"雨花台区\",\n          \"浦口区\",\n          \"江宁区\",\n          \"六合区\",\n          \"溧水县\",\n          \"高淳县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"苏州市\",\n        \"area\": [\n          \"金阊区\",\n          \"平江区\",\n          \"沧浪区\",\n          \"虎丘区\",\n          \"吴中区\",\n          \"相城区\",\n          \"常熟市\",\n          \"张家港市\",\n          \"昆山市\",\n          \"吴江市\",\n          \"太仓市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"无锡市\",\n        \"area\": [\n          \"崇安区\",\n          \"南长区\",\n          \"北塘区\",\n          \"滨湖区\",\n          \"锡山区\",\n          \"惠山区\",\n          \"江阴市\",\n          \"宜兴市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"常州市\",\n        \"area\": [\n          \"钟楼区\",\n          \"天宁区\",\n          \"戚墅堰区\",\n          \"新北区\",\n          \"武进区\",\n          \"金坛市\",\n          \"溧阳市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"镇江市\",\n        \"area\": [\n          \"京口区\",\n          \"润州区\",\n          \"丹徒区\",\n          \"丹阳市\",\n          \"扬中市\",\n          \"句容市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"南通市\",\n        \"area\": [\n          \"崇川区\",\n          \"港闸区\",\n          \"通州市\",\n          \"如皋市\",\n          \"海门市\",\n          \"启东市\",\n          \"海安县\",\n          \"如东县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"泰州市\",\n        \"area\": [\n          \"海陵区\",\n          \"高港区\",\n          \"姜堰市\",\n          \"泰兴市\",\n          \"靖江市\",\n          \"兴化市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"扬州市\",\n        \"area\": [\n          \"广陵区\",\n          \"维扬区\",\n          \"邗江区\",\n          \"江都市\",\n          \"仪征市\",\n          \"高邮市\",\n          \"宝应县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"盐城市\",\n        \"area\": [\n          \"亭湖区\",\n          \"盐都区\",\n          \"大丰市\",\n          \"东台市\",\n          \"建湖县\",\n          \"射阳县\",\n          \"阜宁县\",\n          \"滨海县\",\n          \"响水县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"连云港市\",\n        \"area\": [\n          \"新浦区\",\n          \"海州区\",\n          \"连云区\",\n          \"东海县\",\n          \"灌云县\",\n          \"赣榆县\",\n          \"灌南县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"徐州市\",\n        \"area\": [\n          \"云龙区\",\n          \"鼓楼区\",\n          \"九里区\",\n          \"泉山区\",\n          \"贾汪区\",\n          \"邳州市\",\n          \"新沂市\",\n          \"铜山县\",\n          \"睢宁县\",\n          \"沛县\",\n          \"丰县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"淮安市\",\n        \"area\": [\n          \"清河区\",\n          \"清浦区\",\n          \"楚州区\",\n          \"淮阴区\",\n          \"涟水县\",\n          \"洪泽县\",\n          \"金湖县\",\n          \"盱眙县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"宿迁市\",\n        \"area\": [\n          \"宿城区\",\n          \"宿豫区\",\n          \"沭阳县\",\n          \"泗阳县\",\n          \"泗洪县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"浙江省\",\n    \"city\": [\n      {\n        \"name\": \"杭州市\",\n        \"area\": [\n          \"拱墅区\",\n          \"西湖区\",\n          \"上城区\",\n          \"下城区\",\n          \"江干区\",\n          \"滨江区\",\n          \"余杭区\",\n          \"萧山区\",\n          \"建德市\",\n          \"富阳市\",\n          \"临安市\",\n          \"桐庐县\",\n          \"淳安县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"宁波市\",\n        \"area\": [\n          \"海曙区\",\n          \"江东区\",\n          \"江北区\",\n          \"镇海区\",\n          \"北仑区\",\n          \"鄞州区\",\n          \"余姚市\",\n          \"慈溪市\",\n          \"奉化市\",\n          \"宁海县\",\n          \"象山县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"温州市\",\n        \"area\": [\n          \"鹿城区\",\n          \"龙湾区\",\n          \"瓯海区\",\n          \"瑞安市\",\n          \"乐清市\",\n          \"永嘉县\",\n          \"洞头县\",\n          \"平阳县\",\n          \"苍南县\",\n          \"文成县\",\n          \"泰顺县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"嘉兴市\",\n        \"area\": [\n          \"秀城区\",\n          \"秀洲区\",\n          \"海宁市\",\n          \"平湖市\",\n          \"桐乡市\",\n          \"嘉善县\",\n          \"海盐县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"湖州市\",\n        \"area\": [\n          \"吴兴区\",\n          \"南浔区\",\n          \"长兴县\",\n          \"德清县\",\n          \"安吉县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"绍兴市\",\n        \"area\": [\n          \"越城区\",\n          \"诸暨市\",\n          \"上虞市\",\n          \"嵊州市\",\n          \"绍兴县\",\n          \"新昌县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"金华市\",\n        \"area\": [\n          \"婺城区\",\n          \"金东区\",\n          \"兰溪市\",\n          \"义乌市\",\n          \"东阳市\",\n          \"永康市\",\n          \"武义县\",\n          \"浦江县\",\n          \"磐安县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"衢州市\",\n        \"area\": [\n          \"柯城区\",\n          \"衢江区\",\n          \"江山市\",\n          \"龙游县\",\n          \"常山县\",\n          \"开化县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"舟山市\",\n        \"area\": [\n          \"定海区\",\n          \"普陀区\",\n          \"岱山县\",\n          \"嵊泗县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"台州市\",\n        \"area\": [\n          \"椒江区\",\n          \"黄岩区\",\n          \"路桥区\",\n          \"临海市\",\n          \"温岭市\",\n          \"玉环县\",\n          \"天台县\",\n          \"仙居县\",\n          \"三门县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"丽水市\",\n        \"area\": [\n          \"莲都区\",\n          \"龙泉市\",\n          \"缙云县\",\n          \"青田县\",\n          \"云和县\",\n          \"遂昌县\",\n          \"松阳县\",\n          \"庆元县\",\n          \"景宁畲族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他市\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"安徽省\",\n    \"city\": [\n      {\n        \"name\": \"合肥市\",\n        \"area\": [\n          \"庐阳区\",\n          \"瑶海区\",\n          \"蜀山区\",\n          \"包河区\",\n          \"长丰县\",\n          \"肥东县\",\n          \"肥西县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"芜湖市\",\n        \"area\": [\n          \"镜湖区\",\n          \"弋江区\",\n          \"鸠江区\",\n          \"三山区\",\n          \"芜湖县\",\n          \"南陵县\",\n          \"繁昌县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"蚌埠市\",\n        \"area\": [\n          \"蚌山区\",\n          \"龙子湖区\",\n          \"禹会区\",\n          \"淮上区\",\n          \"怀远县\",\n          \"固镇县\",\n          \"五河县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"淮南市\",\n        \"area\": [\n          \"田家庵区\",\n          \"大通区\",\n          \"谢家集区\",\n          \"八公山区\",\n          \"潘集区\",\n          \"凤台县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"马鞍山市\",\n        \"area\": [\n          \"雨山区\",\n          \"花山区\",\n          \"金家庄区\",\n          \"当涂县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"淮北市\",\n        \"area\": [\n          \"相山区\",\n          \"杜集区\",\n          \"烈山区\",\n          \"濉溪县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"铜陵市\",\n        \"area\": [\n          \"铜官山区\",\n          \"狮子山区\",\n          \"郊区\",\n          \"铜陵县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"安庆市\",\n        \"area\": [\n          \"迎江区\",\n          \"大观区\",\n          \"宜秀区\",\n          \"桐城市\",\n          \"宿松县\",\n          \"枞阳县\",\n          \"太湖县\",\n          \"怀宁县\",\n          \"岳西县\",\n          \"望江县\",\n          \"潜山县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"黄山市\",\n        \"area\": [\n          \"屯溪区\",\n          \"黄山区\",\n          \"徽州区\",\n          \"休宁县\",\n          \"歙县\",\n          \"祁门县\",\n          \"黟县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"滁州市\",\n        \"area\": [\n          \"琅琊区\",\n          \"南谯区\",\n          \"天长市\",\n          \"明光市\",\n          \"全椒县\",\n          \"来安县\",\n          \"定远县\",\n          \"凤阳县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"阜阳市\",\n        \"area\": [\n          \"颍州区\",\n          \"颍东区\",\n          \"颍泉区\",\n          \"界首市\",\n          \"临泉县\",\n          \"颍上县\",\n          \"阜南县\",\n          \"太和县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"宿州市\",\n        \"area\": [\n          \"埇桥区\",\n          \"萧县\",\n          \"泗县\",\n          \"砀山县\",\n          \"灵璧县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"巢湖市\",\n        \"area\": [\n          \"居巢区\",\n          \"含山县\",\n          \"无为县\",\n          \"庐江县\",\n          \"和县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"六安市\",\n        \"area\": [\n          \"金安区\",\n          \"裕安区\",\n          \"寿县\",\n          \"霍山县\",\n          \"霍邱县\",\n          \"舒城县\",\n          \"金寨县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"亳州市\",\n        \"area\": [\n          \"谯城区\",\n          \"利辛县\",\n          \"涡阳县\",\n          \"蒙城县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"池州市\",\n        \"area\": [\n          \"贵池区\",\n          \"东至县\",\n          \"石台县\",\n          \"青阳县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"宣城市\",\n        \"area\": [\n          \"宣州区\",\n          \"宁国市\",\n          \"广德县\",\n          \"郎溪县\",\n          \"泾县\",\n          \"旌德县\",\n          \"绩溪县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他市\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"福建省\",\n    \"city\": [\n      {\n        \"name\": \"福州市\",\n        \"area\": [\n          \"鼓楼区\",\n          \"台江区\",\n          \"仓山区\",\n          \"马尾区\",\n          \"晋安区\",\n          \"福清市\",\n          \"长乐市\",\n          \"闽侯县\",\n          \"闽清县\",\n          \"永泰县\",\n          \"连江县\",\n          \"罗源县\",\n          \"平潭县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"厦门市\",\n        \"area\": [\n          \"思明区\",\n          \"海沧区\",\n          \"湖里区\",\n          \"集美区\",\n          \"同安区\",\n          \"翔安区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"莆田市\",\n        \"area\": [\n          \"城厢区\",\n          \"涵江区\",\n          \"荔城区\",\n          \"秀屿区\",\n          \"仙游县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"三明市\",\n        \"area\": [\n          \"梅列区\",\n          \"三元区\",\n          \"永安市\",\n          \"明溪县\",\n          \"将乐县\",\n          \"大田县\",\n          \"宁化县\",\n          \"建宁县\",\n          \"沙县\",\n          \"尤溪县\",\n          \"清流县\",\n          \"泰宁县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"泉州市\",\n        \"area\": [\n          \"鲤城区\",\n          \"丰泽区\",\n          \"洛江区\",\n          \"泉港区\",\n          \"石狮市\",\n          \"晋江市\",\n          \"南安市\",\n          \"惠安县\",\n          \"永春县\",\n          \"安溪县\",\n          \"德化县\",\n          \"金门县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"漳州市\",\n        \"area\": [\n          \"芗城区\",\n          \"龙文区\",\n          \"龙海市\",\n          \"平和县\",\n          \"南靖县\",\n          \"诏安县\",\n          \"漳浦县\",\n          \"华安县\",\n          \"东山县\",\n          \"长泰县\",\n          \"云霄县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"南平市\",\n        \"area\": [\n          \"延平区\",\n          \"建瓯市\",\n          \"邵武市\",\n          \"武夷山市\",\n          \"建阳市\",\n          \"松溪县\",\n          \"光泽县\",\n          \"顺昌县\",\n          \"浦城县\",\n          \"政和县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"龙岩市\",\n        \"area\": [\n          \"新罗区\",\n          \"漳平市\",\n          \"长汀县\",\n          \"武平县\",\n          \"上杭县\",\n          \"永定县\",\n          \"连城县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"宁德市\",\n        \"area\": [\n          \"蕉城区\",\n          \"福安市\",\n          \"福鼎市\",\n          \"寿宁县\",\n          \"霞浦县\",\n          \"柘荣县\",\n          \"屏南县\",\n          \"古田县\",\n          \"周宁县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"江西省\",\n    \"city\": [\n      {\n        \"name\": \"南昌市\",\n        \"area\": [\n          \"东湖区\",\n          \"西湖区\",\n          \"青云谱区\",\n          \"湾里区\",\n          \"青山湖区\",\n          \"新建县\",\n          \"南昌县\",\n          \"进贤县\",\n          \"安义县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"景德镇市\",\n        \"area\": [\n          \"珠山区\",\n          \"昌江区\",\n          \"乐平市\",\n          \"浮梁县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"萍乡市\",\n        \"area\": [\n          \"安源区\",\n          \"湘东区\",\n          \"莲花县\",\n          \"上栗县\",\n          \"芦溪县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"九江市\",\n        \"area\": [\n          \"浔阳区\",\n          \"庐山区\",\n          \"瑞昌市\",\n          \"九江县\",\n          \"星子县\",\n          \"武宁县\",\n          \"彭泽县\",\n          \"永修县\",\n          \"修水县\",\n          \"湖口县\",\n          \"德安县\",\n          \"都昌县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"新余市\",\n        \"area\": [\n          \"渝水区\",\n          \"分宜县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"鹰潭市\",\n        \"area\": [\n          \"月湖区\",\n          \"贵溪市\",\n          \"余江县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"赣州市\",\n        \"area\": [\n          \"章贡区\",\n          \"瑞金市\",\n          \"南康市\",\n          \"石城县\",\n          \"安远县\",\n          \"赣县\",\n          \"宁都县\",\n          \"寻乌县\",\n          \"兴国县\",\n          \"定南县\",\n          \"上犹县\",\n          \"于都县\",\n          \"龙南县\",\n          \"崇义县\",\n          \"信丰县\",\n          \"全南县\",\n          \"大余县\",\n          \"会昌县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"吉安市\",\n        \"area\": [\n          \"吉州区\",\n          \"青原区\",\n          \"井冈山市\",\n          \"吉安县\",\n          \"永丰县\",\n          \"永新县\",\n          \"新干县\",\n          \"泰和县\",\n          \"峡江县\",\n          \"遂川县\",\n          \"安福县\",\n          \"吉水县\",\n          \"万安县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"宜春市\",\n        \"area\": [\n          \"袁州区\",\n          \"丰城市\",\n          \"樟树市\",\n          \"高安市\",\n          \"铜鼓县\",\n          \"靖安县\",\n          \"宜丰县\",\n          \"奉新县\",\n          \"万载县\",\n          \"上高县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"抚州市\",\n        \"area\": [\n          \"临川区\",\n          \"南丰县\",\n          \"乐安县\",\n          \"金溪县\",\n          \"南城县\",\n          \"东乡县\",\n          \"资溪县\",\n          \"宜黄县\",\n          \"广昌县\",\n          \"黎川县\",\n          \"崇仁县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"上饶市\",\n        \"area\": [\n          \"信州区\",\n          \"德兴市\",\n          \"上饶县\",\n          \"广丰县\",\n          \"鄱阳县\",\n          \"婺源县\",\n          \"铅山县\",\n          \"余干县\",\n          \"横峰县\",\n          \"弋阳县\",\n          \"玉山县\",\n          \"万年县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"山东省\",\n    \"city\": [\n      {\n        \"name\": \"济南市\",\n        \"area\": [\n          \"市中区\",\n          \"历下区\",\n          \"天桥区\",\n          \"槐荫区\",\n          \"历城区\",\n          \"长清区\",\n          \"章丘市\",\n          \"平阴县\",\n          \"济阳县\",\n          \"商河县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"青岛市\",\n        \"area\": [\n          \"市南区\",\n          \"市北区\",\n          \"城阳区\",\n          \"四方区\",\n          \"李沧区\",\n          \"黄岛区\",\n          \"崂山区\",\n          \"胶南市\",\n          \"胶州市\",\n          \"平度市\",\n          \"莱西市\",\n          \"即墨市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"淄博市\",\n        \"area\": [\n          \"张店区\",\n          \"临淄区\",\n          \"淄川区\",\n          \"博山区\",\n          \"周村区\",\n          \"桓台县\",\n          \"高青县\",\n          \"沂源县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"枣庄市\",\n        \"area\": [\n          \"市中区\",\n          \"山亭区\",\n          \"峄城区\",\n          \"台儿庄区\",\n          \"薛城区\",\n          \"滕州市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"东营市\",\n        \"area\": [\n          \"东营区\",\n          \"河口区\",\n          \"垦利县\",\n          \"广饶县\",\n          \"利津县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"烟台市\",\n        \"area\": [\n          \"芝罘区\",\n          \"福山区\",\n          \"牟平区\",\n          \"莱山区\",\n          \"龙口市\",\n          \"莱阳市\",\n          \"莱州市\",\n          \"招远市\",\n          \"蓬莱市\",\n          \"栖霞市\",\n          \"海阳市\",\n          \"长岛县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"潍坊市\",\n        \"area\": [\n          \"潍城区\",\n          \"寒亭区\",\n          \"坊子区\",\n          \"奎文区\",\n          \"青州市\",\n          \"诸城市\",\n          \"寿光市\",\n          \"安丘市\",\n          \"高密市\",\n          \"昌邑市\",\n          \"昌乐县\",\n          \"临朐县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"济宁市\",\n        \"area\": [\n          \"市中区\",\n          \"任城区\",\n          \"曲阜市\",\n          \"兖州市\",\n          \"邹城市\",\n          \"鱼台县\",\n          \"金乡县\",\n          \"嘉祥县\",\n          \"微山县\",\n          \"汶上县\",\n          \"泗水县\",\n          \"梁山县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"泰安市\",\n        \"area\": [\n          \"泰山区\",\n          \"岱岳区\",\n          \"新泰市\",\n          \"肥城市\",\n          \"宁阳县\",\n          \"东平县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"威海市\",\n        \"area\": [\n          \"环翠区\",\n          \"乳山市\",\n          \"文登市\",\n          \"荣成市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"日照市\",\n        \"area\": [\n          \"东港区\",\n          \"岚山区\",\n          \"五莲县\",\n          \"莒县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"莱芜市\",\n        \"area\": [\n          \"莱城区\",\n          \"钢城区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"临沂市\",\n        \"area\": [\n          \"兰山区\",\n          \"罗庄区\",\n          \"河东区\",\n          \"沂南县\",\n          \"郯城县\",\n          \"沂水县\",\n          \"苍山县\",\n          \"费县\",\n          \"平邑县\",\n          \"莒南县\",\n          \"蒙阴县\",\n          \"临沭县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"德州市\",\n        \"area\": [\n          \"德城区\",\n          \"乐陵市\",\n          \"禹城市\",\n          \"陵县\",\n          \"宁津县\",\n          \"齐河县\",\n          \"武城县\",\n          \"庆云县\",\n          \"平原县\",\n          \"夏津县\",\n          \"临邑县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"聊城市\",\n        \"area\": [\n          \"东昌府区\",\n          \"临清市\",\n          \"高唐县\",\n          \"阳谷县\",\n          \"茌平县\",\n          \"莘县\",\n          \"东阿县\",\n          \"冠县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"滨州市\",\n        \"area\": [\n          \"滨城区\",\n          \"邹平县\",\n          \"沾化县\",\n          \"惠民县\",\n          \"博兴县\",\n          \"阳信县\",\n          \"无棣县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"菏泽市\",\n        \"area\": [\n          \"牡丹区\",\n          \"鄄城县\",\n          \"单县\",\n          \"郓城县\",\n          \"曹县\",\n          \"定陶县\",\n          \"巨野县\",\n          \"东明县\",\n          \"成武县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"河南省\",\n    \"city\": [\n      {\n        \"name\": \"郑州市\",\n        \"area\": [\n          \"中原区\",\n          \"金水区\",\n          \"二七区\",\n          \"管城回族区\",\n          \"上街区\",\n          \"惠济区\",\n          \"巩义市\",\n          \"新郑市\",\n          \"新密市\",\n          \"登封市\",\n          \"荥阳市\",\n          \"中牟县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"开封市\",\n        \"area\": [\n          \"鼓楼区\",\n          \"龙亭区\",\n          \"顺河回族区\",\n          \"禹王台区\",\n          \"金明区\",\n          \"开封县\",\n          \"尉氏县\",\n          \"兰考县\",\n          \"杞县\",\n          \"通许县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"洛阳市\",\n        \"area\": [\n          \"西工区\",\n          \"老城区\",\n          \"涧西区\",\n          \"瀍河回族区\",\n          \"洛龙区\",\n          \"吉利区\",\n          \"偃师市\",\n          \"孟津县\",\n          \"汝阳县\",\n          \"伊川县\",\n          \"洛宁县\",\n          \"嵩县\",\n          \"宜阳县\",\n          \"新安县\",\n          \"栾川县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"平顶山市\",\n        \"area\": [\n          \"新华区\",\n          \"卫东区\",\n          \"湛河区\",\n          \"石龙区\",\n          \"汝州市\",\n          \"舞钢市\",\n          \"宝丰县\",\n          \"叶县\",\n          \"郏县\",\n          \"鲁山县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"安阳市\",\n        \"area\": [\n          \"北关区\",\n          \"文峰区\",\n          \"殷都区\",\n          \"龙安区\",\n          \"林州市\",\n          \"安阳县\",\n          \"滑县\",\n          \"内黄县\",\n          \"汤阴县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"鹤壁市\",\n        \"area\": [\n          \"淇滨区\",\n          \"山城区\",\n          \"鹤山区\",\n          \"浚县\",\n          \"淇县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"新乡市\",\n        \"area\": [\n          \"卫滨区\",\n          \"红旗区\",\n          \"凤泉区\",\n          \"牧野区\",\n          \"卫辉市\",\n          \"辉县市\",\n          \"新乡县\",\n          \"获嘉县\",\n          \"原阳县\",\n          \"长垣县\",\n          \"封丘县\",\n          \"延津县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"焦作市\",\n        \"area\": [\n          \"解放区\",\n          \"中站区\",\n          \"马村区\",\n          \"山阳区\",\n          \"沁阳市\",\n          \"孟州市\",\n          \"修武县\",\n          \"温县\",\n          \"武陟县\",\n          \"博爱县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"濮阳市\",\n        \"area\": [\n          \"华龙区\",\n          \"濮阳县\",\n          \"南乐县\",\n          \"台前县\",\n          \"清丰县\",\n          \"范县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"许昌市\",\n        \"area\": [\n          \"魏都区\",\n          \"禹州市\",\n          \"长葛市\",\n          \"许昌县\",\n          \"鄢陵县\",\n          \"襄城县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"漯河市\",\n        \"area\": [\n          \"源汇区\",\n          \"郾城区\",\n          \"召陵区\",\n          \"临颍县\",\n          \"舞阳县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"三门峡市\",\n        \"area\": [\n          \"湖滨区\",\n          \"义马市\",\n          \"灵宝市\",\n          \"渑池县\",\n          \"卢氏县\",\n          \"陕县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"南阳市\",\n        \"area\": [\n          \"卧龙区\",\n          \"宛城区\",\n          \"邓州市\",\n          \"桐柏县\",\n          \"方城县\",\n          \"淅川县\",\n          \"镇平县\",\n          \"唐河县\",\n          \"南召县\",\n          \"内乡县\",\n          \"新野县\",\n          \"社旗县\",\n          \"西峡县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"商丘市\",\n        \"area\": [\n          \"梁园区\",\n          \"睢阳区\",\n          \"永城市\",\n          \"宁陵县\",\n          \"虞城县\",\n          \"民权县\",\n          \"夏邑县\",\n          \"柘城县\",\n          \"睢县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"信阳市\",\n        \"area\": [\n          \"浉河区\",\n          \"平桥区\",\n          \"潢川县\",\n          \"淮滨县\",\n          \"息县\",\n          \"新县\",\n          \"商城县\",\n          \"固始县\",\n          \"罗山县\",\n          \"光山县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"周口市\",\n        \"area\": [\n          \"川汇区\",\n          \"项城市\",\n          \"商水县\",\n          \"淮阳县\",\n          \"太康县\",\n          \"鹿邑县\",\n          \"西华县\",\n          \"扶沟县\",\n          \"沈丘县\",\n          \"郸城县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"驻马店市\",\n        \"area\": [\n          \"驿城区\",\n          \"确山县\",\n          \"新蔡县\",\n          \"上蔡县\",\n          \"西平县\",\n          \"泌阳县\",\n          \"平舆县\",\n          \"汝南县\",\n          \"遂平县\",\n          \"正阳县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"焦作市\",\n        \"area\": [\n          \"济源市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"湖北省\",\n    \"city\": [\n      {\n        \"name\": \"武汉市\",\n        \"area\": [\n          \"江岸区\",\n          \"武昌区\",\n          \"江汉区\",\n          \"硚口区\",\n          \"汉阳区\",\n          \"青山区\",\n          \"洪山区\",\n          \"东西湖区\",\n          \"汉南区\",\n          \"蔡甸区\",\n          \"江夏区\",\n          \"黄陂区\",\n          \"新洲区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"黄石市\",\n        \"area\": [\n          \"黄石港区\",\n          \"西塞山区\",\n          \"下陆区\",\n          \"铁山区\",\n          \"大冶市\",\n          \"阳新县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"十堰市\",\n        \"area\": [\n          \"张湾区\",\n          \"茅箭区\",\n          \"丹江口市\",\n          \"郧县\",\n          \"竹山县\",\n          \"房县\",\n          \"郧西县\",\n          \"竹溪县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"荆州市\",\n        \"area\": [\n          \"沙市区\",\n          \"荆州区\",\n          \"洪湖市\",\n          \"石首市\",\n          \"松滋市\",\n          \"监利县\",\n          \"公安县\",\n          \"江陵县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"宜昌市\",\n        \"area\": [\n          \"西陵区\",\n          \"伍家岗区\",\n          \"点军区\",\n          \"猇亭区\",\n          \"夷陵区\",\n          \"宜都市\",\n          \"当阳市\",\n          \"枝江市\",\n          \"秭归县\",\n          \"远安县\",\n          \"兴山县\",\n          \"五峰土家族自治县\",\n          \"长阳土家族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"襄樊市\",\n        \"area\": [\n          \"襄城区\",\n          \"樊城区\",\n          \"襄阳区\",\n          \"老河口市\",\n          \"枣阳市\",\n          \"宜城市\",\n          \"南漳县\",\n          \"谷城县\",\n          \"保康县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"鄂州市\",\n        \"area\": [\n          \"鄂城区\",\n          \"华容区\",\n          \"梁子湖区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"荆门市\",\n        \"area\": [\n          \"东宝区\",\n          \"掇刀区\",\n          \"钟祥市\",\n          \"京山县\",\n          \"沙洋县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"孝感市\",\n        \"area\": [\n          \"孝南区\",\n          \"应城市\",\n          \"安陆市\",\n          \"汉川市\",\n          \"云梦县\",\n          \"大悟县\",\n          \"孝昌县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"黄冈市\",\n        \"area\": [\n          \"黄州区\",\n          \"麻城市\",\n          \"武穴市\",\n          \"红安县\",\n          \"罗田县\",\n          \"浠水县\",\n          \"蕲春县\",\n          \"黄梅县\",\n          \"英山县\",\n          \"团风县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"咸宁市\",\n        \"area\": [\n          \"咸安区\",\n          \"赤壁市\",\n          \"嘉鱼县\",\n          \"通山县\",\n          \"崇阳县\",\n          \"通城县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"随州市\",\n        \"area\": [\n          \"曾都区\",\n          \"广水市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"恩施土家族苗族自治州\",\n        \"area\": [\n          \"恩施市\",\n          \"利川市\",\n          \"建始县\",\n          \"来凤县\",\n          \"巴东县\",\n          \"鹤峰县\",\n          \"宣恩县\",\n          \"咸丰县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"仙桃市\",\n        \"area\": [\n          \"仙桃\"\n        ]\n      },\n      {\n        \"name\": \"天门市\",\n        \"area\": [\n          \"天门\"\n        ]\n      },\n      {\n        \"name\": \"潜江市\",\n        \"area\": [\n          \"潜江\"\n        ]\n      },\n      {\n        \"name\": \"神农架林区\",\n        \"area\": [\n          \"神农架林区\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"湖南省\",\n    \"city\": [\n      {\n        \"name\": \"长沙市\",\n        \"area\": [\n          \"岳麓区\",\n          \"芙蓉区\",\n          \"天心区\",\n          \"开福区\",\n          \"雨花区\",\n          \"浏阳市\",\n          \"长沙县\",\n          \"望城县\",\n          \"宁乡县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"株洲市\",\n        \"area\": [\n          \"天元区\",\n          \"荷塘区\",\n          \"芦淞区\",\n          \"石峰区\",\n          \"醴陵市\",\n          \"株洲县\",\n          \"炎陵县\",\n          \"茶陵县\",\n          \"攸县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"湘潭市\",\n        \"area\": [\n          \"岳塘区\",\n          \"雨湖区\",\n          \"湘乡市\",\n          \"韶山市\",\n          \"湘潭县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"衡阳市\",\n        \"area\": [\n          \"雁峰区\",\n          \"珠晖区\",\n          \"石鼓区\",\n          \"蒸湘区\",\n          \"南岳区\",\n          \"耒阳市\",\n          \"常宁市\",\n          \"衡阳县\",\n          \"衡东县\",\n          \"衡山县\",\n          \"衡南县\",\n          \"祁东县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"邵阳市\",\n        \"area\": [\n          \"双清区\",\n          \"大祥区\",\n          \"北塔区\",\n          \"武冈市\",\n          \"邵东县\",\n          \"洞口县\",\n          \"新邵县\",\n          \"绥宁县\",\n          \"新宁县\",\n          \"邵阳县\",\n          \"隆回县\",\n          \"城步苗族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"岳阳市\",\n        \"area\": [\n          \"岳阳楼区\",\n          \"云溪区\",\n          \"君山区\",\n          \"临湘市\",\n          \"汨罗市\",\n          \"岳阳县\",\n          \"湘阴县\",\n          \"平江县\",\n          \"华容县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"常德市\",\n        \"area\": [\n          \"武陵区\",\n          \"鼎城区\",\n          \"津市市\",\n          \"澧县\",\n          \"临澧县\",\n          \"桃源县\",\n          \"汉寿县\",\n          \"安乡县\",\n          \"石门县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"张家界市\",\n        \"area\": [\n          \"永定区\",\n          \"武陵源区\",\n          \"慈利县\",\n          \"桑植县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"益阳市\",\n        \"area\": [\n          \"赫山区\",\n          \"资阳区\",\n          \"沅江市\",\n          \"桃江县\",\n          \"南县\",\n          \"安化县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"郴州市\",\n        \"area\": [\n          \"北湖区\",\n          \"苏仙区\",\n          \"资兴市\",\n          \"宜章县\",\n          \"汝城县\",\n          \"安仁县\",\n          \"嘉禾县\",\n          \"临武县\",\n          \"桂东县\",\n          \"永兴县\",\n          \"桂阳县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"永州市\",\n        \"area\": [\n          \"冷水滩区\",\n          \"零陵区\",\n          \"祁阳县\",\n          \"蓝山县\",\n          \"宁远县\",\n          \"新田县\",\n          \"东安县\",\n          \"江永县\",\n          \"道县\",\n          \"双牌县\",\n          \"江华瑶族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"怀化市\",\n        \"area\": [\n          \"鹤城区\",\n          \"洪江市\",\n          \"会同县\",\n          \"沅陵县\",\n          \"辰溪县\",\n          \"溆浦县\",\n          \"中方县\",\n          \"新晃侗族自治县\",\n          \"芷江侗族自治县\",\n          \"通道侗族自治县\",\n          \"靖州苗族侗族自治县\",\n          \"麻阳苗族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"娄底市\",\n        \"area\": [\n          \"娄星区\",\n          \"冷水江市\",\n          \"涟源市\",\n          \"新化县\",\n          \"双峰县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"湘西土家族苗族自治州\",\n        \"area\": [\n          \"吉首市\",\n          \"古丈县\",\n          \"龙山县\",\n          \"永顺县\",\n          \"凤凰县\",\n          \"泸溪县\",\n          \"保靖县\",\n          \"花垣县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"广东省\",\n    \"city\": [\n      {\n        \"name\": \"广州市\",\n        \"area\": [\n          \"越秀区\",\n          \"荔湾区\",\n          \"海珠区\",\n          \"天河区\",\n          \"白云区\",\n          \"黄埔区\",\n          \"番禺区\",\n          \"花都区\",\n          \"南沙区\",\n          \"萝岗区\",\n          \"增城市\",\n          \"从化市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"深圳市\",\n        \"area\": [\n          \"福田区\",\n          \"罗湖区\",\n          \"南山区\",\n          \"宝安区\",\n          \"龙岗区\",\n          \"盐田区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"东莞市\",\n        \"area\": [\n          \"莞城\",\n          \"常平\",\n          \"塘厦\",\n          \"塘厦\",\n          \"塘厦\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"中山市\",\n        \"area\": [\n          \"中山\"\n        ]\n      },\n      {\n        \"name\": \"潮州市\",\n        \"area\": [\n          \"湘桥区\",\n          \"潮安县\",\n          \"饶平县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"揭阳市\",\n        \"area\": [\n          \"榕城区\",\n          \"揭东县\",\n          \"揭西县\",\n          \"惠来县\",\n          \"普宁市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"云浮市\",\n        \"area\": [\n          \"云城区\",\n          \"新兴县\",\n          \"郁南县\",\n          \"云安县\",\n          \"罗定市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"珠海市\",\n        \"area\": [\n          \"香洲区\",\n          \"斗门区\",\n          \"金湾区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"汕头市\",\n        \"area\": [\n          \"金平区\",\n          \"濠江区\",\n          \"龙湖区\",\n          \"潮阳区\",\n          \"潮南区\",\n          \"澄海区\",\n          \"南澳县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"韶关市\",\n        \"area\": [\n          \"浈江区\",\n          \"武江区\",\n          \"曲江区\",\n          \"乐昌市\",\n          \"南雄市\",\n          \"始兴县\",\n          \"仁化县\",\n          \"翁源县\",\n          \"新丰县\",\n          \"乳源瑶族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"佛山市\",\n        \"area\": [\n          \"禅城区\",\n          \"南海区\",\n          \"顺德区\",\n          \"三水区\",\n          \"高明区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"江门市\",\n        \"area\": [\n          \"蓬江区\",\n          \"江海区\",\n          \"新会区\",\n          \"恩平市\",\n          \"台山市\",\n          \"开平市\",\n          \"鹤山市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"湛江市\",\n        \"area\": [\n          \"赤坎区\",\n          \"霞山区\",\n          \"坡头区\",\n          \"麻章区\",\n          \"吴川市\",\n          \"廉江市\",\n          \"雷州市\",\n          \"遂溪县\",\n          \"徐闻县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"茂名市\",\n        \"area\": [\n          \"茂南区\",\n          \"茂港区\",\n          \"化州市\",\n          \"信宜市\",\n          \"高州市\",\n          \"电白县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"肇庆市\",\n        \"area\": [\n          \"端州区\",\n          \"鼎湖区\",\n          \"高要市\",\n          \"四会市\",\n          \"广宁县\",\n          \"怀集县\",\n          \"封开县\",\n          \"德庆县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"惠州市\",\n        \"area\": [\n          \"惠城区\",\n          \"惠阳区\",\n          \"博罗县\",\n          \"惠东县\",\n          \"龙门县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"梅州市\",\n        \"area\": [\n          \"梅江区\",\n          \"兴宁市\",\n          \"梅县\",\n          \"大埔县\",\n          \"丰顺县\",\n          \"五华县\",\n          \"平远县\",\n          \"蕉岭县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"汕尾市\",\n        \"area\": [\n          \"城区\",\n          \"陆丰市\",\n          \"海丰县\",\n          \"陆河县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"河源市\",\n        \"area\": [\n          \"源城区\",\n          \"紫金县\",\n          \"龙川县\",\n          \"连平县\",\n          \"和平县\",\n          \"东源县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"阳江市\",\n        \"area\": [\n          \"江城区\",\n          \"阳春市\",\n          \"阳西县\",\n          \"阳东县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"清远市\",\n        \"area\": [\n          \"清城区\",\n          \"英德市\",\n          \"连州市\",\n          \"佛冈县\",\n          \"阳山县\",\n          \"清新县\",\n          \"连山壮族瑶族自治县\",\n          \"连南瑶族自治县\",\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"广西\",\n    \"city\": [\n      {\n        \"name\": \"南宁市\",\n        \"area\": [\n          \"青秀区\",\n          \"兴宁区\",\n          \"西乡塘区\",\n          \"良庆区\",\n          \"江南区\",\n          \"邕宁区\",\n          \"武鸣县\",\n          \"隆安县\",\n          \"马山县\",\n          \"上林县\",\n          \"宾阳县\",\n          \"横县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"柳州市\",\n        \"area\": [\n          \"城中区\",\n          \"鱼峰区\",\n          \"柳北区\",\n          \"柳南区\",\n          \"柳江县\",\n          \"柳城县\",\n          \"鹿寨县\",\n          \"融安县\",\n          \"融水苗族自治县\",\n          \"三江侗族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"桂林市\",\n        \"area\": [\n          \"象山区\",\n          \"秀峰区\",\n          \"叠彩区\",\n          \"七星区\",\n          \"雁山区\",\n          \"阳朔县\",\n          \"临桂县\",\n          \"灵川县\",\n          \"全州县\",\n          \"平乐县\",\n          \"兴安县\",\n          \"灌阳县\",\n          \"荔浦县\",\n          \"资源县\",\n          \"永福县\",\n          \"龙胜各族自治县\",\n          \"恭城瑶族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"梧州市\",\n        \"area\": [\n          \"万秀区\",\n          \"蝶山区\",\n          \"长洲区\",\n          \"岑溪市\",\n          \"苍梧县\",\n          \"藤县\",\n          \"蒙山县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"北海市\",\n        \"area\": [\n          \"海城区\",\n          \"银海区\",\n          \"铁山港区\",\n          \"合浦县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"防城港市\",\n        \"area\": [\n          \"港口区\",\n          \"防城区\",\n          \"东兴市\",\n          \"上思县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"钦州市\",\n        \"area\": [\n          \"钦南区\",\n          \"钦北区\",\n          \"灵山县\",\n          \"浦北县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"贵港市\",\n        \"area\": [\n          \"港北区\",\n          \"港南区\",\n          \"覃塘区\",\n          \"桂平市\",\n          \"平南县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"玉林市\",\n        \"area\": [\n          \"玉州区\",\n          \"北流市\",\n          \"容县\",\n          \"陆川县\",\n          \"博白县\",\n          \"兴业县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"百色市\",\n        \"area\": [\n          \"右江区\",\n          \"凌云县\",\n          \"平果县\",\n          \"西林县\",\n          \"乐业县\",\n          \"德保县\",\n          \"田林县\",\n          \"田阳县\",\n          \"靖西县\",\n          \"田东县\",\n          \"那坡县\",\n          \"隆林各族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"贺州市\",\n        \"area\": [\n          \"八步区\",\n          \"钟山县\",\n          \"昭平县\",\n          \"富川瑶族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"河池市\",\n        \"area\": [\n          \"金城江区\",\n          \"宜州市\",\n          \"天峨县\",\n          \"凤山县\",\n          \"南丹县\",\n          \"东兰县\",\n          \"都安瑶族自治县\",\n          \"罗城仫佬族自治县\",\n          \"巴马瑶族自治县\",\n          \"环江毛南族自治县\",\n          \"大化瑶族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"来宾市\",\n        \"area\": [\n          \"兴宾区\",\n          \"合山市\",\n          \"象州县\",\n          \"武宣县\",\n          \"忻城县\",\n          \"金秀瑶族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"崇左市\",\n        \"area\": [\n          \"江州区\",\n          \"凭祥市\",\n          \"宁明县\",\n          \"扶绥县\",\n          \"龙州县\",\n          \"大新县\",\n          \"天等县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他市\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"海南省\",\n    \"city\": [\n      {\n        \"name\": \"海口市\",\n        \"area\": [\n          \"龙华区\",\n          \"秀英区\",\n          \"琼山区\",\n          \"美兰区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"三亚市\",\n        \"area\": [\n          \"三亚市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"五指山市\",\n        \"area\": [\n          \"五指山\"\n        ]\n      },\n      {\n        \"name\": \"琼海市\",\n        \"area\": [\n          \"琼海\"\n        ]\n      },\n      {\n        \"name\": \"儋州市\",\n        \"area\": [\n          \"儋州\"\n        ]\n      },\n      {\n        \"name\": \"文昌市\",\n        \"area\": [\n          \"文昌\"\n        ]\n      },\n      {\n        \"name\": \"万宁市\",\n        \"area\": [\n          \"万宁\"\n        ]\n      },\n      {\n        \"name\": \"东方市\",\n        \"area\": [\n          \"东方\"\n        ]\n      },\n      {\n        \"name\": \"澄迈县\",\n        \"area\": [\n          \"澄迈县\"\n        ]\n      },\n      {\n        \"name\": \"定安县\",\n        \"area\": [\n          \"定安县\"\n        ]\n      },\n      {\n        \"name\": \"屯昌县\",\n        \"area\": [\n          \"屯昌县\"\n        ]\n      },\n      {\n        \"name\": \"临高县\",\n        \"area\": [\n          \"临高县\"\n        ]\n      },\n      {\n        \"name\": \"白沙黎族自治县\",\n        \"area\": [\n          \"白沙黎族自治县\"\n        ]\n      },\n      {\n        \"name\": \"昌江黎族自治县\",\n        \"area\": [\n          \"昌江黎族自治县\"\n        ]\n      },\n      {\n        \"name\": \"乐东黎族自治县\",\n        \"area\": [\n          \"乐东黎族自治县\"\n        ]\n      },\n      {\n        \"name\": \"陵水黎族自治县\",\n        \"area\": [\n          \"陵水黎族自治县\"\n        ]\n      },\n      {\n        \"name\": \"保亭黎族苗族自治县\",\n        \"area\": [\n          \"保亭黎族苗族自治县\"\n        ]\n      },\n      {\n        \"name\": \"琼中黎族苗族自治县\",\n        \"area\": [\n          \"琼中黎族苗族自治县\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"重庆市\",\n    \"city\": [\n      {\n        \"name\": \"重庆市\",\n        \"area\": [\n          \"渝中区\",\n          \"大渡口区\",\n          \"江北区\",\n          \"南岸区\",\n          \"北碚区\",\n          \"渝北区\",\n          \"巴南区\",\n          \"长寿区\",\n          \"双桥区\",\n          \"沙坪坝区\",\n          \"万盛区\",\n          \"万州区\",\n          \"涪陵区\",\n          \"黔江区\",\n          \"永川区\",\n          \"合川区\",\n          \"江津区\",\n          \"九龙坡区\",\n          \"南川区\",\n          \"綦江县\",\n          \"潼南县\",\n          \"荣昌县\",\n          \"璧山县\",\n          \"大足县\",\n          \"铜梁县\",\n          \"梁平县\",\n          \"开县\",\n          \"忠县\",\n          \"城口县\",\n          \"垫江县\",\n          \"武隆县\",\n          \"丰都县\",\n          \"奉节县\",\n          \"云阳县\",\n          \"巫溪县\",\n          \"巫山县\",\n          \"石柱土家族自治县\",\n          \"秀山土家族苗族自治县\",\n          \"酉阳土家族苗族自治县\",\n          \"彭水苗族土家族自治县\",\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"四川省\",\n    \"city\": [\n      {\n        \"name\": \"成都市\",\n        \"area\": [\n          \"青羊区\",\n          \"锦江区\",\n          \"金牛区\",\n          \"武侯区\",\n          \"成华区\",\n          \"龙泉驿区\",\n          \"青白江区\",\n          \"新都区\",\n          \"温江区\",\n          \"都江堰市\",\n          \"彭州市\",\n          \"邛崃市\",\n          \"崇州市\",\n          \"金堂县\",\n          \"郫县\",\n          \"新津县\",\n          \"双流县\",\n          \"蒲江县\",\n          \"大邑县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"自贡市\",\n        \"area\": [\n          \"大安区\",\n          \"自流井区\",\n          \"贡井区\",\n          \"沿滩区\",\n          \"荣县\",\n          \"富顺县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"攀枝花市\",\n        \"area\": [\n          \"仁和区\",\n          \"米易县\",\n          \"盐边县\",\n          \"东区\",\n          \"西区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"泸州市\",\n        \"area\": [\n          \"江阳区\",\n          \"纳溪区\",\n          \"龙马潭区\",\n          \"泸县\",\n          \"合江县\",\n          \"叙永县\",\n          \"古蔺县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"德阳市\",\n        \"area\": [\n          \"旌阳区\",\n          \"广汉市\",\n          \"什邡市\",\n          \"绵竹市\",\n          \"罗江县\",\n          \"中江县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"绵阳市\",\n        \"area\": [\n          \"涪城区\",\n          \"游仙区\",\n          \"江油市\",\n          \"盐亭县\",\n          \"三台县\",\n          \"平武县\",\n          \"安县\",\n          \"梓潼县\",\n          \"北川羌族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"广元市\",\n        \"area\": [\n          \"元坝区\",\n          \"朝天区\",\n          \"青川县\",\n          \"旺苍县\",\n          \"剑阁县\",\n          \"苍溪县\",\n          \"市中区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"遂宁市\",\n        \"area\": [\n          \"船山区\",\n          \"安居区\",\n          \"射洪县\",\n          \"蓬溪县\",\n          \"大英县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"内江市\",\n        \"area\": [\n          \"市中区\",\n          \"东兴区\",\n          \"资中县\",\n          \"隆昌县\",\n          \"威远县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"乐山市\",\n        \"area\": [\n          \"市中区\",\n          \"五通桥区\",\n          \"沙湾区\",\n          \"金口河区\",\n          \"峨眉山市\",\n          \"夹江县\",\n          \"井研县\",\n          \"犍为县\",\n          \"沐川县\",\n          \"马边彝族自治县\",\n          \"峨边彝族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"南充\",\n        \"area\": [\n          \"顺庆区\",\n          \"高坪区\",\n          \"嘉陵区\",\n          \"阆中市\",\n          \"营山县\",\n          \"蓬安县\",\n          \"仪陇县\",\n          \"南部县\",\n          \"西充县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"眉山市\",\n        \"area\": [\n          \"东坡区\",\n          \"仁寿县\",\n          \"彭山县\",\n          \"洪雅县\",\n          \"丹棱县\",\n          \"青神县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"宜宾市\",\n        \"area\": [\n          \"翠屏区\",\n          \"宜宾县\",\n          \"兴文县\",\n          \"南溪县\",\n          \"珙县\",\n          \"长宁县\",\n          \"高县\",\n          \"江安县\",\n          \"筠连县\",\n          \"屏山县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"广安市\",\n        \"area\": [\n          \"广安区\",\n          \"华蓥市\",\n          \"岳池县\",\n          \"邻水县\",\n          \"武胜县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"达州市\",\n        \"area\": [\n          \"通川区\",\n          \"万源市\",\n          \"达县\",\n          \"渠县\",\n          \"宣汉县\",\n          \"开江县\",\n          \"大竹县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"雅安市\",\n        \"area\": [\n          \"雨城区\",\n          \"芦山县\",\n          \"石棉县\",\n          \"名山县\",\n          \"天全县\",\n          \"荥经县\",\n          \"宝兴县\",\n          \"汉源县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"巴中市\",\n        \"area\": [\n          \"巴州区\",\n          \"南江县\",\n          \"平昌县\",\n          \"通江县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"资阳市\",\n        \"area\": [\n          \"雁江区\",\n          \"简阳市\",\n          \"安岳县\",\n          \"乐至县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"阿坝藏族羌族自治州\",\n        \"area\": [\n          \"马尔康县\",\n          \"九寨沟县\",\n          \"红原县\",\n          \"汶川县\",\n          \"阿坝县\",\n          \"理县\",\n          \"若尔盖县\",\n          \"小金县\",\n          \"黑水县\",\n          \"金川县\",\n          \"松潘县\",\n          \"壤塘县\",\n          \"茂县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"甘孜藏族自治州\",\n        \"area\": [\n          \"康定县\",\n          \"丹巴县\",\n          \"炉霍县\",\n          \"九龙县\",\n          \"甘孜县\",\n          \"雅江县\",\n          \"新龙县\",\n          \"道孚县\",\n          \"白玉县\",\n          \"理塘县\",\n          \"德格县\",\n          \"乡城县\",\n          \"石渠县\",\n          \"稻城县\",\n          \"色达县\",\n          \"巴塘县\",\n          \"泸定县\",\n          \"得荣县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"凉山彝族自治州\",\n        \"area\": [\n          \"西昌市\",\n          \"美姑县\",\n          \"昭觉县\",\n          \"金阳县\",\n          \"甘洛县\",\n          \"布拖县\",\n          \"雷波县\",\n          \"普格县\",\n          \"宁南县\",\n          \"喜德县\",\n          \"会东县\",\n          \"越西县\",\n          \"会理县\",\n          \"盐源县\",\n          \"德昌县\",\n          \"冕宁县\",\n          \"木里藏族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"贵州省\",\n    \"city\": [\n      {\n        \"name\": \"贵阳市\",\n        \"area\": [\n          \"南明区\",\n          \"云岩区\",\n          \"花溪区\",\n          \"乌当区\",\n          \"白云区\",\n          \"小河区\",\n          \"清镇市\",\n          \"开阳县\",\n          \"修文县\",\n          \"息烽县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"六盘水市\",\n        \"area\": [\n          \"钟山区\",\n          \"水城县\",\n          \"盘县\",\n          \"六枝特区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"遵义市\",\n        \"area\": [\n          \"红花岗区\",\n          \"汇川区\",\n          \"赤水市\",\n          \"仁怀市\",\n          \"遵义县\",\n          \"绥阳县\",\n          \"桐梓县\",\n          \"习水县\",\n          \"凤冈县\",\n          \"正安县\",\n          \"余庆县\",\n          \"湄潭县\",\n          \"道真仡佬族苗族自治县\",\n          \"务川仡佬族苗族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"安顺市\",\n        \"area\": [\n          \"西秀区\",\n          \"普定县\",\n          \"平坝县\",\n          \"镇宁布依族苗族自治县\",\n          \"紫云苗族布依族自治县\",\n          \"关岭布依族苗族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"铜仁地区\",\n        \"area\": [\n          \"铜仁市\",\n          \"德江县\",\n          \"江口县\",\n          \"思南县\",\n          \"石阡县\",\n          \"玉屏侗族自治县\",\n          \"松桃苗族自治县\",\n          \"印江土家族苗族自治县\",\n          \"沿河土家族自治县\",\n          \"万山特区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"毕节地区\",\n        \"area\": [\n          \"毕节市\",\n          \"黔西县\",\n          \"大方县\",\n          \"织金县\",\n          \"金沙县\",\n          \"赫章县\",\n          \"纳雍县\",\n          \"威宁彝族回族苗族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"黔西南布依族苗族自治州\",\n        \"area\": [\n          \"兴义市\",\n          \"望谟县\",\n          \"兴仁县\",\n          \"普安县\",\n          \"册亨县\",\n          \"晴隆县\",\n          \"贞丰县\",\n          \"安龙县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"黔东南苗族侗族自治州\",\n        \"area\": [\n          \"凯里市\",\n          \"施秉县\",\n          \"从江县\",\n          \"锦屏县\",\n          \"镇远县\",\n          \"麻江县\",\n          \"台江县\",\n          \"天柱县\",\n          \"黄平县\",\n          \"榕江县\",\n          \"剑河县\",\n          \"三穗县\",\n          \"雷山县\",\n          \"黎平县\",\n          \"岑巩县\",\n          \"丹寨县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"黔南布依族苗族自治州\",\n        \"area\": [\n          \"都匀市\",\n          \"福泉市\",\n          \"贵定县\",\n          \"惠水县\",\n          \"罗甸县\",\n          \"瓮安县\",\n          \"荔波县\",\n          \"龙里县\",\n          \"平塘县\",\n          \"长顺县\",\n          \"独山县\",\n          \"三都水族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"云南省\",\n    \"city\": [\n      {\n        \"name\": \"昆明市\",\n        \"area\": [\n          \"盘龙区\",\n          \"五华区\",\n          \"官渡区\",\n          \"西山区\",\n          \"东川区\",\n          \"安宁市\",\n          \"呈贡县\",\n          \"晋宁县\",\n          \"富民县\",\n          \"宜良县\",\n          \"嵩明县\",\n          \"石林彝族自治县\",\n          \"禄劝彝族苗族自治县\",\n          \"寻甸回族彝族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"曲靖市\",\n        \"area\": [\n          \"麒麟区\",\n          \"宣威市\",\n          \"马龙县\",\n          \"沾益县\",\n          \"富源县\",\n          \"罗平县\",\n          \"师宗县\",\n          \"陆良县\",\n          \"会泽县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"玉溪市\",\n        \"area\": [\n          \"红塔区\",\n          \"江川县\",\n          \"澄江县\",\n          \"通海县\",\n          \"华宁县\",\n          \"易门县\",\n          \"峨山彝族自治县\",\n          \"新平彝族傣族自治县\",\n          \"元江哈尼族彝族傣族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"保山市\",\n        \"area\": [\n          \"隆阳区\",\n          \"施甸县\",\n          \"腾冲县\",\n          \"龙陵县\",\n          \"昌宁县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"昭通市\",\n        \"area\": [\n          \"昭阳区\",\n          \"鲁甸县\",\n          \"巧家县\",\n          \"盐津县\",\n          \"大关县\",\n          \"永善县\",\n          \"绥江县\",\n          \"镇雄县\",\n          \"彝良县\",\n          \"威信县\",\n          \"水富县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"丽江市\",\n        \"area\": [\n          \"古城区\",\n          \"永胜县\",\n          \"华坪县\",\n          \"玉龙纳西族自治县\",\n          \"宁蒗彝族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"普洱市\",\n        \"area\": [\n          \"思茅区\",\n          \"普洱哈尼族彝族自治县\",\n          \"墨江哈尼族自治县\",\n          \"景东彝族自治县\",\n          \"景谷傣族彝族自治县\",\n          \"镇沅彝族哈尼族拉祜族自治县\",\n          \"江城哈尼族彝族自治县\",\n          \"孟连傣族拉祜族佤族自治县\",\n          \"澜沧拉祜族自治县\",\n          \"西盟佤族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"临沧市\",\n        \"area\": [\n          \"临翔区\",\n          \"凤庆县\",\n          \"云县\",\n          \"永德县\",\n          \"镇康县\",\n          \"双江拉祜族佤族布朗族傣族自治县\",\n          \"耿马傣族佤族自治县\",\n          \"沧源佤族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"德宏傣族景颇族自治州\",\n        \"area\": [\n          \"潞西市\",\n          \"瑞丽市\",\n          \"梁河县\",\n          \"盈江县\",\n          \"陇川县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"怒江傈僳族自治州\",\n        \"area\": [\n          \"泸水县\",\n          \"福贡县\",\n          \"贡山独龙族怒族自治县\",\n          \"兰坪白族普米族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"迪庆藏族自治州\",\n        \"area\": [\n          \"香格里拉县\",\n          \"德钦县\",\n          \"维西傈僳族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"大理白族自治州\",\n        \"area\": [\n          \"大理市\",\n          \"祥云县\",\n          \"宾川县\",\n          \"弥渡县\",\n          \"永平县\",\n          \"云龙县\",\n          \"洱源县\",\n          \"剑川县\",\n          \"鹤庆县\",\n          \"漾濞彝族自治县\",\n          \"南涧彝族自治县\",\n          \"巍山彝族回族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"楚雄彝族自治州\",\n        \"area\": [\n          \"楚雄市\",\n          \"双柏县\",\n          \"牟定县\",\n          \"南华县\",\n          \"姚安县\",\n          \"大姚县\",\n          \"永仁县\",\n          \"元谋县\",\n          \"武定县\",\n          \"禄丰县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"红河哈尼族彝族自治州\",\n        \"area\": [\n          \"蒙自县\",\n          \"个旧市\",\n          \"开远市\",\n          \"绿春县\",\n          \"建水县\",\n          \"石屏县\",\n          \"弥勒县\",\n          \"泸西县\",\n          \"元阳县\",\n          \"红河县\",\n          \"金平苗族瑶族傣族自治县\",\n          \"河口瑶族自治县\",\n          \"屏边苗族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"文山壮族苗族自治州\",\n        \"area\": [\n          \"文山县\",\n          \"砚山县\",\n          \"西畴县\",\n          \"麻栗坡县\",\n          \"马关县\",\n          \"丘北县\",\n          \"广南县\",\n          \"富宁县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"西双版纳傣族自治州\",\n        \"area\": [\n          \"景洪市\",\n          \"勐海县\",\n          \"勐腊县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"西藏\",\n    \"city\": [\n      {\n        \"name\": \"拉萨市\",\n        \"area\": [\n          \"城关区\",\n          \"林周县\",\n          \"当雄县\",\n          \"尼木县\",\n          \"曲水县\",\n          \"堆龙德庆县\",\n          \"达孜县\",\n          \"墨竹工卡县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"那曲地区\",\n        \"area\": [\n          \"那曲县\",\n          \"嘉黎县\",\n          \"比如县\",\n          \"聂荣县\",\n          \"安多县\",\n          \"申扎县\",\n          \"索县\",\n          \"班戈县\",\n          \"巴青县\",\n          \"尼玛县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"昌都地区\",\n        \"area\": [\n          \"昌都县\",\n          \"江达县\",\n          \"贡觉县\",\n          \"类乌齐县\",\n          \"丁青县\",\n          \"察雅县\",\n          \"八宿县\",\n          \"左贡县\",\n          \"芒康县\",\n          \"洛隆县\",\n          \"边坝县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"林芝地区\",\n        \"area\": [\n          \"林芝县\",\n          \"工布江达县\",\n          \"米林县\",\n          \"墨脱县\",\n          \"波密县\",\n          \"察隅县\",\n          \"朗县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"山南地区\",\n        \"area\": [\n          \"乃东县\",\n          \"扎囊县\",\n          \"贡嘎县\",\n          \"桑日县\",\n          \"琼结县\",\n          \"曲松县\",\n          \"措美县\",\n          \"洛扎县\",\n          \"加查县\",\n          \"隆子县\",\n          \"错那县\",\n          \"浪卡子县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"日喀则地区\",\n        \"area\": [\n          \"日喀则市\",\n          \"南木林县\",\n          \"江孜县\",\n          \"定日县\",\n          \"萨迦县\",\n          \"拉孜县\",\n          \"昂仁县\",\n          \"谢通门县\",\n          \"白朗县\",\n          \"仁布县\",\n          \"康马县\",\n          \"定结县\",\n          \"仲巴县\",\n          \"亚东县\",\n          \"吉隆县\",\n          \"聂拉木县\",\n          \"萨嘎县\",\n          \"岗巴县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"阿里地区\",\n        \"area\": [\n          \"噶尔县\",\n          \"普兰县\",\n          \"札达县\",\n          \"日土县\",\n          \"革吉县\",\n          \"改则县\",\n          \"措勤县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"陕西省\",\n    \"city\": [\n      {\n        \"name\": \"西安市\",\n        \"area\": [\n          \"莲湖区\",\n          \"新城区\",\n          \"碑林区\",\n          \"雁塔区\",\n          \"灞桥区\",\n          \"未央区\",\n          \"阎良区\",\n          \"临潼区\",\n          \"长安区\",\n          \"高陵县\",\n          \"蓝田县\",\n          \"户县\",\n          \"周至县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"铜川市\",\n        \"area\": [\n          \"耀州区\",\n          \"王益区\",\n          \"印台区\",\n          \"宜君县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"宝鸡市\",\n        \"area\": [\n          \"渭滨区\",\n          \"金台区\",\n          \"陈仓区\",\n          \"岐山县\",\n          \"凤翔县\",\n          \"陇县\",\n          \"太白县\",\n          \"麟游县\",\n          \"扶风县\",\n          \"千阳县\",\n          \"眉县\",\n          \"凤县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"咸阳市\",\n        \"area\": [\n          \"秦都区\",\n          \"渭城区\",\n          \"杨陵区\",\n          \"兴平市\",\n          \"礼泉县\",\n          \"泾阳县\",\n          \"永寿县\",\n          \"三原县\",\n          \"彬县\",\n          \"旬邑县\",\n          \"长武县\",\n          \"乾县\",\n          \"武功县\",\n          \"淳化县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"渭南市\",\n        \"area\": [\n          \"临渭区\",\n          \"韩城市\",\n          \"华阴市\",\n          \"蒲城县\",\n          \"潼关县\",\n          \"白水县\",\n          \"澄城县\",\n          \"华县\",\n          \"合阳县\",\n          \"富平县\",\n          \"大荔县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"延安市\",\n        \"area\": [\n          \"宝塔区\",\n          \"安塞县\",\n          \"洛川县\",\n          \"子长县\",\n          \"黄陵县\",\n          \"延川县\",\n          \"富县\",\n          \"延长县\",\n          \"甘泉县\",\n          \"宜川县\",\n          \"志丹县\",\n          \"黄龙县\",\n          \"吴起县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"汉中市\",\n        \"area\": [\n          \"汉台区\",\n          \"留坝县\",\n          \"镇巴县\",\n          \"城固县\",\n          \"南郑县\",\n          \"洋县\",\n          \"宁强县\",\n          \"佛坪县\",\n          \"勉县\",\n          \"西乡县\",\n          \"略阳县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"榆林市\",\n        \"area\": [\n          \"榆阳区\",\n          \"清涧县\",\n          \"绥德县\",\n          \"神木县\",\n          \"佳县\",\n          \"府谷县\",\n          \"子洲县\",\n          \"靖边县\",\n          \"横山县\",\n          \"米脂县\",\n          \"吴堡县\",\n          \"定边县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"安康市\",\n        \"area\": [\n          \"汉滨区\",\n          \"紫阳县\",\n          \"岚皋县\",\n          \"旬阳县\",\n          \"镇坪县\",\n          \"平利县\",\n          \"石泉县\",\n          \"宁陕县\",\n          \"白河县\",\n          \"汉阴县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"商洛市\",\n        \"area\": [\n          \"商州区\",\n          \"镇安县\",\n          \"山阳县\",\n          \"洛南县\",\n          \"商南县\",\n          \"丹凤县\",\n          \"柞水县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"甘肃省\",\n    \"city\": [\n      {\n        \"name\": \"兰州市\",\n        \"area\": [\n          \"城关区\",\n          \"七里河区\",\n          \"西固区\",\n          \"安宁区\",\n          \"红古区\",\n          \"永登县\",\n          \"皋兰县\",\n          \"榆中县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"嘉峪关市\",\n        \"area\": [\n          \"嘉峪关市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"金昌市\",\n        \"area\": [\n          \"金川区\",\n          \"永昌县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"白银市\",\n        \"area\": [\n          \"白银区\",\n          \"平川区\",\n          \"靖远县\",\n          \"会宁县\",\n          \"景泰县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"天水市\",\n        \"area\": [\n          \"清水县\",\n          \"秦安县\",\n          \"甘谷县\",\n          \"武山县\",\n          \"张家川回族自治县\",\n          \"北道区\",\n          \"秦城区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"武威市\",\n        \"area\": [\n          \"凉州区\",\n          \"民勤县\",\n          \"古浪县\",\n          \"天祝藏族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"酒泉市\",\n        \"area\": [\n          \"肃州区\",\n          \"玉门市\",\n          \"敦煌市\",\n          \"金塔县\",\n          \"肃北蒙古族自治县\",\n          \"阿克塞哈萨克族自治县\",\n          \"安西县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"张掖市\",\n        \"area\": [\n          \"甘州区\",\n          \"民乐县\",\n          \"临泽县\",\n          \"高台县\",\n          \"山丹县\",\n          \"肃南裕固族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"庆阳市\",\n        \"area\": [\n          \"西峰区\",\n          \"庆城县\",\n          \"环县\",\n          \"华池县\",\n          \"合水县\",\n          \"正宁县\",\n          \"宁县\",\n          \"镇原县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"平凉市\",\n        \"area\": [\n          \"崆峒区\",\n          \"泾川县\",\n          \"灵台县\",\n          \"崇信县\",\n          \"华亭县\",\n          \"庄浪县\",\n          \"静宁县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"定西市\",\n        \"area\": [\n          \"安定区\",\n          \"通渭县\",\n          \"临洮县\",\n          \"漳县\",\n          \"岷县\",\n          \"渭源县\",\n          \"陇西县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"陇南市\",\n        \"area\": [\n          \"武都区\",\n          \"成县\",\n          \"宕昌县\",\n          \"康县\",\n          \"文县\",\n          \"西和县\",\n          \"礼县\",\n          \"两当县\",\n          \"徽县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"临夏回族自治州\",\n        \"area\": [\n          \"临夏市\",\n          \"临夏县\",\n          \"康乐县\",\n          \"永靖县\",\n          \"广河县\",\n          \"和政县\",\n          \"东乡族自治县\",\n          \"积石山保安族东乡族撒拉族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"甘南藏族自治州\",\n        \"area\": [\n          \"合作市\",\n          \"临潭县\",\n          \"卓尼县\",\n          \"舟曲县\",\n          \"迭部县\",\n          \"玛曲县\",\n          \"碌曲县\",\n          \"夏河县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"青海省\",\n    \"city\": [\n      {\n        \"name\": \"西宁市\",\n        \"area\": [\n          \"城中区\",\n          \"城东区\",\n          \"城西区\",\n          \"城北区\",\n          \"湟源县\",\n          \"湟中县\",\n          \"大通回族土族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"海东地区\",\n        \"area\": [\n          \"平安县\",\n          \"乐都县\",\n          \"民和回族土族自治县\",\n          \"互助土族自治县\",\n          \"化隆回族自治县\",\n          \"循化撒拉族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"海北藏族自治州\",\n        \"area\": [\n          \"海晏县\",\n          \"祁连县\",\n          \"刚察县\",\n          \"门源回族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"海南藏族自治州\",\n        \"area\": [\n          \"共和县\",\n          \"同德县\",\n          \"贵德县\",\n          \"兴海县\",\n          \"贵南县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"黄南藏族自治州\",\n        \"area\": [\n          \"同仁县\",\n          \"尖扎县\",\n          \"泽库县\",\n          \"河南蒙古族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"果洛藏族自治州\",\n        \"area\": [\n          \"玛沁县\",\n          \"班玛县\",\n          \"甘德县\",\n          \"达日县\",\n          \"久治县\",\n          \"玛多县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"玉树藏族自治州\",\n        \"area\": [\n          \"玉树县\",\n          \"杂多县\",\n          \"称多县\",\n          \"治多县\",\n          \"囊谦县\",\n          \"曲麻莱县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"海西蒙古族藏族自治州\",\n        \"area\": [\n          \"德令哈市\",\n          \"格尔木市\",\n          \"乌兰县\",\n          \"都兰县\",\n          \"天峻县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"宁夏\",\n    \"city\": [\n      {\n        \"name\": \"银川市\",\n        \"area\": [\n          \"兴庆区\",\n          \"西夏区\",\n          \"金凤区\",\n          \"灵武市\",\n          \"永宁县\",\n          \"贺兰县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"石嘴山市\",\n        \"area\": [\n          \"大武口区\",\n          \"惠农区\",\n          \"平罗县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"吴忠市\",\n        \"area\": [\n          \"利通区\",\n          \"青铜峡市\",\n          \"盐池县\",\n          \"同心县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"固原市\",\n        \"area\": [\n          \"原州区\",\n          \"西吉县\",\n          \"隆德县\",\n          \"泾源县\",\n          \"彭阳县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"中卫市\",\n        \"area\": [\n          \"沙坡头区\",\n          \"中宁县\",\n          \"海原县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"新疆\",\n    \"city\": [\n      {\n        \"name\": \"乌鲁木齐市\",\n        \"area\": [\n          \"天山区\",\n          \"沙依巴克区\",\n          \"新市区\",\n          \"水磨沟区\",\n          \"头屯河区\",\n          \"达坂城区\",\n          \"东山区\",\n          \"乌鲁木齐县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"克拉玛依市\",\n        \"area\": [\n          \"克拉玛依区\",\n          \"独山子区\",\n          \"白碱滩区\",\n          \"乌尔禾区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"吐鲁番地区\",\n        \"area\": [\n          \"吐鲁番市\",\n          \"托克逊县\",\n          \"鄯善县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"哈密地区\",\n        \"area\": [\n          \"哈密市\",\n          \"伊吾县\",\n          \"巴里坤哈萨克自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"和田地区\",\n        \"area\": [\n          \"和田市\",\n          \"和田县\",\n          \"洛浦县\",\n          \"民丰县\",\n          \"皮山县\",\n          \"策勒县\",\n          \"于田县\",\n          \"墨玉县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"阿克苏地区\",\n        \"area\": [\n          \"阿克苏市\",\n          \"温宿县\",\n          \"沙雅县\",\n          \"拜城县\",\n          \"阿瓦提县\",\n          \"库车县\",\n          \"柯坪县\",\n          \"新和县\",\n          \"乌什县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"喀什地区\",\n        \"area\": [\n          \"喀什市\",\n          \"巴楚县\",\n          \"泽普县\",\n          \"伽师县\",\n          \"叶城县\",\n          \"岳普湖县\",\n          \"疏勒县\",\n          \"麦盖提县\",\n          \"英吉沙县\",\n          \"莎车县\",\n          \"疏附县\",\n          \"塔什库尔干塔吉克自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"克孜勒苏柯尔克孜自治州\",\n        \"area\": [\n          \"阿图什市\",\n          \"阿合奇县\",\n          \"乌恰县\",\n          \"阿克陶县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"巴音郭楞蒙古自治州\",\n        \"area\": [\n          \"库尔勒市\",\n          \"和静县\",\n          \"尉犁县\",\n          \"和硕县\",\n          \"且末县\",\n          \"博湖县\",\n          \"轮台县\",\n          \"若羌县\",\n          \"焉耆回族自治县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"昌吉回族自治州\",\n        \"area\": [\n          \"昌吉市\",\n          \"阜康市\",\n          \"奇台县\",\n          \"玛纳斯县\",\n          \"吉木萨尔县\",\n          \"呼图壁县\",\n          \"木垒哈萨克自治县\",\n          \"米泉市\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"博尔塔拉蒙古自治州\",\n        \"area\": [\n          \"博乐市\",\n          \"精河县\",\n          \"温泉县\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"石河子\",\n        \"area\": [\n          \"石河子\"\n        ]\n      },\n      {\n        \"name\": \"阿拉尔\",\n        \"area\": [\n          \"阿拉尔\"\n        ]\n      },\n      {\n        \"name\": \"图木舒克\",\n        \"area\": [\n          \"图木舒克\"\n        ]\n      },\n      {\n        \"name\": \"五家渠\",\n        \"area\": [\n          \"五家渠\"\n        ]\n      },\n      {\n        \"name\": \"伊犁哈萨克自治州\",\n        \"area\": [\n          \"伊宁市\",\n          \"奎屯市\",\n          \"伊宁县\",\n          \"特克斯县\",\n          \"尼勒克县\",\n          \"昭苏县\",\n          \"新源县\",\n          \"霍城县\",\n          \"巩留县\",\n          \"察布查尔锡伯自治县\",\n          \"塔城地区\",\n          \"阿勒泰地区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"其他\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"台湾省\",\n    \"city\": [\n      {\n        \"name\": \"台北市\",\n        \"area\": [\n          \"内湖区\",\n          \"南港区\",\n          \"中正区\",\n          \"万华区\",\n          \"大同区\",\n          \"中山区\",\n          \"松山区\",\n          \"大安区\",\n          \"信义区\",\n          \"文山区\",\n          \"士林区\",\n          \"北投区\"\n        ]\n      },\n      {\n        \"name\": \"新北市\",\n        \"area\": [\n          \"板桥区\",\n          \"汐止区\",\n          \"新店区\",\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"桃园市\",\n        \"area\": [\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"台中市\",\n        \"area\": [\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"台南市\",\n        \"area\": [\n          \"其他\"\n        ]\n      },\n      {\n        \"name\": \"高雄市\",\n        \"area\": [\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"澳门\",\n    \"city\": [\n      {\n        \"name\": \"澳门\",\n        \"area\": [\n          \"花地玛堂区\",\n          \"圣安多尼堂区\",\n          \"大堂区\",\n          \"望德堂区\",\n          \"风顺堂区\",\n          \"嘉模堂区\",\n          \"圣方济各堂区\",\n          \"路凼\",\n          \"其他\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"香港\",\n    \"city\": [\n      {\n        \"name\": \"香港\",\n        \"area\": [\n          \"深水埗区\",\n          \"油尖旺区\",\n          \"九龙城区\",\n          \"黄大仙区\",\n          \"观塘区\",\n          \"北区\",\n          \"大埔区\",\n          \"沙田区\",\n          \"西贡区\",\n          \"元朗区\",\n          \"屯门区\",\n          \"荃湾区\",\n          \"葵青区\",\n          \"离岛区\",\n          \"中西区\",\n          \"湾仔区\",\n          \"东区\",\n          \"南区\",\n          \"其他\"\n        ]\n      }\n    ]\n  }\n]\n\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/sensitivewordsfiliter/BaseSearch.java",
    "content": "package top.flya.system.utils.sensitivewordsfiliter;\n\nimport java.util.ArrayList;\nimport java.util.Hashtable;\nimport java.util.List;\nimport java.util.Map;\n\npublic class BaseSearch {\n    protected TrieNode2[] _first = new TrieNode2[Character.MAX_VALUE + 1];\n    protected String[] _keywords;\n\n\n    /**\n     * 设置关键字\n     *\n     * @param keywords 关键字列表\n     */\n    public void SetKeywords(List<String> keywords) {\n        _keywords = new String[keywords.size()];\n        _keywords = keywords.toArray(_keywords);\n        SetKeywords();\n    }\n\n    protected void SetKeywords() {\n        TrieNode root = new TrieNode();\n        Map<Integer, List<TrieNode>> allNodeLayers = new Hashtable<Integer, List<TrieNode>>();\n        for (int i = 0; i < _keywords.length; i++) {\n            String p = _keywords[i];\n            TrieNode nd = root;\n            for (int j = 0; j < p.length(); j++) {\n                nd = nd.Add(p.charAt(j));\n                if (nd.Layer == 0) {\n                    nd.Layer = j + 1;\n                    if (allNodeLayers.containsKey(nd.Layer) == false) {\n                        List<TrieNode> nodes = new ArrayList<TrieNode>();\n                        nodes.add(nd);\n                        allNodeLayers.put(nd.Layer, nodes);\n                    } else {\n                        allNodeLayers.get(nd.Layer).add(nd);\n                    }\n                }\n            }\n            nd.SetResults(i);\n        }\n\n        List<TrieNode> allNode = new ArrayList<TrieNode>();\n        allNode.add(root);\n        for (int i = 0; i < allNodeLayers.size(); i++) { // 注意 这里不能用 keySet()\n            List<TrieNode> nodes = allNodeLayers.get(i + 1);\n            for (int j = 0; j < nodes.size(); j++) {\n                allNode.add(nodes.get(j));\n            }\n        }\n        allNodeLayers.clear();\n        allNodeLayers = null;\n\n        for (int i = 1; i < allNode.size(); i++) {\n            TrieNode nd = allNode.get(i);\n            nd.Index = i;\n            TrieNode r = nd.Parent.Failure;\n            Character c = nd.Char;\n            while (r != null && !r.m_values.containsKey(c))\n                r = r.Failure;\n            if (r == null)\n                nd.Failure = root;\n            else {\n                nd.Failure = r.m_values.get(c);\n                for (Integer result : nd.Failure.Results) {\n                    nd.SetResults(result);\n                }\n            }\n        }\n        root.Failure = root;\n\n        List<TrieNode2> allNode2 = new ArrayList<TrieNode2>();\n        for (int i = 0; i < allNode.size(); i++) {\n            allNode2.add(new TrieNode2());\n        }\n        for (int i = 0; i < allNode2.size(); i++) {\n            TrieNode oldNode = allNode.get(i);\n            TrieNode2 newNode = allNode2.get(i);\n\n            for (Character key : oldNode.m_values.keySet()) {\n                TrieNode nd = oldNode.m_values.get(key);\n                newNode.Add(key, allNode2.get(nd.Index));\n            }\n            oldNode.Results.forEach(item -> {\n                newNode.SetResults(item);\n            });\n\n            oldNode = oldNode.Failure;\n            while (oldNode != root) {\n                for (Character key : oldNode.m_values.keySet()) {\n                    TrieNode nd = oldNode.m_values.get(key);\n                    if (newNode.HasKey(key) == false) {\n                        newNode.Add(key, allNode2.get(nd.Index));\n                    }\n                }\n                oldNode.Results.forEach(item -> {\n                    newNode.SetResults(item);\n                });\n                oldNode = oldNode.Failure;\n            }\n        }\n        allNode.clear();\n        allNode = null;\n        root = null;\n\n        TrieNode2[] first = new TrieNode2[Character.MAX_VALUE + 1];\n        TrieNode2 root2 = allNode2.get(0);\n        for (Character key : root2.m_values.keySet()) {\n            TrieNode2 nd = root2.m_values.get(key);\n            first[(int) key] = nd;\n        }\n        _first = first;\n    }\n\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/sensitivewordsfiliter/TrieNode.java",
    "content": "package top.flya.system.utils.sensitivewordsfiliter;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\n\npublic class TrieNode implements Comparable<TrieNode> {\n\n    public int Index;\n    public int Layer;\n    public boolean End;\n    public char Char;\n    public List<Integer> Results;\n    public HashMap<Character, TrieNode> m_values;\n    public TrieNode Failure;\n    public TrieNode Parent;\n    public boolean IsWildcard;\n    public int WildcardLayer;\n    public boolean HasWildcard;\n\n\n    public TrieNode() {\n        m_values = new HashMap<Character, TrieNode>();\n        Results = new ArrayList<Integer>();\n    }\n\n    public TrieNode Add(final Character c) {\n        if (m_values.containsKey(c)) {\n            return m_values.get(c);\n        }\n        final TrieNode node = new TrieNode();\n        node.Parent = this;\n        node.Char = c;\n        m_values.put(c, node);\n        return node;\n    }\n\n    public void SetResults(final Integer index) {\n        if (End == false) {\n            End = true;\n        }\n        if (Results.contains(index) == false) {\n            Results.add(index);\n        }\n    }\n\n    @Override\n    public int compareTo(final TrieNode o) {\n        return this.Layer - o.Layer  ;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/sensitivewordsfiliter/TrieNode2.java",
    "content": "package top.flya.system.utils.sensitivewordsfiliter;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\n\n\npublic class TrieNode2 {\n    public boolean End;\n    public List<Integer> Results;\n    public HashMap<Character, TrieNode2> m_values;\n    private int minflag = Integer.MAX_VALUE;\n    private int maxflag = 0;\n\n    public TrieNode2()\n    {\n        Results = new ArrayList<Integer>();\n        m_values = new HashMap<Character, TrieNode2>();\n    }\n\n    public void Add(final char c, final TrieNode2 node3) {\n        if (minflag > c) {\n            minflag = c;\n        }\n        if (maxflag < c) {\n            maxflag = c;\n        }\n        m_values.put(c, node3);\n    }\n\n    public void SetResults(final Integer index) {\n        if (End == false) {\n            End = true;\n        }\n        if (Results.contains(index) == false) {\n            Results.add(index);\n        }\n    }\n\n    public boolean HasKey(final char c) {\n        if (minflag <= c && maxflag >= c) {\n            return m_values.containsKey(c);\n        }\n        return false;\n    }\n\n    public TrieNode2 GetValue(final char c) {\n        return m_values.get(c);\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/sensitivewordsfiliter/WordsSearch.java",
    "content": "package top.flya.system.utils.sensitivewordsfiliter;\n\n\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class WordsSearch extends BaseSearch {\n    public String[] _others;\n\n    /**\n     * 在文本中查找第一个关键字\n     *\n     * @param text 文本\n     * @return\n     */\n    public WordsSearchResult FindFirst(final String text) {\n        TrieNode2 ptr = null;\n        for (int i = 0; i < text.length(); i++) {\n            final char t = text.charAt(i);\n            TrieNode2 tn = null;\n            if (ptr == null) {\n                tn = _first[t];\n            } else {\n                if (ptr.HasKey(t) == false) {\n                    tn = _first[t];\n                } else {\n                    tn = ptr.GetValue(t);\n                }\n            }\n            if (tn != null) {\n                if (tn.End) {\n                    for (final Integer index : tn.Results) {\n                        final String key = _keywords[index];\n                        return new WordsSearchResult(key, i + 1 - key.length(), i, index);\n                    }\n                }\n            }\n            ptr = tn;\n        }\n        return null;\n    }\n\n    /**\n     * 在文本中查找所有的关键字\n     *\n     * @param text 文本\n     * @return\n     */\n    public List<WordsSearchResult> FindAll(final String text) {\n        TrieNode2 ptr = null;\n        final List<WordsSearchResult> list = new ArrayList<WordsSearchResult>();\n\n        for (int i = 0; i < text.length(); i++) {\n            final char t = text.charAt(i);\n            TrieNode2 tn = null;\n            if (ptr == null) {\n                tn = _first[t];\n            } else {\n                if (ptr.HasKey(t) == false) {\n                    tn = _first[t];\n                } else {\n                    tn = ptr.GetValue(t);\n                }\n            }\n            if (tn != null) {\n                if (tn.End) {\n                    for (final Integer index : tn.Results) {\n                        final String key = _keywords[index];\n                        final WordsSearchResult item = new WordsSearchResult(key, i + 1 - key.length(), i, index);\n                        list.add(item);\n                    }\n                }\n            }\n            ptr = tn;\n        }\n        return list;\n    }\n\n    /**\n     * 判断文本是否包含关键字\n     *\n     * @param text 文本\n     * @return\n     */\n    public boolean ContainsAny(final String text) {\n        TrieNode2 ptr = null;\n        for (int i = 0; i < text.length(); i++) {\n            final char t = text.charAt(i);\n            TrieNode2 tn = null;\n            if (ptr == null) {\n                tn = _first[t];\n            } else {\n                if (ptr.HasKey(t) == false) {\n                    tn = _first[t];\n                } else {\n                    tn = ptr.GetValue(t);\n                }\n            }\n            if (tn != null) {\n                if (tn.End) {\n                    return true;\n                }\n            }\n            ptr = tn;\n        }\n        return false;\n    }\n\n    /**\n     * 在文本中替换所有的关键字, 替换符默认为 *\n     *\n     * @param text 文本\n     * @return\n     */\n    public String Replace(final String text) {\n        return Replace(text, '*');\n    }\n\n    /**\n     * 在文本中替换所有的关键字\n     *\n     * @param text        文本\n     * @param replaceChar 替换符\n     * @return\n     */\n    public String Replace(final String text, final char replaceChar) {\n        final StringBuilder result = new StringBuilder(text);\n\n        TrieNode2 ptr = null;\n        for (int i = 0; i < text.length(); i++) {\n            final char t = text.charAt(i);\n            TrieNode2 tn = null;\n            if (ptr == null) {\n                tn = _first[t];\n            } else {\n                if (ptr.HasKey(t) == false) {\n                    tn = _first[t];\n                } else {\n                    tn = ptr.GetValue(t);\n                }\n            }\n            if (tn != null) {\n                if (tn.End) {\n                    final int maxLength = _keywords[tn.Results.get(0)].length();\n                    final int start = i + 1 - maxLength;\n                    for (int j = start; j <= i; j++) {\n                        result.setCharAt(j, replaceChar);\n                    }\n                }\n            }\n            ptr = tn;\n        }\n        return result.toString();\n    }\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/sensitivewordsfiliter/WordsSearchResult.java",
    "content": "package top.flya.system.utils.sensitivewordsfiliter;\n\npublic class WordsSearchResult {\n\n    public WordsSearchResult(final String keyword, final int start, final int end, final int index) {\n        Keyword = keyword;\n        End = end;\n        Start = start;\n        Index = index;\n        MatchKeyword = keyword;\n    }\n\n    public WordsSearchResult(final String keyword, final int start, final int end, final int index,\n                             final String matchKeyword) {\n        Keyword = keyword;\n        End = end;\n        Start = start;\n        Index = index;\n        MatchKeyword = matchKeyword;\n    }\n\n    /** 开始位置 */\n    public int Start;\n    /** 结束位置 */\n    public int End;\n    /** 关键字 */\n    public String Keyword;\n    /** 索引 */\n    public int Index;\n    /** 匹配关键字 */\n    public String MatchKeyword;\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/utils/sensitivewordsfiliter/WorldsFilterUtils.java",
    "content": "package top.flya.system.utils.sensitivewordsfiliter;\n\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.core.util.StrUtil;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.core.io.Resource;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * User:azu<azu@eventslack.com>\n * Date: 2021/5/19\n * Time:16:29\n */\n@Slf4j\npublic class WorldsFilterUtils {\n\n\n    private static final List<String> blackWordsList = new ArrayList<>();\n\n\n    public static void initBlackWordsList() {\n        try {\n            Resource resource = new ClassPathResource(\"wordsfilter/sensi_words2.txt\");\n            if (!resource.exists()) {\n                log.error(\"未找到黑词文件库\");\n                return;\n            }\n            File file = FileUtil.writeFromStream(resource.getInputStream(), FileUtil.createTempFile());\n            List<String> keyList = FileUtil.readUtf8Lines(file);\n            blackWordsList.addAll(keyList);\n            log.info(\"加载黑词库行数 => {}\", keyList.size());\n        } catch (Exception e) {\n            log.error(\"黑词库加载失败\", e);\n        }\n    }\n\n\n\n\n    private static boolean wordsCheck(List<String> worlds, String content) {\n        WordsSearch iwords = new WordsSearch();\n        iwords.SetKeywords(worlds);\n        return iwords.ContainsAny(content);\n    }\n\n\n    public static boolean checkBySystemWords(String content) {\n        loadSystemKeywords();\n        return wordsCheck(blackWordsList, content);\n    }\n\n\n    public synchronized static void loadSystemKeywords() {\n        if (!blackWordsList.isEmpty()) {\n            return;\n        }\n        initBlackWordsList();\n    }\n\n    public synchronized static void forceLoadSystemKeywords() {\n        blackWordsList.clear();\n        initBlackWordsList();\n    }\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/xxlJob/ScheduledExecutorUtils.java",
    "content": "package top.flya.system.xxlJob;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\n\npublic class ScheduledExecutorUtils {\n\n    // 定义一个方法用于安排定时任务，同时接受传入的参数\n    public static void scheduleTask(String timeString, RunnableWithParams task, Object... params) throws ParseException {\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        Date targetTime = sdf.parse(timeString);\n        long initialDelay = targetTime.getTime() - System.currentTimeMillis();\n\n        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);\n\n        // 创建一个任务实例，可以将传入的参数传递给任务逻辑\n        Runnable runnable = () -> {\n            task.run(params);\n        };\n\n        scheduler.schedule(runnable, initialDelay, TimeUnit.MILLISECONDS);\n    }\n\n    public static void main(String[] args) {\n        try {\n            String targetTimeString = \"2023-08-20 15:30:00\"; // Change this to your desired time\n\n            // 创建一个任务逻辑，接受参数并在任务执行时使用\n            RunnableWithParams task = (params) -> {\n                System.out.println(\"Task executed at: \" + new Date());\n                if (params.length > 0) {\n                    System.out.println(\"Parameter received: \" + params[0]);\n                }\n                // 在这里添加你的任务逻辑\n            };\n\n            String parameter = \"Hello, World!\"; // 传递给任务逻辑的参数\n\n            // 调用方法安排定时任务，并传递参数\n            scheduleTask(targetTimeString, task, parameter);\n\n            System.out.println(\"Scheduled task for: \" + targetTimeString);\n        } catch (ParseException e) {\n            e.printStackTrace();\n        }\n    }\n\n    // 定义一个函数式接口，允许任务逻辑接受参数\n    @FunctionalInterface\n    public interface RunnableWithParams {\n        void run(Object... params);\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/xxlJob/WxHandler.java",
    "content": "package top.flya.system.xxlJob;\n\nimport com.xxl.job.core.context.XxlJobHelper;\nimport com.xxl.job.core.handler.annotation.XxlJob;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\nimport top.flya.system.domain.PzcUser;\nimport top.flya.system.mapper.PzcUserMapper;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\n@Component\n@Slf4j\npublic class WxHandler {\n\n    @Resource\n    private PzcUserMapper pzcUserMapper;\n\n\n//    /**\n//     * 1、简单任务示例（Bean模式）\n//     */\n//    @XxlJob(\"demoJobHandler\")\n//    public void demoJobHandler() throws Exception {\n//        XxlJobHelper.log(\"XXL-JOB, Hello World.\");\n//\n//        for (int i = 0; i < 5; i++) {\n//            XxlJobHelper.log(\"beat at:\" + i);\n//        }\n//        // default success\n//    }\n\n    /**\n     * 每天的0点自动同步  用户可以取消的次数取决于用户当前等级\n     */\n    @XxlJob(\"wxJobHandler\")\n    public void syncWxUserCancel() {\n        XxlJobHelper.log(\"定时同步微信用户 的 取消活动次数 FL1906\");\n        log.info(\"定时同步微信用户 的 取消活动次数\");\n        List<PzcUser> pzcUsers = pzcUserMapper.selectList();\n        for (PzcUser pzcUser : pzcUsers) {\n            pzcUser.setUserLevel(Long.valueOf(getLevel(Math.toIntExact(pzcUser.getIntegration()))));\n            pzcUser.setExemptCancel(Math.toIntExact(pzcUser.getUserLevel()));\n            pzcUserMapper.updateById(pzcUser);\n        }\n        log.info(\"定时同步微信用户 的 取消活动次数 完成\");\n        XxlJobHelper.log(\"定时同步微信用户 的 取消活动次数 完成 FL1906\");\n    }\n\n\n    public Integer getLevel(Integer jf) {\n        if (jf <= 30) {\n            return 1;\n        }\n        if (jf <= 70) {\n            return 2;\n        }\n        if (jf <= 120) {\n            return 3;\n        }\n        if (jf <= 180) {\n            return 4;\n        } else {\n            return 5;\n        }\n    }\n\n    /**\n     *  每天的 1点自动同步积分 与取消次数 与用户等级 的映射关系 （用户升级） TODO 这个好像用不上 直接在订单处 进行\n     */\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/xxlJob/diy/JobLoginService.java",
    "content": "package top.flya.system.xxlJob.diy;\n\n\nimport cn.hutool.http.HttpRequest;\nimport cn.hutool.http.HttpResponse;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\nimport java.net.HttpCookie;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\n@Component\npublic class JobLoginService {\n\n    private final Map<String,String> loginCookie=new HashMap<>();\n\n    @Value(\"${xxl.job.admin.address}\")\n    private String adminAddresses;\n\n    @Value(\"${xxl.job.admin.username}\")\n    private String username;\n\n    @Value(\"${xxl.job.admin.password}\")\n    private String password;\n\n    public void login() {\n        String url=adminAddresses+\"/login\";\n        HttpResponse response = HttpRequest.post(url)\n            .form(\"userName\",username)\n            .form(\"password\",password)\n            .execute();\n        List<HttpCookie> cookies = response.getCookies();\n        Optional<HttpCookie> cookieOpt = cookies.stream()\n            .filter(cookie -> cookie.getName().equals(\"XXL_JOB_LOGIN_IDENTITY\")).findFirst();\n        if (!cookieOpt.isPresent())\n            throw new RuntimeException(\"get xxl-job cookie error!\");\n\n        String value = cookieOpt.get().getValue();\n        loginCookie.put(\"XXL_JOB_LOGIN_IDENTITY\",value);\n    }\n\n    public String getCookie() {\n        for (int i = 0; i < 3; i++) {\n            String cookieStr = loginCookie.get(\"XXL_JOB_LOGIN_IDENTITY\"); //redis\n            if (cookieStr !=null) {\n                return \"XXL_JOB_LOGIN_IDENTITY=\"+cookieStr;\n            }\n            login();\n        }\n        throw new RuntimeException(\"get xxl-job cookie error!\");\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/xxlJob/diy/TestController.java",
    "content": "package top.flya.system.xxlJob.diy;\n\n\nimport cn.hutool.http.HttpRequest;\nimport cn.hutool.http.HttpResponse;\nimport cn.hutool.json.JSONArray;\nimport cn.hutool.json.JSONObject;\nimport cn.hutool.json.JSONUtil;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport top.flya.common.core.domain.R;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@RestController\n@RequestMapping(\"/test\")\npublic class TestController {\n\n    @Value(\"${xxl.job.admin.url.add}\")\n    private String addUrl;\n\n    @Value(\"${xxl.job.admin.url.pageList}\")\n    public String pageListUrl;\n\n    @Value(\"${xxl.job.admin.url.update}\")\n    public String updateUrl;\n\n    @Value(\"${xxl.job.admin.url.start}\")\n    public String startUrl;\n\n    @Value(\"${xxl.job.admin.url.stop}\")\n    public String stopUrl;\n    @Autowired\n    private JobLoginService jobLoginService;\n\n    @GetMapping(\"/list\")\n    public List<XxlJobInfo> getJobList() {\n        HttpResponse response = HttpRequest.post(pageListUrl)\n            .form(\"jobGroup\", 3)\n            .form(\"triggerStatus\", -1)\n            .form(\"jobDesc\", \"\")\n            .form(\"executorHandler\", \"wxHandler\")\n            .form(\"start\", 0)\n            .form(\"length\", 10)\n            .form(\"author\", \"\")\n            .cookie(jobLoginService.getCookie())\n            .execute();\n\n        String body = response.body();\n        JSONArray array = JSONUtil.parse(body).getByPath(\"data\", JSONArray.class);\n        List<XxlJobInfo> list = array.stream()\n            .map(o -> JSONUtil.toBean((JSONObject) o, XxlJobInfo.class))\n            .collect(Collectors.toList());\n        return list;\n    }\n\n    @PostMapping(\"/add\")\n    public R createNewJob()\n    {\n        HttpResponse response = HttpRequest.post(addUrl)\n            .form(\"jobGroup\", 3)\n            .form(\"jobDesc\", \"测试任务2333\")\n            .form(\"author\", \"flya\")\n            .form(\"alarmEmail\", \"\")\n            .form(\"scheduleType\", \"CRON\")\n            .form(\"scheduleConf\", \"0 0 0 * * ?\")\n            .form(\"cronGen_display\", \"0 0 0 * * ?\")\n            .form(\"schedule_conf_CRON\",\"\")\n            .form(\"schedule_conf_FIX_RATE\",\"\")\n            .form(\"schedule_conf_FIX_DELAY\",\"\")\n            .form(\"glueType\", \"BEAN\")\n            .form(\"executorHandler\", \"wxHandler\")\n            .form(\"executorParam\", \"\")\n            .form(\"executorRouteStrategy\", \"FIRST\")\n            .form(\"childJobId\", \"\")\n            .form(\"misfireStrategy\", \"DO_NOTHING\")\n\n            .form(\"executorBlockStrategy\", \"SERIAL_EXECUTION\")\n            .form(\"executorTimeout\", 0)\n            .form(\"executorFailRetryCount\", 0)\n            .form(\"glueRemark\", \"GLUE代码初始化\")\n            .form(\"glueSource\", \"\")\n\n            .cookie(jobLoginService.getCookie())\n            .execute();\n        String body = response.body();\n        JSONObject jsonObject = JSONUtil.parseObj(body);\n        if (jsonObject.getInt(\"code\") == 200) {\n            return R.ok();\n        }else {\n            return R.fail();\n        }\n\n    }\n\n\n    @PostMapping(\"/update\")\n    public R updateNewJob()\n    {\n        HttpResponse response = HttpRequest.post(updateUrl)\n            .form(\"jobGroup\", 3)\n            .form(\"jobDesc\", \"测试任务\")\n            .form(\"author\", \"flya2333\")\n            .form(\"alarmEmail\", \"\")\n            .form(\"scheduleType\", \"CRON\")\n            .form(\"scheduleConf\", \"0 0 0 * * ?\")\n            .form(\"cronGen_display\", \"0 0 0 * * ?\")\n            .form(\"schedule_conf_CRON\",\"\")\n            .form(\"schedule_conf_FIX_RATE\",\"\")\n            .form(\"schedule_conf_FIX_DELAY\",\"\")\n            .form(\"glueType\", \"BEAN\")\n            .form(\"executorHandler\", \"wxHandler\")\n            .form(\"executorParam\", \"\")\n            .form(\"executorRouteStrategy\", \"FIRST\")\n            .form(\"childJobId\", \"\")\n            .form(\"misfireStrategy\", \"DO_NOTHING\")\n\n            .form(\"executorBlockStrategy\", \"SERIAL_EXECUTION\")\n            .form(\"executorTimeout\", 0)\n            .form(\"executorFailRetryCount\", 0)\n            .form(\"glueRemark\", \"GLUE代码初始化\")\n            .form(\"glueSource\", \"\")\n            .form(\"id\", 5)\n\n            .cookie(jobLoginService.getCookie())\n            .execute();\n\n        String body = response.body();\n        JSONObject jsonObject = JSONUtil.parseObj(body);\n        if (jsonObject.getInt(\"code\") == 200) {\n            return R.ok();\n        }else {\n            return R.fail();\n        }\n    }\n\n    @PostMapping(\"/start\")\n    public R startNewJob()\n    {\n\n        HttpResponse response = HttpRequest.post(startUrl+\"?id=5\")\n            .cookie(jobLoginService.getCookie())\n            .execute();\n        String body = response.body();\n        JSONObject jsonObject = JSONUtil.parseObj(body);\n        if (jsonObject.getInt(\"code\") == 200) {\n            return R.ok();\n        }else {\n            return R.fail();\n        }\n    }\n\n    @PostMapping(\"/stop\")\n    public R stopNewJob()\n    {\n        HttpResponse response = HttpRequest.post(stopUrl+\"?id=5\")\n            .cookie(jobLoginService.getCookie())\n            .execute();\n        String body = response.body();\n        JSONObject jsonObject = JSONUtil.parseObj(body);\n        if (jsonObject.getInt(\"code\") == 200) {\n            return R.ok();\n        }else {\n            return R.fail();\n        }\n    }\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/xxlJob/diy/XxlJobGroup.java",
    "content": "package top.flya.system.xxlJob.diy;\n\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * Created by xuxueli on 16/9/30.\n */\npublic class XxlJobGroup {\n\n    private int id;\n    private String appname;\n    private String title;\n    private int addressType;        // 执行器地址类型：0=自动注册、1=手动录入\n    private String addressList;     // 执行器地址列表，多地址逗号分隔(手动录入)\n    private Date updateTime;\n\n    // registry list\n    private List<String> registryList;  // 执行器地址列表(系统注册)\n    public List<String> getRegistryList() {\n        if (addressList!=null && addressList.trim().length()>0) {\n            registryList = new ArrayList<String>(Arrays.asList(addressList.split(\",\")));\n        }\n        return registryList;\n    }\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n\n    public String getAppname() {\n        return appname;\n    }\n\n    public void setAppname(String appname) {\n        this.appname = appname;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public void setTitle(String title) {\n        this.title = title;\n    }\n\n    public int getAddressType() {\n        return addressType;\n    }\n\n    public void setAddressType(int addressType) {\n        this.addressType = addressType;\n    }\n\n    public String getAddressList() {\n        return addressList;\n    }\n\n    public Date getUpdateTime() {\n        return updateTime;\n    }\n\n    public void setUpdateTime(Date updateTime) {\n        this.updateTime = updateTime;\n    }\n\n    public void setAddressList(String addressList) {\n        this.addressList = addressList;\n    }\n\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/java/top/flya/system/xxlJob/diy/XxlJobInfo.java",
    "content": "package top.flya.system.xxlJob.diy;\n\n\n\nimport java.util.Date;\n\n/**\n * xxl-job info\n *\n * @author xuxueli  2016-1-12 18:25:49\n */\npublic class XxlJobInfo {\n\n    private int id;\t\t\t\t// 主键ID\n\n    private int jobGroup;\t\t// 执行器主键ID\n    private String jobDesc;\n\n    private Date addTime;\n    private Date updateTime;\n\n    private String author;\t\t// 负责人\n    private String alarmEmail;\t// 报警邮件\n\n    private String scheduleType;\t\t\t// 调度类型\n    private String scheduleConf;\t\t\t// 调度配置，值含义取决于调度类型\n    private String misfireStrategy;\t\t\t// 调度过期策略\n\n    private String executorRouteStrategy;\t// 执行器路由策略\n    private String executorHandler;\t\t    // 执行器，任务Handler名称\n    private String executorParam;\t\t    // 执行器，任务参数\n    private String executorBlockStrategy;\t// 阻塞处理策略\n    private int executorTimeout;     \t\t// 任务执行超时时间，单位秒\n    private int executorFailRetryCount;\t\t// 失败重试次数\n\n    private String glueType;\t\t// GLUE类型\t#com.xxl.job.core.glue.GlueTypeEnum\n    private String glueSource;\t\t// GLUE源代码\n    private String glueRemark;\t\t// GLUE备注\n    private Date glueUpdatetime;\t// GLUE更新时间\n\n    private String childJobId;\t\t// 子任务ID，多个逗号分隔\n\n    private int triggerStatus;\t\t// 调度状态：0-停止，1-运行\n    private long triggerLastTime;\t// 上次调度时间\n    private long triggerNextTime;\t// 下次调度时间\n\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n\n    public int getJobGroup() {\n        return jobGroup;\n    }\n\n    public void setJobGroup(int jobGroup) {\n        this.jobGroup = jobGroup;\n    }\n\n    public String getJobDesc() {\n        return jobDesc;\n    }\n\n    public void setJobDesc(String jobDesc) {\n        this.jobDesc = jobDesc;\n    }\n\n    public Date getAddTime() {\n        return addTime;\n    }\n\n    public void setAddTime(Date addTime) {\n        this.addTime = addTime;\n    }\n\n    public Date getUpdateTime() {\n        return updateTime;\n    }\n\n    public void setUpdateTime(Date updateTime) {\n        this.updateTime = updateTime;\n    }\n\n    public String getAuthor() {\n        return author;\n    }\n\n    public void setAuthor(String author) {\n        this.author = author;\n    }\n\n    public String getAlarmEmail() {\n        return alarmEmail;\n    }\n\n    public void setAlarmEmail(String alarmEmail) {\n        this.alarmEmail = alarmEmail;\n    }\n\n    public String getScheduleType() {\n        return scheduleType;\n    }\n\n    public void setScheduleType(String scheduleType) {\n        this.scheduleType = scheduleType;\n    }\n\n    public String getScheduleConf() {\n        return scheduleConf;\n    }\n\n    public void setScheduleConf(String scheduleConf) {\n        this.scheduleConf = scheduleConf;\n    }\n\n    public String getMisfireStrategy() {\n        return misfireStrategy;\n    }\n\n    public void setMisfireStrategy(String misfireStrategy) {\n        this.misfireStrategy = misfireStrategy;\n    }\n\n    public String getExecutorRouteStrategy() {\n        return executorRouteStrategy;\n    }\n\n    public void setExecutorRouteStrategy(String executorRouteStrategy) {\n        this.executorRouteStrategy = executorRouteStrategy;\n    }\n\n    public String getExecutorHandler() {\n        return executorHandler;\n    }\n\n    public void setExecutorHandler(String executorHandler) {\n        this.executorHandler = executorHandler;\n    }\n\n    public String getExecutorParam() {\n        return executorParam;\n    }\n\n    public void setExecutorParam(String executorParam) {\n        this.executorParam = executorParam;\n    }\n\n    public String getExecutorBlockStrategy() {\n        return executorBlockStrategy;\n    }\n\n    public void setExecutorBlockStrategy(String executorBlockStrategy) {\n        this.executorBlockStrategy = executorBlockStrategy;\n    }\n\n    public int getExecutorTimeout() {\n        return executorTimeout;\n    }\n\n    public void setExecutorTimeout(int executorTimeout) {\n        this.executorTimeout = executorTimeout;\n    }\n\n    public int getExecutorFailRetryCount() {\n        return executorFailRetryCount;\n    }\n\n    public void setExecutorFailRetryCount(int executorFailRetryCount) {\n        this.executorFailRetryCount = executorFailRetryCount;\n    }\n\n    public String getGlueType() {\n        return glueType;\n    }\n\n    public void setGlueType(String glueType) {\n        this.glueType = glueType;\n    }\n\n    public String getGlueSource() {\n        return glueSource;\n    }\n\n    public void setGlueSource(String glueSource) {\n        this.glueSource = glueSource;\n    }\n\n    public String getGlueRemark() {\n        return glueRemark;\n    }\n\n    public void setGlueRemark(String glueRemark) {\n        this.glueRemark = glueRemark;\n    }\n\n    public Date getGlueUpdatetime() {\n        return glueUpdatetime;\n    }\n\n    public void setGlueUpdatetime(Date glueUpdatetime) {\n        this.glueUpdatetime = glueUpdatetime;\n    }\n\n    public String getChildJobId() {\n        return childJobId;\n    }\n\n    public void setChildJobId(String childJobId) {\n        this.childJobId = childJobId;\n    }\n\n    public int getTriggerStatus() {\n        return triggerStatus;\n    }\n\n    public void setTriggerStatus(int triggerStatus) {\n        this.triggerStatus = triggerStatus;\n    }\n\n    public long getTriggerLastTime() {\n        return triggerLastTime;\n    }\n\n    public void setTriggerLastTime(long triggerLastTime) {\n        this.triggerLastTime = triggerLastTime;\n    }\n\n    public long getTriggerNextTime() {\n        return triggerNextTime;\n    }\n\n    public void setTriggerNextTime(long triggerNextTime) {\n        this.triggerNextTime = triggerNextTime;\n    }\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/apiclient_key.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJKRICZHhIi47i\nQWwEsWjtVA2TBgtXIhrprdKn64MYZGHHAoISi3vPnUwd9RDdjRaxHr0yf13qO4zx\nMRiky3GWYEiic769MQKmJZubk72hrB3u4/CRhaSxj5XTJfUy8v95/ILB3yravamA\ntfan95xNyplBhq1eF4JHIJfBKHCrZ6jN1P7/ERzsR97bVZ0vS1uPQQYWZ+Hy9nC6\nvsCe4c0EEldKDX5uNZ48KkIJnMVhXinf7Jv/NzIEZiPU+Cd6rSF/7kn33E1CkFah\nKYAX6KutOFIJcuquyvHsDgEWbYqMhoEXm6QpE+g1yh1xEs+KPhBkbMFxHyEkSFsM\nQiPJkYyHAgMBAAECggEANmfKPCVqNsyv91eUXGyTIWUTSsletdE+kCb4C3xx913m\n6Akwns1kzhEP7iZCynkHQx46M3cpMlmq0+zgammvrekam/1MACVKPx96x4gcyKYh\nbmPtw53unitkbTgd6gq4uAhoYQD3uTOErZAJRwJ8HroF2ygOqZ0YGh6hjJdgaarj\nHG9vnRD7Q2CSOnxNXKqExbGW8Qjwj+MT2x/ecu7cCXqhvV7SSyRwGPA8HJ9WfjP1\n+SOs502hrdZUPpyGVPUZdl5Pv/zsl+dCipqgAqyuJX6Y3rzoVH1XGANLhXDikQdQ\ngQpFg0rCigf7HmVdNk1xbb5kn6CuNYt0Lpd2zGAcEQKBgQD73bILtaMntUZ2FCmS\n7ZNrv/0LANQupmECoLjdelHkSPVFROzv1MAw+9rD7Gzcmk3qcUrIGGlbAzD9phDc\nlW2Co0mnzUlVjhSOE4h1o96bEhlk0DTsNkG+mP418IpMaC6wzjVpVIOK+sNybE78\noC4Kd5DldMFbNdcNPIeMIhbRXwKBgQDMdlFA5UYmBs/gO/bDMr4y2bPYnVPIM1QN\nBzU5BeU88UPCe6qMk/W4KTYoutMctNR7CVW6RAof64dbWSqrXfEAfS3PaVj4lqk+\nQA3Q0mYrcDNFeqvupnCuuCRLcqj+mDSbz4TmA3wh1H0Rjr7GSBc4Nm70aI2ybibA\n9r6D5g/N2QKBgQDi+l67CLz5Svct8Guq+qlxYDq1kNCnHc+tI5SWG+bzGQDYpQ31\n8MAnJMF48XcFs5VmIyUmgEFqAM1EuUTW1V80bN0y+OEO1hUWGOpQQhaZn0z9OlmH\nSjojfxMRHy4zP2xcb+lYfA5z0BsU4iCor93uFuSgtICQJ0wfpJ3vHsV7dwKBgB0G\nApy6rw2A1AtZl7q3vkYLnzr1gkod9yVuS4DPtG3FNcAqu9f+vNeqifSYKJWfmbXp\nalDpjaJgVbOC4cq3qBlQq6sQoj+Pa3DZuNxWsYgjAjQvqK5U1BQJMaXAHfsd8gHY\nIF5iSkGnHyXZ5HzTCPDC0VdCbDLS7g9gN0UT6FRpAoGAC8GKrdjDwMvkUtbA3aGy\n99FnuD9oqaTLdcFo0O5xVNdqNK+yRf4yY99ky8RqfxJ/I1vPyU1hoviB2qYKphp1\nHDtbu6gz4/kuosiRh8vWi2/kHREAyw20iRbRncBEmxLJuKKxNlbz5vF76RzlSTTg\nA5zHu1EygULI4ytgxx5lTEQ=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcActivityConnArtistMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcActivityConnArtistMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcActivityConnArtist\" id=\"PzcActivityConnArtistResult\">\n        <result property=\"activityConnArtistId\" column=\"activity_conn_artist_id\"/>\n        <result property=\"activityId\" column=\"activity_id\"/>\n        <result property=\"artistId\" column=\"artist_id\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"state\" column=\"state\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcActivityConnIntroMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcActivityConnIntroMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcActivityConnIntro\" id=\"PzcActivityConnIntroResult\">\n        <result property=\"activityConnIntroId\" column=\"activity_conn_intro_id\"/>\n        <result property=\"activityId\" column=\"activity_id\"/>\n        <result property=\"introId\" column=\"intro_id\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"state\" column=\"state\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcActivityConnTagMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcActivityConnTagMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcActivityConnTag\" id=\"PzcActivityConnTagResult\">\n        <result property=\"activityConnTagId\" column=\"activity_conn_tag_id\"/>\n        <result property=\"activityId\" column=\"activity_id\"/>\n        <result property=\"tagId\" column=\"tag_id\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"state\" column=\"state\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcActivityGroupApplyMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcActivityGroupApplyMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcActivityGroupApply\" id=\"PzcActivityGroupApplyResult\">\n        <result property=\"applyId\" column=\"apply_id\"/>\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"activityId\" column=\"activity_id\"/>\n        <result property=\"groupId\" column=\"group_id\"/>\n        <result property=\"groupType\" column=\"group_type\"/>\n        <result property=\"money\" column=\"money\"/>\n        <result property=\"message\" column=\"message\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcActivityGroupMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcActivityGroupMapper\">\n\n    <resultMap type=\"top.flya.system.domain.vo.PzcActivityGroupVo\" id=\"PzcActivityGroupResult\">\n        <result property=\"groupId\" column=\"group_id\"/>\n        <result property=\"activityId\" column=\"activity_id\"/>\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"title\" column=\"title\"/>\n        <result property=\"money\" column=\"money\"/>\n        <result property=\"groupType\" column=\"group_type\"/>\n        <result property=\"address\" column=\"address\"/>\n        <result property=\"activityTime\" column=\"activity_time\"/>\n        <result property=\"auth\" column=\"auth\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"region\" column=\"region\"/>\n        <association property=\"user\">\n            <result column=\"user_id\" property=\"userId\"/>\n            <result property=\"openid\" column=\"openid\"/>\n            <result property=\"money\" column=\"u_money\"/>\n            <result property=\"userLevel\" column=\"user_level\"/>\n            <result property=\"integration\" column=\"integration\"/>\n            <result property=\"integrationNow\" column=\"integration_now\"/>\n            <result property=\"realname\" column=\"realname\"/>\n            <result property=\"nickname\" column=\"nickname\"/>\n            <result property=\"sex\" column=\"sex\"/>\n            <result property=\"phone\" column=\"phone\"/>\n            <result property=\"avatar\" column=\"avatar\"/>\n            <result property=\"address\" column=\"u_address\"/>\n            <result property=\"intro\" column=\"intro\"/>\n            <result property=\"age\" column=\"age\"/>\n            <result property=\"constellation\" column=\"constellation\"/>\n            <result property=\"mbti\" column=\"mbti\"/>\n            <result property=\"hobby\" column=\"hobby\"/>\n            <result property=\"school\" column=\"school\"/>\n            <result property=\"occupation\" column=\"occupation\"/>\n            <result property=\"createTime\" column=\"create_time\"/>\n            <result property=\"musicStyle\" column=\"music_style\"/>\n            <result property=\"updateTime\" column=\"update_time\"/>\n            <result property=\"state\" column=\"state\"/>\n\n        </association>\n    </resultMap>\n\n\n    <sql id=\"base\">\n        select group_id,activity_id,g.user_id as user_id,title,g.money as money,u.money as u_money,group_type,g.address as address,activity_time,auth,g.create_time as create_time,g.update_time as update_time,\n               openid,user_level,nickname,sex,avatar,u.address as u_address,intro,age,constellation,mbti,hobby,school,occupation,music_style,region\n    </sql>\n\n    <select id=\"selectDetailsList\" resultMap=\"PzcActivityGroupResult\">\n        <include refid=\"base\"/>\n        from pzc_activity_group as g inner join pzc_user as u\n        where 1=1\n        and g.status = 0\n        <if test=\"bo.userId ==null\">\n            AND g.activity_time > NOW()\n        </if>\n        <if test=\"bo.userId !=null\">\n            and g.user_id = #{bo.userId}\n\n        </if>\n            and g.user_id = u.user_id\n        <if test=\"bo.userSex != null\">\n            and u.sex = #{bo.userSex}\n        </if>\n        <if test=\"bo.activityId !=null\">\n            and g.activity_id = #{bo.activityId}\n        </if>\n\n        <if test=\"bo.region !=null\">\n           and g.region = #{bo.region}\n        </if>\n\n        <if test=\"bo.activityTime1 !=null\">\n            order by  g.activity_time ASC\n        </if>\n        <if test=\"bo.region ==null and bo.activityTime1 ==null and bo.userLevel ==null\">\n            order by  g.activity_time ASC\n        </if>\n\n\n    </select>\n    <select id=\"selectVoByIdDIY\" resultMap=\"PzcActivityGroupResult\">\n        <include refid=\"base\"/>\n        from pzc_activity_group as g inner join pzc_user as u on g.user_id = u.user_id\n        where 1=1 and g.group_id = #{groupId}\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcActivityMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcActivityMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcActivity\" id=\"PzcActivityResult\">\n        <result property=\"activityId\" column=\"activity_id\"/>\n        <result property=\"address\" column=\"address\"/>\n        <result property=\"regionId\" column=\"region_id\"/>\n        <result property=\"title\" column=\"title\"/>\n        <result property=\"startTime\" column=\"start_time\"/>\n        <result property=\"endDate\" column=\"end_date\"/>\n        <result property=\"innerImage\" column=\"inner_image\"/>\n        <result property=\"showTime\" column=\"show_time\"/>\n        <result property=\"coverImage\" column=\"cover_image\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"state\" column=\"state\"/>\n    </resultMap>\n    <select id=\"selectActivityByActivityIds\" resultType=\"top.flya.system.domain.PzcActivity\">\n        select * from pzc_activity where activity_id in\n                                   <foreach collection=\"activityIds\" item=\"activityId\" open=\"(\" separator=\",\" close=\")\">\n                                       #{activityId}\n                                   </foreach>\n        and classify = #{classify}\n    </select>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcArtistMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcArtistMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcArtist\" id=\"PzcArtistResult\">\n        <result property=\"artistId\" column=\"artist_id\"/>\n        <result property=\"name\" column=\"name\"/>\n        <result property=\"imageUrl\" column=\"image_url\"/>\n        <result property=\"description\" column=\"description\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"state\" column=\"state\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcIntroMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcIntroMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcIntro\" id=\"PzcIntroResult\">\n        <result property=\"introId\" column=\"intro_id\"/>\n        <result property=\"title\" column=\"title\"/>\n        <result property=\"content\" column=\"content\"/>\n        <result property=\"type\" column=\"type\"/>\n        <result property=\"imageFullUrl\" column=\"image_full_url\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"state\" column=\"state\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcOfficialMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcOfficialMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcOfficial\" id=\"PzcOfficialResult\">\n        <result property=\"officialId\" column=\"official_id\"/>\n        <result property=\"fromUserId\" column=\"from_user_id\"/>\n        <result property=\"toUserId\" column=\"to_user_id\"/>\n        <result property=\"title\" column=\"title\"/>\n        <result property=\"content\" column=\"content\"/>\n        <result property=\"isRead\" column=\"is_read\"/>\n        <result property=\"groupId\" column=\"group_id\"/>\n        <result property=\"activityId\" column=\"activity_id\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcOrderMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcOrderMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcOrder\" id=\"PzcOrderResult\">\n        <result property=\"orderId\" column=\"order_id\"/>\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"activityId\" column=\"activity_id\"/>\n        <result property=\"money\" column=\"money\"/>\n        <result property=\"orderStatus\" column=\"order_status\"/>\n        <result property=\"type\" column=\"type\"/>\n        <result property=\"outOrderNum\" column=\"out_order_num\"/>\n        <result property=\"intro\" column=\"intro\"/>\n        <result property=\"title\" column=\"title\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcOrganizerMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcOrganizerMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcOrganizer\" id=\"PzcOrganizerResult\">\n        <result property=\"organizerId\" column=\"organizer_id\"/>\n        <result property=\"phone\" column=\"phone\"/>\n        <result property=\"name\" column=\"name\"/>\n        <result property=\"logo\" column=\"logo\"/>\n        <result property=\"content\" column=\"content\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"state\" column=\"state\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcOrganizerTicketMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcOrganizerTicketMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcOrganizerTicket\" id=\"PzcOrganizerTicketResult\">\n        <result property=\"organizerTicketId\" column=\"organizer_ticket_id\"/>\n        <result property=\"name\" column=\"name\"/>\n        <result property=\"qrImage\" column=\"qr_image\"/>\n        <result property=\"logoImage\" column=\"logo_image\"/>\n        <result property=\"organizerId\" column=\"organizer_id\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"state\" column=\"state\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcRegionMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcRegionMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcRegion\" id=\"PzcRegionResult\">\n        <result property=\"regionId\" column=\"region_id\"/>\n        <result property=\"base\" column=\"base\"/>\n        <result property=\"name\" column=\"name\"/>\n        <result property=\"imgUrl\" column=\"img_url\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"state\" column=\"state\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcTagMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcTagMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcTag\" id=\"PzcTagResult\">\n        <result property=\"tagId\" column=\"tag_id\"/>\n        <result property=\"name\" column=\"name\"/>\n        <result property=\"imageUrl\" column=\"image_url\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"state\" column=\"state\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcUserCollectMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcUserCollectMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcUserCollect\" id=\"PzcUserCollectResult\">\n        <result property=\"collectId\" column=\"collect_id\"/>\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"activityId\" column=\"activity_id\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcUserHistoryMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcUserHistoryMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcUserHistory\" id=\"PzcUserHistoryResult\">\n        <result property=\"historyId\" column=\"history_id\"/>\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"activityId\" column=\"activity_id\"/>\n        <result property=\"type\" column=\"type\"/>\n        <result property=\"message\" column=\"message\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcUserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcUserMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcUser\" id=\"PzcUserResult\">\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"openid\" column=\"openid\"/>\n        <result property=\"money\" column=\"money\"/>\n        <result property=\"userLevel\" column=\"user_level\"/>\n        <result property=\"integration\" column=\"integration\"/>\n        <result property=\"integrationNow\" column=\"integration_now\"/>\n        <result property=\"realname\" column=\"realname\"/>\n        <result property=\"nickname\" column=\"nickname\"/>\n        <result property=\"sex\" column=\"sex\"/>\n        <result property=\"phone\" column=\"phone\"/>\n        <result property=\"avatar\" column=\"avatar\"/>\n        <result property=\"address\" column=\"address\"/>\n        <result property=\"intro\" column=\"intro\"/>\n        <result property=\"age\" column=\"age\"/>\n        <result property=\"constellation\" column=\"constellation\"/>\n        <result property=\"mbti\" column=\"mbti\"/>\n        <result property=\"hobby\" column=\"hobby\"/>\n        <result property=\"school\" column=\"school\"/>\n        <result property=\"occupation\" column=\"occupation\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"musicStyle\" column=\"music_style\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"state\" column=\"state\"/>\n    </resultMap>\n\n\n    <update id=\"updateMoney\">\n        update pzc_user set money = #{bo.money} where user_id = #{bo.userId}\n    </update>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcUserPhotoMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcUserPhotoMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcUserPhoto\" id=\"PzcUserPhotoResult\">\n        <result property=\"photoId\" column=\"photo_id\"/>\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"url\" column=\"url\"/>\n        <result property=\"message\" column=\"message\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcUserTalkMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcUserTalkMapper\">\n\n    <resultMap type=\"top.flya.system.domain.vo.PzcUserTalkVo\" id=\"PzcUserTalkResult\">\n        <result property=\"talkId\" column=\"talk_id\"/>\n        <result property=\"fromUserId\" column=\"from_user_id\"/>\n        <result property=\"toUserId\" column=\"to_user_id\"/>\n        <result property=\"message\" column=\"message\"/>\n        <result property=\"messageStatus\" column=\"message_status\"/>\n        <result property=\"messageType\" column=\"message_type\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"groupId\" column=\"group_id\"/>\n<!--        <result property=\"notReadCount\" column=\"not_read_count\"/>-->\n    </resultMap>\n\n\n\n    <select id=\"selectVoPageV1\" resultMap=\"PzcUserTalkResult\" > <!-- 思路： 这里先 再如一个 对方用户的参数-->\n\n        SELECT talk_id,from_user_id, to_user_id, message, message_status, message_type, create_time,update_time,group_id\n        FROM pzc_user_talk\n        WHERE ((from_user_id = #{userId} and to_user_id = #{otherUser})\n        OR (to_user_id = #{userId} and from_user_id = #{otherUser})) and user_id = #{userId}\n        ORDER BY create_time DESC LIMIT 1\n    </select>\n\n    <select id=\"selectNotReadCount\" resultType=\"java.lang.Integer\">\n        select count(1) from pzc_user_talk where (from_user_id = #{fromUserId} and to_user_id = #{toUserId}) and user_id = #{userId} and message_status = 0\n    </select>\n    <select id=\"selectVoPageV2\" resultType=\"top.flya.system.domain.vo.PzcUserTalkVo\">\n        select talk_id,from_user_id,to_user_id,message,message_status,message_type,create_time,update_time,group_id from pzc_user_talk where\n               ((from_user_id = #{bo.fromUserId} and to_user_id = #{bo.toUserId}) or (from_user_id = #{bo.toUserId} and to_user_id = #{bo.fromUserId})) and user_id = #{bo.userId}\n               order by update_time desc\n    </select>\n    <select id=\"selectMyTalkUserIds\" resultType=\"java.lang.Long\">\n        select distinct from_user_id from pzc_user_talk where to_user_id = #{my} and user_id = #{my}\n    </select>\n    <select id=\"selectMyTalkUserIdsV2\" resultType=\"java.lang.Long\">\n        select distinct to_user_id from pzc_user_talk where from_user_id = #{my} and user_id = #{my}\n    </select>\n\n\n    <!--    <select id=\"selectVoPageV1\" resultMap=\"PzcUserTalkResult\">-->\n<!--        SELECT t1.talk_id, t1.from_user_id, t1.to_user_id, t1.message, t1.message_status, t1.message_type, t1.create_time, t1.update_time,-->\n<!--               (SELECT COUNT(t2.talk_id) FROM pzc_user_talk t2 WHERE t2.from_user_id = #{userId} AND t2.message_status = 0 GROUP BY t2.to_user_id ) AS not_read_count-->\n<!--        FROM pzc_user_talk t1-->\n<!--        WHERE t1.from_user_id = #{userId}-->\n<!--        GROUP BY t1.to_user_id-->\n<!--        ORDER BY t1.create_time DESC-->\n<!--    </select>-->\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/mapper/system/PzcViewPagerMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.PzcViewPagerMapper\">\n\n    <resultMap type=\"top.flya.system.domain.PzcViewPager\" id=\"PzcViewPagerResult\">\n        <result property=\"viewPagerId\" column=\"view_pager_id\"/>\n        <result property=\"name\" column=\"name\"/>\n        <result property=\"imageUrl\" column=\"image_url\"/>\n        <result property=\"linkUrl\" column=\"link_url\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"state\" column=\"state\"/>\n        <result property=\"activityId\" column=\"activity_id\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/static/bootstrap.css",
    "content": "/*!\n * Bootstrap v2.0.4\n *\n * Copyright 2012 Twitter, Inc\n * Licensed under the Apache License v2.0\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Designed and built with all the love in the world @twitter by @mdo and @fat.\n */\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nnav,\nsection {\n  display: block;\n}\n\naudio,\ncanvas,\nvideo {\n  display: inline-block;\n  *display: inline;\n  *zoom: 1;\n}\n\naudio:not([controls]) {\n  display: none;\n}\n\nhtml {\n  font-size: 100%;\n  -webkit-text-size-adjust: 100%;\n      -ms-text-size-adjust: 100%;\n}\n\na:focus {\n  outline: thin dotted #333;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n\na:hover,\na:active {\n  outline: 0;\n}\n\nsub,\nsup {\n  position: relative;\n  font-size: 75%;\n  line-height: 0;\n  vertical-align: baseline;\n}\n\nsup {\n  top: -0.5em;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\nimg {\n  max-width: 100%;\n  vertical-align: middle;\n  border: 0;\n  -ms-interpolation-mode: bicubic;\n}\n\n#map_canvas img {\n  max-width: none;\n}\n\nbutton,\ninput,\nselect,\ntextarea {\n  margin: 0;\n  font-size: 100%;\n  vertical-align: middle;\n}\n\nbutton,\ninput {\n  *overflow: visible;\n  line-height: normal;\n}\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  padding: 0;\n  border: 0;\n}\n\nbutton,\ninput[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  cursor: pointer;\n  -webkit-appearance: button;\n}\n\ninput[type=\"search\"] {\n  -webkit-box-sizing: content-box;\n     -moz-box-sizing: content-box;\n          box-sizing: content-box;\n  -webkit-appearance: textfield;\n}\n\ninput[type=\"search\"]::-webkit-search-decoration,\ninput[type=\"search\"]::-webkit-search-cancel-button {\n  -webkit-appearance: none;\n}\n\ntextarea {\n  overflow: auto;\n  vertical-align: top;\n}\n\n.clearfix {\n  *zoom: 1;\n}\n\n.clearfix:before,\n.clearfix:after {\n  display: table;\n  content: \"\";\n}\n\n.clearfix:after {\n  clear: both;\n}\n\n.hide-text {\n  font: 0/0 a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n\n.input-block-level {\n  display: block;\n  width: 100%;\n  min-height: 28px;\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n      -ms-box-sizing: border-box;\n          box-sizing: border-box;\n}\n\nbody {\n  margin: 0;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 13px;\n  line-height: 18px;\n  color: #333333;\n  background-color: #ffffff;\n}\n\na {\n  color: #0088cc;\n  text-decoration: none;\n}\n\na:hover {\n  color: #005580;\n  text-decoration: underline;\n}\n\n.row {\n  margin-left: -20px;\n  *zoom: 1;\n}\n\n.row:before,\n.row:after {\n  display: table;\n  content: \"\";\n}\n\n.row:after {\n  clear: both;\n}\n\n[class*=\"span\"] {\n  float: left;\n  margin-left: 20px;\n}\n\n.container,\n.navbar-fixed-top .container,\n.navbar-fixed-bottom .container {\n  width: 940px;\n}\n\n.span12 {\n  width: 940px;\n}\n\n.span11 {\n  width: 860px;\n}\n\n.span10 {\n  width: 780px;\n}\n\n.span9 {\n  width: 700px;\n}\n\n.span8 {\n  width: 620px;\n}\n\n.span7 {\n  width: 540px;\n}\n\n.span6 {\n  width: 460px;\n}\n\n.span5 {\n  width: 380px;\n}\n\n.span4 {\n  width: 300px;\n}\n\n.span3 {\n  width: 220px;\n}\n\n.span2 {\n  width: 140px;\n}\n\n.span1 {\n  width: 60px;\n}\n\n.offset12 {\n  margin-left: 980px;\n}\n\n.offset11 {\n  margin-left: 900px;\n}\n\n.offset10 {\n  margin-left: 820px;\n}\n\n.offset9 {\n  margin-left: 740px;\n}\n\n.offset8 {\n  margin-left: 660px;\n}\n\n.offset7 {\n  margin-left: 580px;\n}\n\n.offset6 {\n  margin-left: 500px;\n}\n\n.offset5 {\n  margin-left: 420px;\n}\n\n.offset4 {\n  margin-left: 340px;\n}\n\n.offset3 {\n  margin-left: 260px;\n}\n\n.offset2 {\n  margin-left: 180px;\n}\n\n.offset1 {\n  margin-left: 100px;\n}\n\n.row-fluid {\n  width: 100%;\n  *zoom: 1;\n}\n\n.row-fluid:before,\n.row-fluid:after {\n  display: table;\n  content: \"\";\n}\n\n.row-fluid:after {\n  clear: both;\n}\n\n.row-fluid [class*=\"span\"] {\n  display: block;\n  float: left;\n  width: 100%;\n  min-height: 28px;\n  margin-left: 2.127659574%;\n  *margin-left: 2.0744680846382977%;\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n      -ms-box-sizing: border-box;\n          box-sizing: border-box;\n}\n\n.row-fluid [class*=\"span\"]:first-child {\n  margin-left: 0;\n}\n\n.row-fluid .span12 {\n  width: 99.99999998999999%;\n  *width: 99.94680850063828%;\n}\n\n.row-fluid .span11 {\n  width: 91.489361693%;\n  *width: 91.4361702036383%;\n}\n\n.row-fluid .span10 {\n  width: 82.97872339599999%;\n  *width: 82.92553190663828%;\n}\n\n.row-fluid .span9 {\n  width: 74.468085099%;\n  *width: 74.4148936096383%;\n}\n\n.row-fluid .span8 {\n  width: 65.95744680199999%;\n  *width: 65.90425531263828%;\n}\n\n.row-fluid .span7 {\n  width: 57.446808505%;\n  *width: 57.3936170156383%;\n}\n\n.row-fluid .span6 {\n  width: 48.93617020799999%;\n  *width: 48.88297871863829%;\n}\n\n.row-fluid .span5 {\n  width: 40.425531911%;\n  *width: 40.3723404216383%;\n}\n\n.row-fluid .span4 {\n  width: 31.914893614%;\n  *width: 31.8617021246383%;\n}\n\n.row-fluid .span3 {\n  width: 23.404255317%;\n  *width: 23.3510638276383%;\n}\n\n.row-fluid .span2 {\n  width: 14.89361702%;\n  *width: 14.8404255306383%;\n}\n\n.row-fluid .span1 {\n  width: 6.382978723%;\n  *width: 6.329787233638298%;\n}\n\n.container {\n  margin-right: auto;\n  margin-left: auto;\n  *zoom: 1;\n}\n\n.container:before,\n.container:after {\n  display: table;\n  content: \"\";\n}\n\n.container:after {\n  clear: both;\n}\n\n.container-fluid {\n  padding-right: 20px;\n  padding-left: 20px;\n  *zoom: 1;\n}\n\n.container-fluid:before,\n.container-fluid:after {\n  display: table;\n  content: \"\";\n}\n\n.container-fluid:after {\n  clear: both;\n}\n\np {\n  margin: 0 0 9px;\n}\n\np small {\n  font-size: 11px;\n  color: #999999;\n}\n\n.lead {\n  margin-bottom: 18px;\n  font-size: 20px;\n  font-weight: 200;\n  line-height: 27px;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n  margin: 0;\n  font-family: inherit;\n  font-weight: bold;\n  color: inherit;\n  text-rendering: optimizelegibility;\n}\n\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small {\n  font-weight: normal;\n  color: #999999;\n}\n\nh1 {\n  font-size: 30px;\n  line-height: 36px;\n}\n\nh1 small {\n  font-size: 18px;\n}\n\nh2 {\n  font-size: 24px;\n  line-height: 36px;\n}\n\nh2 small {\n  font-size: 18px;\n}\n\nh3 {\n  font-size: 18px;\n  line-height: 27px;\n}\n\nh3 small {\n  font-size: 14px;\n}\n\nh4,\nh5,\nh6 {\n  line-height: 18px;\n}\n\nh4 {\n  font-size: 14px;\n}\n\nh4 small {\n  font-size: 12px;\n}\n\nh5 {\n  font-size: 12px;\n}\n\nh6 {\n  font-size: 11px;\n  color: #999999;\n  text-transform: uppercase;\n}\n\n.page-header {\n  padding-bottom: 17px;\n  margin: 18px 0;\n  border-bottom: 1px solid #eeeeee;\n}\n\n.page-header h1 {\n  line-height: 1;\n}\n\nul,\nol {\n  padding: 0;\n  margin: 0 0 9px 25px;\n}\n\nul ul,\nul ol,\nol ol,\nol ul {\n  margin-bottom: 0;\n}\n\nul {\n  list-style: disc;\n}\n\nol {\n  list-style: decimal;\n}\n\nli {\n  line-height: 18px;\n}\n\nul.unstyled,\nol.unstyled {\n  margin-left: 0;\n  list-style: none;\n}\n\ndl {\n  margin-bottom: 18px;\n}\n\ndt,\ndd {\n  line-height: 18px;\n}\n\ndt {\n  font-weight: bold;\n  line-height: 17px;\n}\n\ndd {\n  margin-left: 9px;\n}\n\n.dl-horizontal dt {\n  float: left;\n  width: 120px;\n  overflow: hidden;\n  clear: left;\n  text-align: right;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.dl-horizontal dd {\n  margin-left: 130px;\n}\n\nhr {\n  margin: 18px 0;\n  border: 0;\n  border-top: 1px solid #eeeeee;\n  border-bottom: 1px solid #ffffff;\n}\n\nstrong {\n  font-weight: bold;\n}\n\nem {\n  font-style: italic;\n}\n\n.muted {\n  color: #999999;\n}\n\nabbr[title] {\n  cursor: help;\n  border-bottom: 1px dotted #999999;\n}\n\nabbr.initialism {\n  font-size: 90%;\n  text-transform: uppercase;\n}\n\nblockquote {\n  padding: 0 0 0 15px;\n  margin: 0 0 18px;\n  border-left: 5px solid #eeeeee;\n}\n\nblockquote p {\n  margin-bottom: 0;\n  font-size: 16px;\n  font-weight: 300;\n  line-height: 22.5px;\n}\n\nblockquote small {\n  display: block;\n  line-height: 18px;\n  color: #999999;\n}\n\nblockquote small:before {\n  content: '\\2014 \\00A0';\n}\n\nblockquote.pull-right {\n  float: right;\n  padding-right: 15px;\n  padding-left: 0;\n  border-right: 5px solid #eeeeee;\n  border-left: 0;\n}\n\nblockquote.pull-right p,\nblockquote.pull-right small {\n  text-align: right;\n}\n\nq:before,\nq:after,\nblockquote:before,\nblockquote:after {\n  content: \"\";\n}\n\naddress {\n  display: block;\n  margin-bottom: 18px;\n  font-style: normal;\n  line-height: 18px;\n}\n\nsmall {\n  font-size: 100%;\n}\n\ncite {\n  font-style: normal;\n}\n\ncode,\npre {\n  padding: 0 3px 2px;\n  font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n  font-size: 12px;\n  color: #333333;\n  -webkit-border-radius: 3px;\n     -moz-border-radius: 3px;\n          border-radius: 3px;\n}\n\ncode {\n  padding: 2px 4px;\n  color: #d14;\n  background-color: #f7f7f9;\n  border: 1px solid #e1e1e8;\n}\n\npre {\n  display: block;\n  padding: 8.5px;\n  margin: 0 0 9px;\n  font-size: 12.025px;\n  line-height: 18px;\n  word-break: break-all;\n  word-wrap: break-word;\n  white-space: pre;\n  white-space: pre-wrap;\n  background-color: #f5f5f5;\n  border: 1px solid #ccc;\n  border: 1px solid rgba(0, 0, 0, 0.15);\n  -webkit-border-radius: 4px;\n     -moz-border-radius: 4px;\n          border-radius: 4px;\n}\n\npre.prettyprint {\n  margin-bottom: 18px;\n}\n\npre code {\n  padding: 0;\n  color: inherit;\n  background-color: transparent;\n  border: 0;\n}\n\n.pre-scrollable {\n  max-height: 340px;\n  overflow-y: scroll;\n}\n\nform {\n  margin: 0 0 18px;\n}\n\nfieldset {\n  padding: 0;\n  margin: 0;\n  border: 0;\n}\n\nlegend {\n  display: block;\n  width: 100%;\n  padding: 0;\n  margin-bottom: 27px;\n  font-size: 19.5px;\n  line-height: 36px;\n  color: #333333;\n  border: 0;\n  border-bottom: 1px solid #e5e5e5;\n}\n\nlegend small {\n  font-size: 13.5px;\n  color: #999999;\n}\n\nlabel,\ninput,\nbutton,\nselect,\ntextarea {\n  font-size: 13px;\n  font-weight: normal;\n  line-height: 18px;\n}\n\ninput,\nbutton,\nselect,\ntextarea {\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n}\n\nlabel {\n  display: block;\n  margin-bottom: 5px;\n}\n\nselect,\ntextarea,\ninput[type=\"text\"],\ninput[type=\"password\"],\ninput[type=\"datetime\"],\ninput[type=\"datetime-local\"],\ninput[type=\"date\"],\ninput[type=\"month\"],\ninput[type=\"time\"],\ninput[type=\"week\"],\ninput[type=\"number\"],\ninput[type=\"email\"],\ninput[type=\"url\"],\ninput[type=\"search\"],\ninput[type=\"tel\"],\ninput[type=\"color\"],\n.uneditable-input {\n  display: inline-block;\n  height: 18px;\n  padding: 4px;\n  margin-bottom: 9px;\n  font-size: 13px;\n  line-height: 18px;\n  color: #555555;\n}\n\ninput,\ntextarea {\n  width: 210px;\n}\n\ntextarea {\n  height: auto;\n}\n\ntextarea,\ninput[type=\"text\"],\ninput[type=\"password\"],\ninput[type=\"datetime\"],\ninput[type=\"datetime-local\"],\ninput[type=\"date\"],\ninput[type=\"month\"],\ninput[type=\"time\"],\ninput[type=\"week\"],\ninput[type=\"number\"],\ninput[type=\"email\"],\ninput[type=\"url\"],\ninput[type=\"search\"],\ninput[type=\"tel\"],\ninput[type=\"color\"],\n.uneditable-input {\n  background-color: #ffffff;\n  border: 1px solid #cccccc;\n  -webkit-border-radius: 3px;\n     -moz-border-radius: 3px;\n          border-radius: 3px;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;\n     -moz-transition: border linear 0.2s, box-shadow linear 0.2s;\n      -ms-transition: border linear 0.2s, box-shadow linear 0.2s;\n       -o-transition: border linear 0.2s, box-shadow linear 0.2s;\n          transition: border linear 0.2s, box-shadow linear 0.2s;\n}\n\ntextarea:focus,\ninput[type=\"text\"]:focus,\ninput[type=\"password\"]:focus,\ninput[type=\"datetime\"]:focus,\ninput[type=\"datetime-local\"]:focus,\ninput[type=\"date\"]:focus,\ninput[type=\"month\"]:focus,\ninput[type=\"time\"]:focus,\ninput[type=\"week\"]:focus,\ninput[type=\"number\"]:focus,\ninput[type=\"email\"]:focus,\ninput[type=\"url\"]:focus,\ninput[type=\"search\"]:focus,\ninput[type=\"tel\"]:focus,\ninput[type=\"color\"]:focus,\n.uneditable-input:focus {\n  border-color: rgba(82, 168, 236, 0.8);\n  outline: 0;\n  outline: thin dotted \\9;\n  /* IE6-9 */\n\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);\n     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  margin: 3px 0;\n  *margin-top: 0;\n  /* IE7 */\n\n  line-height: normal;\n  cursor: pointer;\n}\n\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"],\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  width: auto;\n}\n\n.uneditable-textarea {\n  width: auto;\n  height: auto;\n}\n\nselect,\ninput[type=\"file\"] {\n  height: 28px;\n  /* In IE7, the height of the select element cannot be changed by height, only font-size */\n\n  *margin-top: 4px;\n  /* For IE7, add top margin to align select with labels */\n\n  line-height: 28px;\n}\n\nselect {\n  width: 220px;\n  border: 1px solid #bbb;\n}\n\nselect[multiple],\nselect[size] {\n  height: auto;\n}\n\nselect:focus,\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n  outline: thin dotted #333;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n\n.radio,\n.checkbox {\n  min-height: 18px;\n  padding-left: 18px;\n}\n\n.radio input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"] {\n  float: left;\n  margin-left: -18px;\n}\n\n.controls > .radio:first-child,\n.controls > .checkbox:first-child {\n  padding-top: 5px;\n}\n\n.radio.inline,\n.checkbox.inline {\n  display: inline-block;\n  padding-top: 5px;\n  margin-bottom: 0;\n  vertical-align: middle;\n}\n\n.radio.inline + .radio.inline,\n.checkbox.inline + .checkbox.inline {\n  margin-left: 10px;\n}\n\n.input-mini {\n  width: 60px;\n}\n\n.input-small {\n  width: 90px;\n}\n\n.input-medium {\n  width: 150px;\n}\n\n.input-large {\n  width: 210px;\n}\n\n.input-xlarge {\n  width: 270px;\n}\n\n.input-xxlarge {\n  width: 530px;\n}\n\ninput[class*=\"span\"],\nselect[class*=\"span\"],\ntextarea[class*=\"span\"],\n.uneditable-input[class*=\"span\"],\n.row-fluid input[class*=\"span\"],\n.row-fluid select[class*=\"span\"],\n.row-fluid textarea[class*=\"span\"],\n.row-fluid .uneditable-input[class*=\"span\"] {\n  float: none;\n  margin-left: 0;\n}\n\n.input-append input[class*=\"span\"],\n.input-append .uneditable-input[class*=\"span\"],\n.input-prepend input[class*=\"span\"],\n.input-prepend .uneditable-input[class*=\"span\"],\n.row-fluid .input-prepend [class*=\"span\"],\n.row-fluid .input-append [class*=\"span\"] {\n  display: inline-block;\n}\n\ninput,\ntextarea,\n.uneditable-input {\n  margin-left: 0;\n}\n\ninput.span12,\ntextarea.span12,\n.uneditable-input.span12 {\n  width: 930px;\n}\n\ninput.span11,\ntextarea.span11,\n.uneditable-input.span11 {\n  width: 850px;\n}\n\ninput.span10,\ntextarea.span10,\n.uneditable-input.span10 {\n  width: 770px;\n}\n\ninput.span9,\ntextarea.span9,\n.uneditable-input.span9 {\n  width: 690px;\n}\n\ninput.span8,\ntextarea.span8,\n.uneditable-input.span8 {\n  width: 610px;\n}\n\ninput.span7,\ntextarea.span7,\n.uneditable-input.span7 {\n  width: 530px;\n}\n\ninput.span6,\ntextarea.span6,\n.uneditable-input.span6 {\n  width: 450px;\n}\n\ninput.span5,\ntextarea.span5,\n.uneditable-input.span5 {\n  width: 370px;\n}\n\ninput.span4,\ntextarea.span4,\n.uneditable-input.span4 {\n  width: 290px;\n}\n\ninput.span3,\ntextarea.span3,\n.uneditable-input.span3 {\n  width: 210px;\n}\n\ninput.span2,\ntextarea.span2,\n.uneditable-input.span2 {\n  width: 130px;\n}\n\ninput.span1,\ntextarea.span1,\n.uneditable-input.span1 {\n  width: 50px;\n}\n\ninput[disabled],\nselect[disabled],\ntextarea[disabled],\ninput[readonly],\nselect[readonly],\ntextarea[readonly] {\n  cursor: not-allowed;\n  background-color: #eeeeee;\n  border-color: #ddd;\n}\n\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"][readonly],\ninput[type=\"checkbox\"][readonly] {\n  background-color: transparent;\n}\n\n.control-group.warning > label,\n.control-group.warning .help-block,\n.control-group.warning .help-inline {\n  color: #c09853;\n}\n\n.control-group.warning .checkbox,\n.control-group.warning .radio,\n.control-group.warning input,\n.control-group.warning select,\n.control-group.warning textarea {\n  color: #c09853;\n  border-color: #c09853;\n}\n\n.control-group.warning .checkbox:focus,\n.control-group.warning .radio:focus,\n.control-group.warning input:focus,\n.control-group.warning select:focus,\n.control-group.warning textarea:focus {\n  border-color: #a47e3c;\n  -webkit-box-shadow: 0 0 6px #dbc59e;\n     -moz-box-shadow: 0 0 6px #dbc59e;\n          box-shadow: 0 0 6px #dbc59e;\n}\n\n.control-group.warning .input-prepend .add-on,\n.control-group.warning .input-append .add-on {\n  color: #c09853;\n  background-color: #fcf8e3;\n  border-color: #c09853;\n}\n\n.control-group.error > label,\n.control-group.error .help-block,\n.control-group.error .help-inline {\n  color: #b94a48;\n}\n\n.control-group.error .checkbox,\n.control-group.error .radio,\n.control-group.error input,\n.control-group.error select,\n.control-group.error textarea {\n  color: #b94a48;\n  border-color: #b94a48;\n}\n\n.control-group.error .checkbox:focus,\n.control-group.error .radio:focus,\n.control-group.error input:focus,\n.control-group.error select:focus,\n.control-group.error textarea:focus {\n  border-color: #953b39;\n  -webkit-box-shadow: 0 0 6px #d59392;\n     -moz-box-shadow: 0 0 6px #d59392;\n          box-shadow: 0 0 6px #d59392;\n}\n\n.control-group.error .input-prepend .add-on,\n.control-group.error .input-append .add-on {\n  color: #b94a48;\n  background-color: #f2dede;\n  border-color: #b94a48;\n}\n\n.control-group.success > label,\n.control-group.success .help-block,\n.control-group.success .help-inline {\n  color: #468847;\n}\n\n.control-group.success .checkbox,\n.control-group.success .radio,\n.control-group.success input,\n.control-group.success select,\n.control-group.success textarea {\n  color: #468847;\n  border-color: #468847;\n}\n\n.control-group.success .checkbox:focus,\n.control-group.success .radio:focus,\n.control-group.success input:focus,\n.control-group.success select:focus,\n.control-group.success textarea:focus {\n  border-color: #356635;\n  -webkit-box-shadow: 0 0 6px #7aba7b;\n     -moz-box-shadow: 0 0 6px #7aba7b;\n          box-shadow: 0 0 6px #7aba7b;\n}\n\n.control-group.success .input-prepend .add-on,\n.control-group.success .input-append .add-on {\n  color: #468847;\n  background-color: #dff0d8;\n  border-color: #468847;\n}\n\ninput:focus:required:invalid,\ntextarea:focus:required:invalid,\nselect:focus:required:invalid {\n  color: #b94a48;\n  border-color: #ee5f5b;\n}\n\ninput:focus:required:invalid:focus,\ntextarea:focus:required:invalid:focus,\nselect:focus:required:invalid:focus {\n  border-color: #e9322d;\n  -webkit-box-shadow: 0 0 6px #f8b9b7;\n     -moz-box-shadow: 0 0 6px #f8b9b7;\n          box-shadow: 0 0 6px #f8b9b7;\n}\n\n.form-actions {\n  padding: 17px 20px 18px;\n  margin-top: 18px;\n  margin-bottom: 18px;\n  background-color: #f5f5f5;\n  border-top: 1px solid #e5e5e5;\n  *zoom: 1;\n}\n\n.form-actions:before,\n.form-actions:after {\n  display: table;\n  content: \"\";\n}\n\n.form-actions:after {\n  clear: both;\n}\n\n.uneditable-input {\n  overflow: hidden;\n  white-space: nowrap;\n  cursor: not-allowed;\n  background-color: #ffffff;\n  border-color: #eee;\n  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);\n     -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);\n          box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);\n}\n\n:-moz-placeholder {\n  color: #999999;\n}\n\n:-ms-input-placeholder {\n  color: #999999;\n}\n\n::-webkit-input-placeholder {\n  color: #999999;\n}\n\n.help-block,\n.help-inline {\n  color: #555555;\n}\n\n.help-block {\n  display: block;\n  margin-bottom: 9px;\n}\n\n.help-inline {\n  display: inline-block;\n  *display: inline;\n  padding-left: 5px;\n  vertical-align: middle;\n  *zoom: 1;\n}\n\n.input-prepend,\n.input-append {\n  margin-bottom: 5px;\n}\n\n.input-prepend input,\n.input-append input,\n.input-prepend select,\n.input-append select,\n.input-prepend .uneditable-input,\n.input-append .uneditable-input {\n  position: relative;\n  margin-bottom: 0;\n  *margin-left: 0;\n  vertical-align: middle;\n  -webkit-border-radius: 0 3px 3px 0;\n     -moz-border-radius: 0 3px 3px 0;\n          border-radius: 0 3px 3px 0;\n}\n\n.input-prepend input:focus,\n.input-append input:focus,\n.input-prepend select:focus,\n.input-append select:focus,\n.input-prepend .uneditable-input:focus,\n.input-append .uneditable-input:focus {\n  z-index: 2;\n}\n\n.input-prepend .uneditable-input,\n.input-append .uneditable-input {\n  border-left-color: #ccc;\n}\n\n.input-prepend .add-on,\n.input-append .add-on {\n  display: inline-block;\n  width: auto;\n  height: 18px;\n  min-width: 16px;\n  padding: 4px 5px;\n  font-weight: normal;\n  line-height: 18px;\n  text-align: center;\n  text-shadow: 0 1px 0 #ffffff;\n  vertical-align: middle;\n  background-color: #eeeeee;\n  border: 1px solid #ccc;\n}\n\n.input-prepend .add-on,\n.input-append .add-on,\n.input-prepend .btn,\n.input-append .btn {\n  margin-left: -1px;\n  -webkit-border-radius: 0;\n     -moz-border-radius: 0;\n          border-radius: 0;\n}\n\n.input-prepend .active,\n.input-append .active {\n  background-color: #a9dba9;\n  border-color: #46a546;\n}\n\n.input-prepend .add-on,\n.input-prepend .btn {\n  margin-right: -1px;\n}\n\n.input-prepend .add-on:first-child,\n.input-prepend .btn:first-child {\n  -webkit-border-radius: 3px 0 0 3px;\n     -moz-border-radius: 3px 0 0 3px;\n          border-radius: 3px 0 0 3px;\n}\n\n.input-append input,\n.input-append select,\n.input-append .uneditable-input {\n  -webkit-border-radius: 3px 0 0 3px;\n     -moz-border-radius: 3px 0 0 3px;\n          border-radius: 3px 0 0 3px;\n}\n\n.input-append .uneditable-input {\n  border-right-color: #ccc;\n  border-left-color: #eee;\n}\n\n.input-append .add-on:last-child,\n.input-append .btn:last-child {\n  -webkit-border-radius: 0 3px 3px 0;\n     -moz-border-radius: 0 3px 3px 0;\n          border-radius: 0 3px 3px 0;\n}\n\n.input-prepend.input-append input,\n.input-prepend.input-append select,\n.input-prepend.input-append .uneditable-input {\n  -webkit-border-radius: 0;\n     -moz-border-radius: 0;\n          border-radius: 0;\n}\n\n.input-prepend.input-append .add-on:first-child,\n.input-prepend.input-append .btn:first-child {\n  margin-right: -1px;\n  -webkit-border-radius: 3px 0 0 3px;\n     -moz-border-radius: 3px 0 0 3px;\n          border-radius: 3px 0 0 3px;\n}\n\n.input-prepend.input-append .add-on:last-child,\n.input-prepend.input-append .btn:last-child {\n  margin-left: -1px;\n  -webkit-border-radius: 0 3px 3px 0;\n     -moz-border-radius: 0 3px 3px 0;\n          border-radius: 0 3px 3px 0;\n}\n\n.search-query {\n  padding-right: 14px;\n  padding-right: 4px \\9;\n  padding-left: 14px;\n  padding-left: 4px \\9;\n  /* IE7-8 doesn't have border-radius, so don't indent the padding */\n\n  margin-bottom: 0;\n  -webkit-border-radius: 14px;\n     -moz-border-radius: 14px;\n          border-radius: 14px;\n}\n\n.form-search input,\n.form-inline input,\n.form-horizontal input,\n.form-search textarea,\n.form-inline textarea,\n.form-horizontal textarea,\n.form-search select,\n.form-inline select,\n.form-horizontal select,\n.form-search .help-inline,\n.form-inline .help-inline,\n.form-horizontal .help-inline,\n.form-search .uneditable-input,\n.form-inline .uneditable-input,\n.form-horizontal .uneditable-input,\n.form-search .input-prepend,\n.form-inline .input-prepend,\n.form-horizontal .input-prepend,\n.form-search .input-append,\n.form-inline .input-append,\n.form-horizontal .input-append {\n  display: inline-block;\n  *display: inline;\n  margin-bottom: 0;\n  *zoom: 1;\n}\n\n.form-search .hide,\n.form-inline .hide,\n.form-horizontal .hide {\n  display: none;\n}\n\n.form-search label,\n.form-inline label {\n  display: inline-block;\n}\n\n.form-search .input-append,\n.form-inline .input-append,\n.form-search .input-prepend,\n.form-inline .input-prepend {\n  margin-bottom: 0;\n}\n\n.form-search .radio,\n.form-search .checkbox,\n.form-inline .radio,\n.form-inline .checkbox {\n  padding-left: 0;\n  margin-bottom: 0;\n  vertical-align: middle;\n}\n\n.form-search .radio input[type=\"radio\"],\n.form-search .checkbox input[type=\"checkbox\"],\n.form-inline .radio input[type=\"radio\"],\n.form-inline .checkbox input[type=\"checkbox\"] {\n  float: left;\n  margin-right: 3px;\n  margin-left: 0;\n}\n\n.control-group {\n  margin-bottom: 9px;\n}\n\nlegend + .control-group {\n  margin-top: 18px;\n  -webkit-margin-top-collapse: separate;\n}\n\n.form-horizontal .control-group {\n  margin-bottom: 18px;\n  *zoom: 1;\n}\n\n.form-horizontal .control-group:before,\n.form-horizontal .control-group:after {\n  display: table;\n  content: \"\";\n}\n\n.form-horizontal .control-group:after {\n  clear: both;\n}\n\n.form-horizontal .control-label {\n  float: left;\n  width: 140px;\n  padding-top: 5px;\n  text-align: right;\n}\n\n.form-horizontal .controls {\n  *display: inline-block;\n  *padding-left: 20px;\n  margin-left: 160px;\n  *margin-left: 0;\n}\n\n.form-horizontal .controls:first-child {\n  *padding-left: 160px;\n}\n\n.form-horizontal .help-block {\n  margin-top: 9px;\n  margin-bottom: 0;\n}\n\n.form-horizontal .form-actions {\n  padding-left: 160px;\n}\n\ntable {\n  max-width: 100%;\n  background-color: transparent;\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\n.table {\n  width: 100%;\n  margin-bottom: 18px;\n}\n\n.table th,\n.table td {\n  padding: 8px;\n  line-height: 18px;\n  text-align: left;\n  vertical-align: top;\n  border-top: 1px solid #dddddd;\n}\n\n.table th {\n  font-weight: bold;\n}\n\n.table thead th {\n  vertical-align: bottom;\n}\n\n.table caption + thead tr:first-child th,\n.table caption + thead tr:first-child td,\n.table colgroup + thead tr:first-child th,\n.table colgroup + thead tr:first-child td,\n.table thead:first-child tr:first-child th,\n.table thead:first-child tr:first-child td {\n  border-top: 0;\n}\n\n.table tbody + tbody {\n  border-top: 2px solid #dddddd;\n}\n\n.table-condensed th,\n.table-condensed td {\n  padding: 4px 5px;\n}\n\n.table-bordered {\n  border: 1px solid #dddddd;\n  border-collapse: separate;\n  *border-collapse: collapsed;\n  border-left: 0;\n  -webkit-border-radius: 4px;\n     -moz-border-radius: 4px;\n          border-radius: 4px;\n}\n\n.table-bordered th,\n.table-bordered td {\n  border-left: 1px solid #dddddd;\n}\n\n.table-bordered caption + thead tr:first-child th,\n.table-bordered caption + tbody tr:first-child th,\n.table-bordered caption + tbody tr:first-child td,\n.table-bordered colgroup + thead tr:first-child th,\n.table-bordered colgroup + tbody tr:first-child th,\n.table-bordered colgroup + tbody tr:first-child td,\n.table-bordered thead:first-child tr:first-child th,\n.table-bordered tbody:first-child tr:first-child th,\n.table-bordered tbody:first-child tr:first-child td {\n  border-top: 0;\n}\n\n.table-bordered thead:first-child tr:first-child th:first-child,\n.table-bordered tbody:first-child tr:first-child td:first-child {\n  -webkit-border-top-left-radius: 4px;\n          border-top-left-radius: 4px;\n  -moz-border-radius-topleft: 4px;\n}\n\n.table-bordered thead:first-child tr:first-child th:last-child,\n.table-bordered tbody:first-child tr:first-child td:last-child {\n  -webkit-border-top-right-radius: 4px;\n          border-top-right-radius: 4px;\n  -moz-border-radius-topright: 4px;\n}\n\n.table-bordered thead:last-child tr:last-child th:first-child,\n.table-bordered tbody:last-child tr:last-child td:first-child {\n  -webkit-border-radius: 0 0 0 4px;\n     -moz-border-radius: 0 0 0 4px;\n          border-radius: 0 0 0 4px;\n  -webkit-border-bottom-left-radius: 4px;\n          border-bottom-left-radius: 4px;\n  -moz-border-radius-bottomleft: 4px;\n}\n\n.table-bordered thead:last-child tr:last-child th:last-child,\n.table-bordered tbody:last-child tr:last-child td:last-child {\n  -webkit-border-bottom-right-radius: 4px;\n          border-bottom-right-radius: 4px;\n  -moz-border-radius-bottomright: 4px;\n}\n\n.table-striped tbody tr:nth-child(odd) td,\n.table-striped tbody tr:nth-child(odd) th {\n  background-color: #f9f9f9;\n}\n\n.table tbody tr:hover td,\n.table tbody tr:hover th {\n  background-color: #f5f5f5;\n}\n\ntable .span1 {\n  float: none;\n  width: 44px;\n  margin-left: 0;\n}\n\ntable .span2 {\n  float: none;\n  width: 124px;\n  margin-left: 0;\n}\n\ntable .span3 {\n  float: none;\n  width: 204px;\n  margin-left: 0;\n}\n\ntable .span4 {\n  float: none;\n  width: 284px;\n  margin-left: 0;\n}\n\ntable .span5 {\n  float: none;\n  width: 364px;\n  margin-left: 0;\n}\n\ntable .span6 {\n  float: none;\n  width: 444px;\n  margin-left: 0;\n}\n\ntable .span7 {\n  float: none;\n  width: 524px;\n  margin-left: 0;\n}\n\ntable .span8 {\n  float: none;\n  width: 604px;\n  margin-left: 0;\n}\n\ntable .span9 {\n  float: none;\n  width: 684px;\n  margin-left: 0;\n}\n\ntable .span10 {\n  float: none;\n  width: 764px;\n  margin-left: 0;\n}\n\ntable .span11 {\n  float: none;\n  width: 844px;\n  margin-left: 0;\n}\n\ntable .span12 {\n  float: none;\n  width: 924px;\n  margin-left: 0;\n}\n\ntable .span13 {\n  float: none;\n  width: 1004px;\n  margin-left: 0;\n}\n\ntable .span14 {\n  float: none;\n  width: 1084px;\n  margin-left: 0;\n}\n\ntable .span15 {\n  float: none;\n  width: 1164px;\n  margin-left: 0;\n}\n\ntable .span16 {\n  float: none;\n  width: 1244px;\n  margin-left: 0;\n}\n\ntable .span17 {\n  float: none;\n  width: 1324px;\n  margin-left: 0;\n}\n\ntable .span18 {\n  float: none;\n  width: 1404px;\n  margin-left: 0;\n}\n\ntable .span19 {\n  float: none;\n  width: 1484px;\n  margin-left: 0;\n}\n\ntable .span20 {\n  float: none;\n  width: 1564px;\n  margin-left: 0;\n}\n\ntable .span21 {\n  float: none;\n  width: 1644px;\n  margin-left: 0;\n}\n\ntable .span22 {\n  float: none;\n  width: 1724px;\n  margin-left: 0;\n}\n\ntable .span23 {\n  float: none;\n  width: 1804px;\n  margin-left: 0;\n}\n\ntable .span24 {\n  float: none;\n  width: 1884px;\n  margin-left: 0;\n}\n\n[class^=\"icon-\"],\n[class*=\" icon-\"] {\n  display: inline-block;\n  width: 14px;\n  height: 14px;\n  *margin-right: .3em;\n  line-height: 14px;\n  vertical-align: text-top;\n  background-image: url(\"../img/glyphicons-halflings.png\");\n  background-position: 14px 14px;\n  background-repeat: no-repeat;\n}\n\n[class^=\"icon-\"]:last-child,\n[class*=\" icon-\"]:last-child {\n  *margin-left: 0;\n}\n\n.icon-white {\n  background-image: url(\"../img/glyphicons-halflings-white.png\");\n}\n\n.icon-glass {\n  background-position: 0      0;\n}\n\n.icon-music {\n  background-position: -24px 0;\n}\n\n.icon-search {\n  background-position: -48px 0;\n}\n\n.icon-envelope {\n  background-position: -72px 0;\n}\n\n.icon-heart {\n  background-position: -96px 0;\n}\n\n.icon-star {\n  background-position: -120px 0;\n}\n\n.icon-star-empty {\n  background-position: -144px 0;\n}\n\n.icon-user {\n  background-position: -168px 0;\n}\n\n.icon-film {\n  background-position: -192px 0;\n}\n\n.icon-th-large {\n  background-position: -216px 0;\n}\n\n.icon-th {\n  background-position: -240px 0;\n}\n\n.icon-th-list {\n  background-position: -264px 0;\n}\n\n.icon-ok {\n  background-position: -288px 0;\n}\n\n.icon-remove {\n  background-position: -312px 0;\n}\n\n.icon-zoom-in {\n  background-position: -336px 0;\n}\n\n.icon-zoom-out {\n  background-position: -360px 0;\n}\n\n.icon-off {\n  background-position: -384px 0;\n}\n\n.icon-signal {\n  background-position: -408px 0;\n}\n\n.icon-cog {\n  background-position: -432px 0;\n}\n\n.icon-trash {\n  background-position: -456px 0;\n}\n\n.icon-home {\n  background-position: 0 -24px;\n}\n\n.icon-file {\n  background-position: -24px -24px;\n}\n\n.icon-time {\n  background-position: -48px -24px;\n}\n\n.icon-road {\n  background-position: -72px -24px;\n}\n\n.icon-download-alt {\n  background-position: -96px -24px;\n}\n\n.icon-download {\n  background-position: -120px -24px;\n}\n\n.icon-upload {\n  background-position: -144px -24px;\n}\n\n.icon-inbox {\n  background-position: -168px -24px;\n}\n\n.icon-play-circle {\n  background-position: -192px -24px;\n}\n\n.icon-repeat {\n  background-position: -216px -24px;\n}\n\n.icon-refresh {\n  background-position: -240px -24px;\n}\n\n.icon-list-alt {\n  background-position: -264px -24px;\n}\n\n.icon-lock {\n  background-position: -287px -24px;\n}\n\n.icon-flag {\n  background-position: -312px -24px;\n}\n\n.icon-headphones {\n  background-position: -336px -24px;\n}\n\n.icon-volume-off {\n  background-position: -360px -24px;\n}\n\n.icon-volume-down {\n  background-position: -384px -24px;\n}\n\n.icon-volume-up {\n  background-position: -408px -24px;\n}\n\n.icon-qrcode {\n  background-position: -432px -24px;\n}\n\n.icon-barcode {\n  background-position: -456px -24px;\n}\n\n.icon-tag {\n  background-position: 0 -48px;\n}\n\n.icon-tags {\n  background-position: -25px -48px;\n}\n\n.icon-book {\n  background-position: -48px -48px;\n}\n\n.icon-bookmark {\n  background-position: -72px -48px;\n}\n\n.icon-print {\n  background-position: -96px -48px;\n}\n\n.icon-camera {\n  background-position: -120px -48px;\n}\n\n.icon-font {\n  background-position: -144px -48px;\n}\n\n.icon-bold {\n  background-position: -167px -48px;\n}\n\n.icon-italic {\n  background-position: -192px -48px;\n}\n\n.icon-text-height {\n  background-position: -216px -48px;\n}\n\n.icon-text-width {\n  background-position: -240px -48px;\n}\n\n.icon-align-left {\n  background-position: -264px -48px;\n}\n\n.icon-align-center {\n  background-position: -288px -48px;\n}\n\n.icon-align-right {\n  background-position: -312px -48px;\n}\n\n.icon-align-justify {\n  background-position: -336px -48px;\n}\n\n.icon-list {\n  background-position: -360px -48px;\n}\n\n.icon-indent-left {\n  background-position: -384px -48px;\n}\n\n.icon-indent-right {\n  background-position: -408px -48px;\n}\n\n.icon-facetime-video {\n  background-position: -432px -48px;\n}\n\n.icon-picture {\n  background-position: -456px -48px;\n}\n\n.icon-pencil {\n  background-position: 0 -72px;\n}\n\n.icon-map-marker {\n  background-position: -24px -72px;\n}\n\n.icon-adjust {\n  background-position: -48px -72px;\n}\n\n.icon-tint {\n  background-position: -72px -72px;\n}\n\n.icon-edit {\n  background-position: -96px -72px;\n}\n\n.icon-share {\n  background-position: -120px -72px;\n}\n\n.icon-check {\n  background-position: -144px -72px;\n}\n\n.icon-move {\n  background-position: -168px -72px;\n}\n\n.icon-step-backward {\n  background-position: -192px -72px;\n}\n\n.icon-fast-backward {\n  background-position: -216px -72px;\n}\n\n.icon-backward {\n  background-position: -240px -72px;\n}\n\n.icon-play {\n  background-position: -264px -72px;\n}\n\n.icon-pause {\n  background-position: -288px -72px;\n}\n\n.icon-stop {\n  background-position: -312px -72px;\n}\n\n.icon-forward {\n  background-position: -336px -72px;\n}\n\n.icon-fast-forward {\n  background-position: -360px -72px;\n}\n\n.icon-step-forward {\n  background-position: -384px -72px;\n}\n\n.icon-eject {\n  background-position: -408px -72px;\n}\n\n.icon-chevron-left {\n  background-position: -432px -72px;\n}\n\n.icon-chevron-right {\n  background-position: -456px -72px;\n}\n\n.icon-plus-sign {\n  background-position: 0 -96px;\n}\n\n.icon-minus-sign {\n  background-position: -24px -96px;\n}\n\n.icon-remove-sign {\n  background-position: -48px -96px;\n}\n\n.icon-ok-sign {\n  background-position: -72px -96px;\n}\n\n.icon-question-sign {\n  background-position: -96px -96px;\n}\n\n.icon-info-sign {\n  background-position: -120px -96px;\n}\n\n.icon-screenshot {\n  background-position: -144px -96px;\n}\n\n.icon-remove-circle {\n  background-position: -168px -96px;\n}\n\n.icon-ok-circle {\n  background-position: -192px -96px;\n}\n\n.icon-ban-circle {\n  background-position: -216px -96px;\n}\n\n.icon-arrow-left {\n  background-position: -240px -96px;\n}\n\n.icon-arrow-right {\n  background-position: -264px -96px;\n}\n\n.icon-arrow-up {\n  background-position: -289px -96px;\n}\n\n.icon-arrow-down {\n  background-position: -312px -96px;\n}\n\n.icon-share-alt {\n  background-position: -336px -96px;\n}\n\n.icon-resize-full {\n  background-position: -360px -96px;\n}\n\n.icon-resize-small {\n  background-position: -384px -96px;\n}\n\n.icon-plus {\n  background-position: -408px -96px;\n}\n\n.icon-minus {\n  background-position: -433px -96px;\n}\n\n.icon-asterisk {\n  background-position: -456px -96px;\n}\n\n.icon-exclamation-sign {\n  background-position: 0 -120px;\n}\n\n.icon-gift {\n  background-position: -24px -120px;\n}\n\n.icon-leaf {\n  background-position: -48px -120px;\n}\n\n.icon-fire {\n  background-position: -72px -120px;\n}\n\n.icon-eye-open {\n  background-position: -96px -120px;\n}\n\n.icon-eye-close {\n  background-position: -120px -120px;\n}\n\n.icon-warning-sign {\n  background-position: -144px -120px;\n}\n\n.icon-plane {\n  background-position: -168px -120px;\n}\n\n.icon-calendar {\n  background-position: -192px -120px;\n}\n\n.icon-random {\n  background-position: -216px -120px;\n}\n\n.icon-comment {\n  background-position: -240px -120px;\n}\n\n.icon-magnet {\n  background-position: -264px -120px;\n}\n\n.icon-chevron-up {\n  background-position: -288px -120px;\n}\n\n.icon-chevron-down {\n  background-position: -313px -119px;\n}\n\n.icon-retweet {\n  background-position: -336px -120px;\n}\n\n.icon-shopping-cart {\n  background-position: -360px -120px;\n}\n\n.icon-folder-close {\n  background-position: -384px -120px;\n}\n\n.icon-folder-open {\n  background-position: -408px -120px;\n}\n\n.icon-resize-vertical {\n  background-position: -432px -119px;\n}\n\n.icon-resize-horizontal {\n  background-position: -456px -118px;\n}\n\n.icon-hdd {\n  background-position: 0 -144px;\n}\n\n.icon-bullhorn {\n  background-position: -24px -144px;\n}\n\n.icon-bell {\n  background-position: -48px -144px;\n}\n\n.icon-certificate {\n  background-position: -72px -144px;\n}\n\n.icon-thumbs-up {\n  background-position: -96px -144px;\n}\n\n.icon-thumbs-down {\n  background-position: -120px -144px;\n}\n\n.icon-hand-right {\n  background-position: -144px -144px;\n}\n\n.icon-hand-left {\n  background-position: -168px -144px;\n}\n\n.icon-hand-up {\n  background-position: -192px -144px;\n}\n\n.icon-hand-down {\n  background-position: -216px -144px;\n}\n\n.icon-circle-arrow-right {\n  background-position: -240px -144px;\n}\n\n.icon-circle-arrow-left {\n  background-position: -264px -144px;\n}\n\n.icon-circle-arrow-up {\n  background-position: -288px -144px;\n}\n\n.icon-circle-arrow-down {\n  background-position: -312px -144px;\n}\n\n.icon-globe {\n  background-position: -336px -144px;\n}\n\n.icon-wrench {\n  background-position: -360px -144px;\n}\n\n.icon-tasks {\n  background-position: -384px -144px;\n}\n\n.icon-filter {\n  background-position: -408px -144px;\n}\n\n.icon-briefcase {\n  background-position: -432px -144px;\n}\n\n.icon-fullscreen {\n  background-position: -456px -144px;\n}\n\n.dropup,\n.dropdown {\n  position: relative;\n}\n\n.dropdown-toggle {\n  *margin-bottom: -3px;\n}\n\n.dropdown-toggle:active,\n.open .dropdown-toggle {\n  outline: 0;\n}\n\n.caret {\n  display: inline-block;\n  width: 0;\n  height: 0;\n  vertical-align: top;\n  border-top: 4px solid #000000;\n  border-right: 4px solid transparent;\n  border-left: 4px solid transparent;\n  content: \"\";\n  opacity: 0.3;\n  filter: alpha(opacity=30);\n}\n\n.dropdown .caret {\n  margin-top: 8px;\n  margin-left: 2px;\n}\n\n.dropdown:hover .caret,\n.open .caret {\n  opacity: 1;\n  filter: alpha(opacity=100);\n}\n\n.dropdown-menu {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: 1000;\n  display: none;\n  float: left;\n  min-width: 160px;\n  padding: 4px 0;\n  margin: 1px 0 0;\n  list-style: none;\n  background-color: #ffffff;\n  border: 1px solid #ccc;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  *border-right-width: 2px;\n  *border-bottom-width: 2px;\n  -webkit-border-radius: 5px;\n     -moz-border-radius: 5px;\n          border-radius: 5px;\n  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n     -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n          box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n  -webkit-background-clip: padding-box;\n     -moz-background-clip: padding;\n          background-clip: padding-box;\n}\n\n.dropdown-menu.pull-right {\n  right: 0;\n  left: auto;\n}\n\n.dropdown-menu .divider {\n  *width: 100%;\n  height: 1px;\n  margin: 8px 1px;\n  *margin: -5px 0 5px;\n  overflow: hidden;\n  background-color: #e5e5e5;\n  border-bottom: 1px solid #ffffff;\n}\n\n.dropdown-menu a {\n  display: block;\n  padding: 3px 15px;\n  clear: both;\n  font-weight: normal;\n  line-height: 18px;\n  color: #333333;\n  white-space: nowrap;\n}\n\n.dropdown-menu li > a:hover,\n.dropdown-menu .active > a,\n.dropdown-menu .active > a:hover {\n  color: #ffffff;\n  text-decoration: none;\n  background-color: #0088cc;\n}\n\n.open {\n  *z-index: 1000;\n}\n\n.open > .dropdown-menu {\n  display: block;\n}\n\n.pull-right > .dropdown-menu {\n  right: 0;\n  left: auto;\n}\n\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n  border-top: 0;\n  border-bottom: 4px solid #000000;\n  content: \"\\2191\";\n}\n\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n  top: auto;\n  bottom: 100%;\n  margin-bottom: 1px;\n}\n\n.typeahead {\n  margin-top: 2px;\n  -webkit-border-radius: 4px;\n     -moz-border-radius: 4px;\n          border-radius: 4px;\n}\n\n.well {\n  min-height: 20px;\n  padding: 19px;\n  margin-bottom: 20px;\n  background-color: #f5f5f5;\n  border: 1px solid #eee;\n  border: 1px solid rgba(0, 0, 0, 0.05);\n  -webkit-border-radius: 4px;\n     -moz-border-radius: 4px;\n          border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n\n.well blockquote {\n  border-color: #ddd;\n  border-color: rgba(0, 0, 0, 0.15);\n}\n\n.well-large {\n  padding: 24px;\n  -webkit-border-radius: 6px;\n     -moz-border-radius: 6px;\n          border-radius: 6px;\n}\n\n.well-small {\n  padding: 9px;\n  -webkit-border-radius: 3px;\n     -moz-border-radius: 3px;\n          border-radius: 3px;\n}\n\n.fade {\n  opacity: 0;\n  -webkit-transition: opacity 0.15s linear;\n     -moz-transition: opacity 0.15s linear;\n      -ms-transition: opacity 0.15s linear;\n       -o-transition: opacity 0.15s linear;\n          transition: opacity 0.15s linear;\n}\n\n.fade.in {\n  opacity: 1;\n}\n\n.collapse {\n  position: relative;\n  height: 0;\n  overflow: hidden;\n  -webkit-transition: height 0.35s ease;\n     -moz-transition: height 0.35s ease;\n      -ms-transition: height 0.35s ease;\n       -o-transition: height 0.35s ease;\n          transition: height 0.35s ease;\n}\n\n.collapse.in {\n  height: auto;\n}\n\n.close {\n  float: right;\n  font-size: 20px;\n  font-weight: bold;\n  line-height: 18px;\n  color: #000000;\n  text-shadow: 0 1px 0 #ffffff;\n  opacity: 0.2;\n  filter: alpha(opacity=20);\n}\n\n.close:hover {\n  color: #000000;\n  text-decoration: none;\n  cursor: pointer;\n  opacity: 0.4;\n  filter: alpha(opacity=40);\n}\n\nbutton.close {\n  padding: 0;\n  cursor: pointer;\n  background: transparent;\n  border: 0;\n  -webkit-appearance: none;\n}\n\n.btn {\n  display: inline-block;\n  *display: inline;\n  padding: 4px 10px 4px;\n  margin-bottom: 0;\n  *margin-left: .3em;\n  font-size: 13px;\n  line-height: 18px;\n  *line-height: 20px;\n  color: #333333;\n  text-align: center;\n  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);\n  vertical-align: middle;\n  cursor: pointer;\n  background-color: #f5f5f5;\n  *background-color: #e6e6e6;\n  background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));\n  background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);\n  background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);\n  background-image: linear-gradient(top, #ffffff, #e6e6e6);\n  background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);\n  background-repeat: repeat-x;\n  border: 1px solid #cccccc;\n  *border: 0;\n  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);\n  border-color: #e6e6e6 #e6e6e6 #bfbfbf;\n  border-bottom-color: #b3b3b3;\n  -webkit-border-radius: 4px;\n     -moz-border-radius: 4px;\n          border-radius: 4px;\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);\n  filter: progid:dximagetransform.microsoft.gradient(enabled=false);\n  *zoom: 1;\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);\n     -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);\n          box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n\n.btn:hover,\n.btn:active,\n.btn.active,\n.btn.disabled,\n.btn[disabled] {\n  background-color: #e6e6e6;\n  *background-color: #d9d9d9;\n}\n\n.btn:active,\n.btn.active {\n  background-color: #cccccc \\9;\n}\n\n.btn:first-child {\n  *margin-left: 0;\n}\n\n.btn:hover {\n  color: #333333;\n  text-decoration: none;\n  background-color: #e6e6e6;\n  *background-color: #d9d9d9;\n  /* Buttons in IE7 don't get borders, so darken on hover */\n\n  background-position: 0 -15px;\n  -webkit-transition: background-position 0.1s linear;\n     -moz-transition: background-position 0.1s linear;\n      -ms-transition: background-position 0.1s linear;\n       -o-transition: background-position 0.1s linear;\n          transition: background-position 0.1s linear;\n}\n\n.btn:focus {\n  outline: thin dotted #333;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n\n.btn.active,\n.btn:active {\n  background-color: #e6e6e6;\n  background-color: #d9d9d9 \\9;\n  background-image: none;\n  outline: 0;\n  -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);\n     -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);\n          box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n\n.btn.disabled,\n.btn[disabled] {\n  cursor: default;\n  background-color: #e6e6e6;\n  background-image: none;\n  opacity: 0.65;\n  filter: alpha(opacity=65);\n  -webkit-box-shadow: none;\n     -moz-box-shadow: none;\n          box-shadow: none;\n}\n\n.btn-large {\n  padding: 9px 14px;\n  font-size: 15px;\n  line-height: normal;\n  -webkit-border-radius: 5px;\n     -moz-border-radius: 5px;\n          border-radius: 5px;\n}\n\n.btn-large [class^=\"icon-\"] {\n  margin-top: 1px;\n}\n\n.btn-small {\n  padding: 5px 9px;\n  font-size: 11px;\n  line-height: 16px;\n}\n\n.btn-small [class^=\"icon-\"] {\n  margin-top: -1px;\n}\n\n.btn-mini {\n  padding: 2px 6px;\n  font-size: 11px;\n  line-height: 14px;\n}\n\n.btn-primary,\n.btn-primary:hover,\n.btn-warning,\n.btn-warning:hover,\n.btn-danger,\n.btn-danger:hover,\n.btn-success,\n.btn-success:hover,\n.btn-info,\n.btn-info:hover,\n.btn-inverse,\n.btn-inverse:hover {\n  color: #ffffff;\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n\n.btn-primary.active,\n.btn-warning.active,\n.btn-danger.active,\n.btn-success.active,\n.btn-info.active,\n.btn-inverse.active {\n  color: rgba(255, 255, 255, 0.75);\n}\n\n.btn {\n  border-color: #ccc;\n  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);\n}\n\n.btn-primary {\n  background-color: #0074cc;\n  *background-color: #0055cc;\n  background-image: -ms-linear-gradient(top, #0088cc, #0055cc);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0055cc));\n  background-image: -webkit-linear-gradient(top, #0088cc, #0055cc);\n  background-image: -o-linear-gradient(top, #0088cc, #0055cc);\n  background-image: -moz-linear-gradient(top, #0088cc, #0055cc);\n  background-image: linear-gradient(top, #0088cc, #0055cc);\n  background-repeat: repeat-x;\n  border-color: #0055cc #0055cc #003580;\n  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#0088cc', endColorstr='#0055cc', GradientType=0);\n  filter: progid:dximagetransform.microsoft.gradient(enabled=false);\n}\n\n.btn-primary:hover,\n.btn-primary:active,\n.btn-primary.active,\n.btn-primary.disabled,\n.btn-primary[disabled] {\n  background-color: #0055cc;\n  *background-color: #004ab3;\n}\n\n.btn-primary:active,\n.btn-primary.active {\n  background-color: #004099 \\9;\n}\n\n.btn-warning {\n  background-color: #faa732;\n  *background-color: #f89406;\n  background-image: -ms-linear-gradient(top, #fbb450, #f89406);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));\n  background-image: -webkit-linear-gradient(top, #fbb450, #f89406);\n  background-image: -o-linear-gradient(top, #fbb450, #f89406);\n  background-image: -moz-linear-gradient(top, #fbb450, #f89406);\n  background-image: linear-gradient(top, #fbb450, #f89406);\n  background-repeat: repeat-x;\n  border-color: #f89406 #f89406 #ad6704;\n  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);\n  filter: progid:dximagetransform.microsoft.gradient(enabled=false);\n}\n\n.btn-warning:hover,\n.btn-warning:active,\n.btn-warning.active,\n.btn-warning.disabled,\n.btn-warning[disabled] {\n  background-color: #f89406;\n  *background-color: #df8505;\n}\n\n.btn-warning:active,\n.btn-warning.active {\n  background-color: #c67605 \\9;\n}\n\n.btn-danger {\n  background-color: #da4f49;\n  *background-color: #bd362f;\n  background-image: -ms-linear-gradient(top, #ee5f5b, #bd362f);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));\n  background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f);\n  background-image: -o-linear-gradient(top, #ee5f5b, #bd362f);\n  background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f);\n  background-image: linear-gradient(top, #ee5f5b, #bd362f);\n  background-repeat: repeat-x;\n  border-color: #bd362f #bd362f #802420;\n  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);\n  filter: progid:dximagetransform.microsoft.gradient(enabled=false);\n}\n\n.btn-danger:hover,\n.btn-danger:active,\n.btn-danger.active,\n.btn-danger.disabled,\n.btn-danger[disabled] {\n  background-color: #bd362f;\n  *background-color: #a9302a;\n}\n\n.btn-danger:active,\n.btn-danger.active {\n  background-color: #942a25 \\9;\n}\n\n.btn-success {\n  background-color: #5bb75b;\n  *background-color: #51a351;\n  background-image: -ms-linear-gradient(top, #62c462, #51a351);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));\n  background-image: -webkit-linear-gradient(top, #62c462, #51a351);\n  background-image: -o-linear-gradient(top, #62c462, #51a351);\n  background-image: -moz-linear-gradient(top, #62c462, #51a351);\n  background-image: linear-gradient(top, #62c462, #51a351);\n  background-repeat: repeat-x;\n  border-color: #51a351 #51a351 #387038;\n  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);\n  filter: progid:dximagetransform.microsoft.gradient(enabled=false);\n}\n\n.btn-success:hover,\n.btn-success:active,\n.btn-success.active,\n.btn-success.disabled,\n.btn-success[disabled] {\n  background-color: #51a351;\n  *background-color: #499249;\n}\n\n.btn-success:active,\n.btn-success.active {\n  background-color: #408140 \\9;\n}\n\n.btn-info {\n  background-color: #49afcd;\n  *background-color: #2f96b4;\n  background-image: -ms-linear-gradient(top, #5bc0de, #2f96b4);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));\n  background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4);\n  background-image: -o-linear-gradient(top, #5bc0de, #2f96b4);\n  background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4);\n  background-image: linear-gradient(top, #5bc0de, #2f96b4);\n  background-repeat: repeat-x;\n  border-color: #2f96b4 #2f96b4 #1f6377;\n  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);\n  filter: progid:dximagetransform.microsoft.gradient(enabled=false);\n}\n\n.btn-info:hover,\n.btn-info:active,\n.btn-info.active,\n.btn-info.disabled,\n.btn-info[disabled] {\n  background-color: #2f96b4;\n  *background-color: #2a85a0;\n}\n\n.btn-info:active,\n.btn-info.active {\n  background-color: #24748c \\9;\n}\n\n.btn-inverse {\n  background-color: #414141;\n  *background-color: #222222;\n  background-image: -ms-linear-gradient(top, #555555, #222222);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#222222));\n  background-image: -webkit-linear-gradient(top, #555555, #222222);\n  background-image: -o-linear-gradient(top, #555555, #222222);\n  background-image: -moz-linear-gradient(top, #555555, #222222);\n  background-image: linear-gradient(top, #555555, #222222);\n  background-repeat: repeat-x;\n  border-color: #222222 #222222 #000000;\n  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0);\n  filter: progid:dximagetransform.microsoft.gradient(enabled=false);\n}\n\n.btn-inverse:hover,\n.btn-inverse:active,\n.btn-inverse.active,\n.btn-inverse.disabled,\n.btn-inverse[disabled] {\n  background-color: #222222;\n  *background-color: #151515;\n}\n\n.btn-inverse:active,\n.btn-inverse.active {\n  background-color: #080808 \\9;\n}\n\nbutton.btn,\ninput[type=\"submit\"].btn {\n  *padding-top: 2px;\n  *padding-bottom: 2px;\n}\n\nbutton.btn::-moz-focus-inner,\ninput[type=\"submit\"].btn::-moz-focus-inner {\n  padding: 0;\n  border: 0;\n}\n\nbutton.btn.btn-large,\ninput[type=\"submit\"].btn.btn-large {\n  *padding-top: 7px;\n  *padding-bottom: 7px;\n}\n\nbutton.btn.btn-small,\ninput[type=\"submit\"].btn.btn-small {\n  *padding-top: 3px;\n  *padding-bottom: 3px;\n}\n\nbutton.btn.btn-mini,\ninput[type=\"submit\"].btn.btn-mini {\n  *padding-top: 1px;\n  *padding-bottom: 1px;\n}\n\n.btn-group {\n  position: relative;\n  *margin-left: .3em;\n  *zoom: 1;\n}\n\n.btn-group:before,\n.btn-group:after {\n  display: table;\n  content: \"\";\n}\n\n.btn-group:after {\n  clear: both;\n}\n\n.btn-group:first-child {\n  *margin-left: 0;\n}\n\n.btn-group + .btn-group {\n  margin-left: 5px;\n}\n\n.btn-toolbar {\n  margin-top: 9px;\n  margin-bottom: 9px;\n}\n\n.btn-toolbar .btn-group {\n  display: inline-block;\n  *display: inline;\n  /* IE7 inline-block hack */\n\n  *zoom: 1;\n}\n\n.btn-group > .btn {\n  position: relative;\n  float: left;\n  margin-left: -1px;\n  -webkit-border-radius: 0;\n     -moz-border-radius: 0;\n          border-radius: 0;\n}\n\n.btn-group > .btn:first-child {\n  margin-left: 0;\n  -webkit-border-bottom-left-radius: 4px;\n          border-bottom-left-radius: 4px;\n  -webkit-border-top-left-radius: 4px;\n          border-top-left-radius: 4px;\n  -moz-border-radius-bottomleft: 4px;\n  -moz-border-radius-topleft: 4px;\n}\n\n.btn-group > .btn:last-child,\n.btn-group > .dropdown-toggle {\n  -webkit-border-top-right-radius: 4px;\n          border-top-right-radius: 4px;\n  -webkit-border-bottom-right-radius: 4px;\n          border-bottom-right-radius: 4px;\n  -moz-border-radius-topright: 4px;\n  -moz-border-radius-bottomright: 4px;\n}\n\n.btn-group > .btn.large:first-child {\n  margin-left: 0;\n  -webkit-border-bottom-left-radius: 6px;\n          border-bottom-left-radius: 6px;\n  -webkit-border-top-left-radius: 6px;\n          border-top-left-radius: 6px;\n  -moz-border-radius-bottomleft: 6px;\n  -moz-border-radius-topleft: 6px;\n}\n\n.btn-group > .btn.large:last-child,\n.btn-group > .large.dropdown-toggle {\n  -webkit-border-top-right-radius: 6px;\n          border-top-right-radius: 6px;\n  -webkit-border-bottom-right-radius: 6px;\n          border-bottom-right-radius: 6px;\n  -moz-border-radius-topright: 6px;\n  -moz-border-radius-bottomright: 6px;\n}\n\n.btn-group > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group > .btn:active,\n.btn-group > .btn.active {\n  z-index: 2;\n}\n\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n  outline: 0;\n}\n\n.btn-group > .dropdown-toggle {\n  *padding-top: 4px;\n  padding-right: 8px;\n  *padding-bottom: 4px;\n  padding-left: 8px;\n  -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);\n     -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);\n          box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n\n.btn-group > .btn-mini.dropdown-toggle {\n  padding-right: 5px;\n  padding-left: 5px;\n}\n\n.btn-group > .btn-small.dropdown-toggle {\n  *padding-top: 4px;\n  *padding-bottom: 4px;\n}\n\n.btn-group > .btn-large.dropdown-toggle {\n  padding-right: 12px;\n  padding-left: 12px;\n}\n\n.btn-group.open .dropdown-toggle {\n  background-image: none;\n  -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);\n     -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);\n          box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n\n.btn-group.open .btn.dropdown-toggle {\n  background-color: #e6e6e6;\n}\n\n.btn-group.open .btn-primary.dropdown-toggle {\n  background-color: #0055cc;\n}\n\n.btn-group.open .btn-warning.dropdown-toggle {\n  background-color: #f89406;\n}\n\n.btn-group.open .btn-danger.dropdown-toggle {\n  background-color: #bd362f;\n}\n\n.btn-group.open .btn-success.dropdown-toggle {\n  background-color: #51a351;\n}\n\n.btn-group.open .btn-info.dropdown-toggle {\n  background-color: #2f96b4;\n}\n\n.btn-group.open .btn-inverse.dropdown-toggle {\n  background-color: #222222;\n}\n\n.btn .caret {\n  margin-top: 7px;\n  margin-left: 0;\n}\n\n.btn:hover .caret,\n.open.btn-group .caret {\n  opacity: 1;\n  filter: alpha(opacity=100);\n}\n\n.btn-mini .caret {\n  margin-top: 5px;\n}\n\n.btn-small .caret {\n  margin-top: 6px;\n}\n\n.btn-large .caret {\n  margin-top: 6px;\n  border-top-width: 5px;\n  border-right-width: 5px;\n  border-left-width: 5px;\n}\n\n.dropup .btn-large .caret {\n  border-top: 0;\n  border-bottom: 5px solid #000000;\n}\n\n.btn-primary .caret,\n.btn-warning .caret,\n.btn-danger .caret,\n.btn-info .caret,\n.btn-success .caret,\n.btn-inverse .caret {\n  border-top-color: #ffffff;\n  border-bottom-color: #ffffff;\n  opacity: 0.75;\n  filter: alpha(opacity=75);\n}\n\n.alert {\n  padding: 8px 35px 8px 14px;\n  margin-bottom: 18px;\n  color: #c09853;\n  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);\n  background-color: #fcf8e3;\n  border: 1px solid #fbeed5;\n  -webkit-border-radius: 4px;\n     -moz-border-radius: 4px;\n          border-radius: 4px;\n}\n\n.alert-heading {\n  color: inherit;\n}\n\n.alert .close {\n  position: relative;\n  top: -2px;\n  right: -21px;\n  line-height: 18px;\n}\n\n.alert-success {\n  color: #468847;\n  background-color: #dff0d8;\n  border-color: #d6e9c6;\n}\n\n.alert-danger,\n.alert-error {\n  color: #b94a48;\n  background-color: #f2dede;\n  border-color: #eed3d7;\n}\n\n.alert-info {\n  color: #3a87ad;\n  background-color: #d9edf7;\n  border-color: #bce8f1;\n}\n\n.alert-block {\n  padding-top: 14px;\n  padding-bottom: 14px;\n}\n\n.alert-block > p,\n.alert-block > ul {\n  margin-bottom: 0;\n}\n\n.alert-block p + p {\n  margin-top: 5px;\n}\n\n.nav {\n  margin-bottom: 18px;\n  margin-left: 0;\n  list-style: none;\n}\n\n.nav > li > a {\n  display: block;\n}\n\n.nav > li > a:hover {\n  text-decoration: none;\n  background-color: #eeeeee;\n}\n\n.nav > .pull-right {\n  float: right;\n}\n\n.nav .nav-header {\n  display: block;\n  padding: 3px 15px;\n  font-size: 11px;\n  font-weight: bold;\n  line-height: 18px;\n  color: #999999;\n  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);\n  text-transform: uppercase;\n}\n\n.nav li + .nav-header {\n  margin-top: 9px;\n}\n\n.nav-list {\n  padding-right: 15px;\n  padding-left: 15px;\n  margin-bottom: 0;\n}\n\n.nav-list > li > a,\n.nav-list .nav-header {\n  margin-right: -15px;\n  margin-left: -15px;\n  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);\n}\n\n.nav-list > li > a {\n  padding: 3px 15px;\n}\n\n.nav-list > .active > a,\n.nav-list > .active > a:hover {\n  color: #ffffff;\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n  background-color: #0088cc;\n}\n\n.nav-list [class^=\"icon-\"] {\n  margin-right: 2px;\n}\n\n.nav-list .divider {\n  *width: 100%;\n  height: 1px;\n  margin: 8px 1px;\n  *margin: -5px 0 5px;\n  overflow: hidden;\n  background-color: #e5e5e5;\n  border-bottom: 1px solid #ffffff;\n}\n\n.nav-tabs,\n.nav-pills {\n  *zoom: 1;\n}\n\n.nav-tabs:before,\n.nav-pills:before,\n.nav-tabs:after,\n.nav-pills:after {\n  display: table;\n  content: \"\";\n}\n\n.nav-tabs:after,\n.nav-pills:after {\n  clear: both;\n}\n\n.nav-tabs > li,\n.nav-pills > li {\n  float: left;\n}\n\n.nav-tabs > li > a,\n.nav-pills > li > a {\n  padding-right: 12px;\n  padding-left: 12px;\n  margin-right: 2px;\n  line-height: 14px;\n}\n\n.nav-tabs {\n  border-bottom: 1px solid #ddd;\n}\n\n.nav-tabs > li {\n  margin-bottom: -1px;\n}\n\n.nav-tabs > li > a {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  line-height: 18px;\n  border: 1px solid transparent;\n  -webkit-border-radius: 4px 4px 0 0;\n     -moz-border-radius: 4px 4px 0 0;\n          border-radius: 4px 4px 0 0;\n}\n\n.nav-tabs > li > a:hover {\n  border-color: #eeeeee #eeeeee #dddddd;\n}\n\n.nav-tabs > .active > a,\n.nav-tabs > .active > a:hover {\n  color: #555555;\n  cursor: default;\n  background-color: #ffffff;\n  border: 1px solid #ddd;\n  border-bottom-color: transparent;\n}\n\n.nav-pills > li > a {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  margin-top: 2px;\n  margin-bottom: 2px;\n  -webkit-border-radius: 5px;\n     -moz-border-radius: 5px;\n          border-radius: 5px;\n}\n\n.nav-pills > .active > a,\n.nav-pills > .active > a:hover {\n  color: #ffffff;\n  background-color: #0088cc;\n}\n\n.nav-stacked > li {\n  float: none;\n}\n\n.nav-stacked > li > a {\n  margin-right: 0;\n}\n\n.nav-tabs.nav-stacked {\n  border-bottom: 0;\n}\n\n.nav-tabs.nav-stacked > li > a {\n  border: 1px solid #ddd;\n  -webkit-border-radius: 0;\n     -moz-border-radius: 0;\n          border-radius: 0;\n}\n\n.nav-tabs.nav-stacked > li:first-child > a {\n  -webkit-border-radius: 4px 4px 0 0;\n     -moz-border-radius: 4px 4px 0 0;\n          border-radius: 4px 4px 0 0;\n}\n\n.nav-tabs.nav-stacked > li:last-child > a {\n  -webkit-border-radius: 0 0 4px 4px;\n     -moz-border-radius: 0 0 4px 4px;\n          border-radius: 0 0 4px 4px;\n}\n\n.nav-tabs.nav-stacked > li > a:hover {\n  z-index: 2;\n  border-color: #ddd;\n}\n\n.nav-pills.nav-stacked > li > a {\n  margin-bottom: 3px;\n}\n\n.nav-pills.nav-stacked > li:last-child > a {\n  margin-bottom: 1px;\n}\n\n.nav-tabs .dropdown-menu {\n  -webkit-border-radius: 0 0 5px 5px;\n     -moz-border-radius: 0 0 5px 5px;\n          border-radius: 0 0 5px 5px;\n}\n\n.nav-pills .dropdown-menu {\n  -webkit-border-radius: 4px;\n     -moz-border-radius: 4px;\n          border-radius: 4px;\n}\n\n.nav-tabs .dropdown-toggle .caret,\n.nav-pills .dropdown-toggle .caret {\n  margin-top: 6px;\n  border-top-color: #0088cc;\n  border-bottom-color: #0088cc;\n}\n\n.nav-tabs .dropdown-toggle:hover .caret,\n.nav-pills .dropdown-toggle:hover .caret {\n  border-top-color: #005580;\n  border-bottom-color: #005580;\n}\n\n.nav-tabs .active .dropdown-toggle .caret,\n.nav-pills .active .dropdown-toggle .caret {\n  border-top-color: #333333;\n  border-bottom-color: #333333;\n}\n\n.nav > .dropdown.active > a:hover {\n  color: #000000;\n  cursor: pointer;\n}\n\n.nav-tabs .open .dropdown-toggle,\n.nav-pills .open .dropdown-toggle,\n.nav > li.dropdown.open.active > a:hover {\n  color: #ffffff;\n  background-color: #999999;\n  border-color: #999999;\n}\n\n.nav li.dropdown.open .caret,\n.nav li.dropdown.open.active .caret,\n.nav li.dropdown.open a:hover .caret {\n  border-top-color: #ffffff;\n  border-bottom-color: #ffffff;\n  opacity: 1;\n  filter: alpha(opacity=100);\n}\n\n.tabs-stacked .open > a:hover {\n  border-color: #999999;\n}\n\n.tabbable {\n  *zoom: 1;\n}\n\n.tabbable:before,\n.tabbable:after {\n  display: table;\n  content: \"\";\n}\n\n.tabbable:after {\n  clear: both;\n}\n\n.tab-content {\n  overflow: auto;\n}\n\n.tabs-below > .nav-tabs,\n.tabs-right > .nav-tabs,\n.tabs-left > .nav-tabs {\n  border-bottom: 0;\n}\n\n.tab-content > .tab-pane,\n.pill-content > .pill-pane {\n  display: none;\n}\n\n.tab-content > .active,\n.pill-content > .active {\n  display: block;\n}\n\n.tabs-below > .nav-tabs {\n  border-top: 1px solid #ddd;\n}\n\n.tabs-below > .nav-tabs > li {\n  margin-top: -1px;\n  margin-bottom: 0;\n}\n\n.tabs-below > .nav-tabs > li > a {\n  -webkit-border-radius: 0 0 4px 4px;\n     -moz-border-radius: 0 0 4px 4px;\n          border-radius: 0 0 4px 4px;\n}\n\n.tabs-below > .nav-tabs > li > a:hover {\n  border-top-color: #ddd;\n  border-bottom-color: transparent;\n}\n\n.tabs-below > .nav-tabs > .active > a,\n.tabs-below > .nav-tabs > .active > a:hover {\n  border-color: transparent #ddd #ddd #ddd;\n}\n\n.tabs-left > .nav-tabs > li,\n.tabs-right > .nav-tabs > li {\n  float: none;\n}\n\n.tabs-left > .nav-tabs > li > a,\n.tabs-right > .nav-tabs > li > a {\n  min-width: 74px;\n  margin-right: 0;\n  margin-bottom: 3px;\n}\n\n.tabs-left > .nav-tabs {\n  float: left;\n  margin-right: 19px;\n  border-right: 1px solid #ddd;\n}\n\n.tabs-left > .nav-tabs > li > a {\n  margin-right: -1px;\n  -webkit-border-radius: 4px 0 0 4px;\n     -moz-border-radius: 4px 0 0 4px;\n          border-radius: 4px 0 0 4px;\n}\n\n.tabs-left > .nav-tabs > li > a:hover {\n  border-color: #eeeeee #dddddd #eeeeee #eeeeee;\n}\n\n.tabs-left > .nav-tabs .active > a,\n.tabs-left > .nav-tabs .active > a:hover {\n  border-color: #ddd transparent #ddd #ddd;\n  *border-right-color: #ffffff;\n}\n\n.tabs-right > .nav-tabs {\n  float: right;\n  margin-left: 19px;\n  border-left: 1px solid #ddd;\n}\n\n.tabs-right > .nav-tabs > li > a {\n  margin-left: -1px;\n  -webkit-border-radius: 0 4px 4px 0;\n     -moz-border-radius: 0 4px 4px 0;\n          border-radius: 0 4px 4px 0;\n}\n\n.tabs-right > .nav-tabs > li > a:hover {\n  border-color: #eeeeee #eeeeee #eeeeee #dddddd;\n}\n\n.tabs-right > .nav-tabs .active > a,\n.tabs-right > .nav-tabs .active > a:hover {\n  border-color: #ddd #ddd #ddd transparent;\n  *border-left-color: #ffffff;\n}\n\n.navbar {\n  *position: relative;\n  *z-index: 2;\n  margin-bottom: 18px;\n  overflow: visible;\n}\n\n.navbar-inner {\n  min-height: 40px;\n  padding-right: 20px;\n  padding-left: 20px;\n  background-color: #2c2c2c;\n  background-image: -moz-linear-gradient(top, #333333, #222222);\n  background-image: -ms-linear-gradient(top, #333333, #222222);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));\n  background-image: -webkit-linear-gradient(top, #333333, #222222);\n  background-image: -o-linear-gradient(top, #333333, #222222);\n  background-image: linear-gradient(top, #333333, #222222);\n  background-repeat: repeat-x;\n  -webkit-border-radius: 4px;\n     -moz-border-radius: 4px;\n          border-radius: 4px;\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);\n  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1);\n     -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1);\n          box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1);\n}\n\n.navbar .container {\n  width: auto;\n}\n\n.nav-collapse.collapse {\n  height: auto;\n}\n\n.navbar {\n  color: #999999;\n}\n\n.navbar .brand:hover {\n  text-decoration: none;\n}\n\n.navbar .brand {\n  display: block;\n  float: left;\n  padding: 8px 20px 12px;\n  margin-left: -20px;\n  font-size: 20px;\n  font-weight: 200;\n  line-height: 1;\n  color: #999999;\n}\n\n.navbar .navbar-text {\n  margin-bottom: 0;\n  line-height: 40px;\n}\n\n.navbar .navbar-link {\n  color: #999999;\n}\n\n.navbar .navbar-link:hover {\n  color: #ffffff;\n}\n\n.navbar .btn,\n.navbar .btn-group {\n  margin-top: 5px;\n}\n\n.navbar .btn-group .btn {\n  margin: 0;\n}\n\n.navbar-form {\n  margin-bottom: 0;\n  *zoom: 1;\n}\n\n.navbar-form:before,\n.navbar-form:after {\n  display: table;\n  content: \"\";\n}\n\n.navbar-form:after {\n  clear: both;\n}\n\n.navbar-form input,\n.navbar-form select,\n.navbar-form .radio,\n.navbar-form .checkbox {\n  margin-top: 5px;\n}\n\n.navbar-form input,\n.navbar-form select {\n  display: inline-block;\n  margin-bottom: 0;\n}\n\n.navbar-form input[type=\"image\"],\n.navbar-form input[type=\"checkbox\"],\n.navbar-form input[type=\"radio\"] {\n  margin-top: 3px;\n}\n\n.navbar-form .input-append,\n.navbar-form .input-prepend {\n  margin-top: 6px;\n  white-space: nowrap;\n}\n\n.navbar-form .input-append input,\n.navbar-form .input-prepend input {\n  margin-top: 0;\n}\n\n.navbar-search {\n  position: relative;\n  float: left;\n  margin-top: 6px;\n  margin-bottom: 0;\n}\n\n.navbar-search .search-query {\n  padding: 4px 9px;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 13px;\n  font-weight: normal;\n  line-height: 1;\n  color: #ffffff;\n  background-color: #626262;\n  border: 1px solid #151515;\n  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);\n     -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);\n          box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);\n  -webkit-transition: none;\n     -moz-transition: none;\n      -ms-transition: none;\n       -o-transition: none;\n          transition: none;\n}\n\n.navbar-search .search-query:-moz-placeholder {\n  color: #cccccc;\n}\n\n.navbar-search .search-query:-ms-input-placeholder {\n  color: #cccccc;\n}\n\n.navbar-search .search-query::-webkit-input-placeholder {\n  color: #cccccc;\n}\n\n.navbar-search .search-query:focus,\n.navbar-search .search-query.focused {\n  padding: 5px 10px;\n  color: #333333;\n  text-shadow: 0 1px 0 #ffffff;\n  background-color: #ffffff;\n  border: 0;\n  outline: 0;\n  -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);\n     -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);\n          box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);\n}\n\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  position: fixed;\n  right: 0;\n  left: 0;\n  z-index: 1030;\n  margin-bottom: 0;\n}\n\n.navbar-fixed-top .navbar-inner,\n.navbar-fixed-bottom .navbar-inner {\n  padding-right: 0;\n  padding-left: 0;\n  -webkit-border-radius: 0;\n     -moz-border-radius: 0;\n          border-radius: 0;\n}\n\n.navbar-fixed-top .container,\n.navbar-fixed-bottom .container {\n  width: 940px;\n}\n\n.navbar-fixed-top {\n  top: 0;\n}\n\n.navbar-fixed-bottom {\n  bottom: 0;\n}\n\n.navbar .nav {\n  position: relative;\n  left: 0;\n  display: block;\n  float: left;\n  margin: 0 10px 0 0;\n}\n\n.navbar .nav.pull-right {\n  float: right;\n}\n\n.navbar .nav > li {\n  display: block;\n  float: left;\n}\n\n.navbar .nav > li > a {\n  float: none;\n  padding: 9px 10px 11px;\n  line-height: 19px;\n  color: #999999;\n  text-decoration: none;\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n\n.navbar .btn {\n  display: inline-block;\n  padding: 4px 10px 4px;\n  margin: 5px 5px 6px;\n  line-height: 18px;\n}\n\n.navbar .btn-group {\n  padding: 5px 5px 6px;\n  margin: 0;\n}\n\n.navbar .nav > li > a:hover {\n  color: #ffffff;\n  text-decoration: none;\n  background-color: transparent;\n}\n\n.navbar .nav .active > a,\n.navbar .nav .active > a:hover {\n  color: #ffffff;\n  text-decoration: none;\n  background-color: #222222;\n}\n\n.navbar .divider-vertical {\n  width: 1px;\n  height: 40px;\n  margin: 0 9px;\n  overflow: hidden;\n  background-color: #222222;\n  border-right: 1px solid #333333;\n}\n\n.navbar .nav.pull-right {\n  margin-right: 0;\n  margin-left: 10px;\n}\n\n.navbar .btn-navbar {\n  display: none;\n  float: right;\n  padding: 7px 10px;\n  margin-right: 5px;\n  margin-left: 5px;\n  background-color: #2c2c2c;\n  *background-color: #222222;\n  background-image: -ms-linear-gradient(top, #333333, #222222);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));\n  background-image: -webkit-linear-gradient(top, #333333, #222222);\n  background-image: -o-linear-gradient(top, #333333, #222222);\n  background-image: linear-gradient(top, #333333, #222222);\n  background-image: -moz-linear-gradient(top, #333333, #222222);\n  background-repeat: repeat-x;\n  border-color: #222222 #222222 #000000;\n  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);\n  filter: progid:dximagetransform.microsoft.gradient(enabled=false);\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);\n     -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);\n          box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);\n}\n\n.navbar .btn-navbar:hover,\n.navbar .btn-navbar:active,\n.navbar .btn-navbar.active,\n.navbar .btn-navbar.disabled,\n.navbar .btn-navbar[disabled] {\n  background-color: #222222;\n  *background-color: #151515;\n}\n\n.navbar .btn-navbar:active,\n.navbar .btn-navbar.active {\n  background-color: #080808 \\9;\n}\n\n.navbar .btn-navbar .icon-bar {\n  display: block;\n  width: 18px;\n  height: 2px;\n  background-color: #f5f5f5;\n  -webkit-border-radius: 1px;\n     -moz-border-radius: 1px;\n          border-radius: 1px;\n  -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);\n     -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);\n          box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);\n}\n\n.btn-navbar .icon-bar + .icon-bar {\n  margin-top: 3px;\n}\n\n.navbar .dropdown-menu:before {\n  position: absolute;\n  top: -7px;\n  left: 9px;\n  display: inline-block;\n  border-right: 7px solid transparent;\n  border-bottom: 7px solid #ccc;\n  border-left: 7px solid transparent;\n  border-bottom-color: rgba(0, 0, 0, 0.2);\n  content: '';\n}\n\n.navbar .dropdown-menu:after {\n  position: absolute;\n  top: -6px;\n  left: 10px;\n  display: inline-block;\n  border-right: 6px solid transparent;\n  border-bottom: 6px solid #ffffff;\n  border-left: 6px solid transparent;\n  content: '';\n}\n\n.navbar-fixed-bottom .dropdown-menu:before {\n  top: auto;\n  bottom: -7px;\n  border-top: 7px solid #ccc;\n  border-bottom: 0;\n  border-top-color: rgba(0, 0, 0, 0.2);\n}\n\n.navbar-fixed-bottom .dropdown-menu:after {\n  top: auto;\n  bottom: -6px;\n  border-top: 6px solid #ffffff;\n  border-bottom: 0;\n}\n\n.navbar .nav li.dropdown .dropdown-toggle .caret,\n.navbar .nav li.dropdown.open .caret {\n  border-top-color: #ffffff;\n  border-bottom-color: #ffffff;\n}\n\n.navbar .nav li.dropdown.active .caret {\n  opacity: 1;\n  filter: alpha(opacity=100);\n}\n\n.navbar .nav li.dropdown.open > .dropdown-toggle,\n.navbar .nav li.dropdown.active > .dropdown-toggle,\n.navbar .nav li.dropdown.open.active > .dropdown-toggle {\n  background-color: transparent;\n}\n\n.navbar .nav li.dropdown.active > .dropdown-toggle:hover {\n  color: #ffffff;\n}\n\n.navbar .pull-right .dropdown-menu,\n.navbar .dropdown-menu.pull-right {\n  right: 0;\n  left: auto;\n}\n\n.navbar .pull-right .dropdown-menu:before,\n.navbar .dropdown-menu.pull-right:before {\n  right: 12px;\n  left: auto;\n}\n\n.navbar .pull-right .dropdown-menu:after,\n.navbar .dropdown-menu.pull-right:after {\n  right: 13px;\n  left: auto;\n}\n\n.breadcrumb {\n  padding: 7px 14px;\n  margin: 0 0 18px;\n  list-style: none;\n  background-color: #fbfbfb;\n  background-image: -moz-linear-gradient(top, #ffffff, #f5f5f5);\n  background-image: -ms-linear-gradient(top, #ffffff, #f5f5f5);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5));\n  background-image: -webkit-linear-gradient(top, #ffffff, #f5f5f5);\n  background-image: -o-linear-gradient(top, #ffffff, #f5f5f5);\n  background-image: linear-gradient(top, #ffffff, #f5f5f5);\n  background-repeat: repeat-x;\n  border: 1px solid #ddd;\n  -webkit-border-radius: 3px;\n     -moz-border-radius: 3px;\n          border-radius: 3px;\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);\n  -webkit-box-shadow: inset 0 1px 0 #ffffff;\n     -moz-box-shadow: inset 0 1px 0 #ffffff;\n          box-shadow: inset 0 1px 0 #ffffff;\n}\n\n.breadcrumb li {\n  display: inline-block;\n  *display: inline;\n  text-shadow: 0 1px 0 #ffffff;\n  *zoom: 1;\n}\n\n.breadcrumb .divider {\n  padding: 0 5px;\n  color: #999999;\n}\n\n.breadcrumb .active a {\n  color: #333333;\n}\n\n.pagination {\n  height: 36px;\n  margin: 18px 0;\n}\n\n.pagination ul {\n  display: inline-block;\n  *display: inline;\n  margin-bottom: 0;\n  margin-left: 0;\n  -webkit-border-radius: 3px;\n     -moz-border-radius: 3px;\n          border-radius: 3px;\n  *zoom: 1;\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n     -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n          box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n\n.pagination li {\n  display: inline;\n}\n\n.pagination a {\n  float: left;\n  padding: 0 14px;\n  line-height: 34px;\n  text-decoration: none;\n  border: 1px solid #ddd;\n  border-left-width: 0;\n}\n\n.pagination a:hover,\n.pagination .active a {\n  background-color: #f5f5f5;\n}\n\n.pagination .active a {\n  color: #999999;\n  cursor: default;\n}\n\n.pagination .disabled span,\n.pagination .disabled a,\n.pagination .disabled a:hover {\n  color: #999999;\n  cursor: default;\n  background-color: transparent;\n}\n\n.pagination li:first-child a {\n  border-left-width: 1px;\n  -webkit-border-radius: 3px 0 0 3px;\n     -moz-border-radius: 3px 0 0 3px;\n          border-radius: 3px 0 0 3px;\n}\n\n.pagination li:last-child a {\n  -webkit-border-radius: 0 3px 3px 0;\n     -moz-border-radius: 0 3px 3px 0;\n          border-radius: 0 3px 3px 0;\n}\n\n.pagination-centered {\n  text-align: center;\n}\n\n.pagination-right {\n  text-align: right;\n}\n\n.pager {\n  margin-bottom: 18px;\n  margin-left: 0;\n  text-align: center;\n  list-style: none;\n  *zoom: 1;\n}\n\n.pager:before,\n.pager:after {\n  display: table;\n  content: \"\";\n}\n\n.pager:after {\n  clear: both;\n}\n\n.pager li {\n  display: inline;\n}\n\n.pager a {\n  display: inline-block;\n  padding: 5px 14px;\n  background-color: #fff;\n  border: 1px solid #ddd;\n  -webkit-border-radius: 15px;\n     -moz-border-radius: 15px;\n          border-radius: 15px;\n}\n\n.pager a:hover {\n  text-decoration: none;\n  background-color: #f5f5f5;\n}\n\n.pager .next a {\n  float: right;\n}\n\n.pager .previous a {\n  float: left;\n}\n\n.pager .disabled a,\n.pager .disabled a:hover {\n  color: #999999;\n  cursor: default;\n  background-color: #fff;\n}\n\n.modal-open .dropdown-menu {\n  z-index: 2050;\n}\n\n.modal-open .dropdown.open {\n  *z-index: 2050;\n}\n\n.modal-open .popover {\n  z-index: 2060;\n}\n\n.modal-open .tooltip {\n  z-index: 2070;\n}\n\n.modal-backdrop {\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1040;\n  background-color: #000000;\n}\n\n.modal-backdrop.fade {\n  opacity: 0;\n}\n\n.modal-backdrop,\n.modal-backdrop.fade.in {\n  opacity: 0.8;\n  filter: alpha(opacity=80);\n}\n\n.modal {\n  position: fixed;\n  top: 50%;\n  left: 50%;\n  z-index: 1050;\n  width: 560px;\n  margin: -250px 0 0 -280px;\n  overflow: auto;\n  background-color: #ffffff;\n  border: 1px solid #999;\n  border: 1px solid rgba(0, 0, 0, 0.3);\n  *border: 1px solid #999;\n  -webkit-border-radius: 6px;\n     -moz-border-radius: 6px;\n          border-radius: 6px;\n  -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);\n     -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);\n          box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);\n  -webkit-background-clip: padding-box;\n     -moz-background-clip: padding-box;\n          background-clip: padding-box;\n}\n\n.modal.fade {\n  top: -25%;\n  -webkit-transition: opacity 0.3s linear, top 0.3s ease-out;\n     -moz-transition: opacity 0.3s linear, top 0.3s ease-out;\n      -ms-transition: opacity 0.3s linear, top 0.3s ease-out;\n       -o-transition: opacity 0.3s linear, top 0.3s ease-out;\n          transition: opacity 0.3s linear, top 0.3s ease-out;\n}\n\n.modal.fade.in {\n  top: 50%;\n}\n\n.modal-header {\n  padding: 9px 15px;\n  border-bottom: 1px solid #eee;\n}\n\n.modal-header .close {\n  margin-top: 2px;\n}\n\n.modal-body {\n  max-height: 400px;\n  padding: 15px;\n  overflow-y: auto;\n}\n\n.modal-form {\n  margin-bottom: 0;\n}\n\n.modal-footer {\n  padding: 14px 15px 15px;\n  margin-bottom: 0;\n  text-align: right;\n  background-color: #f5f5f5;\n  border-top: 1px solid #ddd;\n  -webkit-border-radius: 0 0 6px 6px;\n     -moz-border-radius: 0 0 6px 6px;\n          border-radius: 0 0 6px 6px;\n  *zoom: 1;\n  -webkit-box-shadow: inset 0 1px 0 #ffffff;\n     -moz-box-shadow: inset 0 1px 0 #ffffff;\n          box-shadow: inset 0 1px 0 #ffffff;\n}\n\n.modal-footer:before,\n.modal-footer:after {\n  display: table;\n  content: \"\";\n}\n\n.modal-footer:after {\n  clear: both;\n}\n\n.modal-footer .btn + .btn {\n  margin-bottom: 0;\n  margin-left: 5px;\n}\n\n.modal-footer .btn-group .btn + .btn {\n  margin-left: -1px;\n}\n\n.tooltip {\n  position: absolute;\n  z-index: 1020;\n  display: block;\n  padding: 5px;\n  font-size: 11px;\n  opacity: 0;\n  filter: alpha(opacity=0);\n  visibility: visible;\n}\n\n.tooltip.in {\n  opacity: 0.8;\n  filter: alpha(opacity=80);\n}\n\n.tooltip.top {\n  margin-top: -2px;\n}\n\n.tooltip.right {\n  margin-left: 2px;\n}\n\n.tooltip.bottom {\n  margin-top: 2px;\n}\n\n.tooltip.left {\n  margin-left: -2px;\n}\n\n.tooltip.top .tooltip-arrow {\n  bottom: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-top: 5px solid #000000;\n  border-right: 5px solid transparent;\n  border-left: 5px solid transparent;\n}\n\n.tooltip.left .tooltip-arrow {\n  top: 50%;\n  right: 0;\n  margin-top: -5px;\n  border-top: 5px solid transparent;\n  border-bottom: 5px solid transparent;\n  border-left: 5px solid #000000;\n}\n\n.tooltip.bottom .tooltip-arrow {\n  top: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-right: 5px solid transparent;\n  border-bottom: 5px solid #000000;\n  border-left: 5px solid transparent;\n}\n\n.tooltip.right .tooltip-arrow {\n  top: 50%;\n  left: 0;\n  margin-top: -5px;\n  border-top: 5px solid transparent;\n  border-right: 5px solid #000000;\n  border-bottom: 5px solid transparent;\n}\n\n.tooltip-inner {\n  max-width: 200px;\n  padding: 3px 8px;\n  color: #ffffff;\n  text-align: center;\n  text-decoration: none;\n  background-color: #000000;\n  -webkit-border-radius: 4px;\n     -moz-border-radius: 4px;\n          border-radius: 4px;\n}\n\n.tooltip-arrow {\n  position: absolute;\n  width: 0;\n  height: 0;\n}\n\n.popover {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: 1010;\n  display: none;\n  padding: 5px;\n}\n\n.popover.top {\n  margin-top: -5px;\n}\n\n.popover.right {\n  margin-left: 5px;\n}\n\n.popover.bottom {\n  margin-top: 5px;\n}\n\n.popover.left {\n  margin-left: -5px;\n}\n\n.popover.top .arrow {\n  bottom: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-top: 5px solid #000000;\n  border-right: 5px solid transparent;\n  border-left: 5px solid transparent;\n}\n\n.popover.right .arrow {\n  top: 50%;\n  left: 0;\n  margin-top: -5px;\n  border-top: 5px solid transparent;\n  border-right: 5px solid #000000;\n  border-bottom: 5px solid transparent;\n}\n\n.popover.bottom .arrow {\n  top: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-right: 5px solid transparent;\n  border-bottom: 5px solid #000000;\n  border-left: 5px solid transparent;\n}\n\n.popover.left .arrow {\n  top: 50%;\n  right: 0;\n  margin-top: -5px;\n  border-top: 5px solid transparent;\n  border-bottom: 5px solid transparent;\n  border-left: 5px solid #000000;\n}\n\n.popover .arrow {\n  position: absolute;\n  width: 0;\n  height: 0;\n}\n\n.popover-inner {\n  width: 280px;\n  padding: 3px;\n  overflow: hidden;\n  background: #000000;\n  background: rgba(0, 0, 0, 0.8);\n  -webkit-border-radius: 6px;\n     -moz-border-radius: 6px;\n          border-radius: 6px;\n  -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);\n     -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);\n          box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);\n}\n\n.popover-title {\n  padding: 9px 15px;\n  line-height: 1;\n  background-color: #f5f5f5;\n  border-bottom: 1px solid #eee;\n  -webkit-border-radius: 3px 3px 0 0;\n     -moz-border-radius: 3px 3px 0 0;\n          border-radius: 3px 3px 0 0;\n}\n\n.popover-content {\n  padding: 14px;\n  background-color: #ffffff;\n  -webkit-border-radius: 0 0 3px 3px;\n     -moz-border-radius: 0 0 3px 3px;\n          border-radius: 0 0 3px 3px;\n  -webkit-background-clip: padding-box;\n     -moz-background-clip: padding-box;\n          background-clip: padding-box;\n}\n\n.popover-content p,\n.popover-content ul,\n.popover-content ol {\n  margin-bottom: 0;\n}\n\n.thumbnails {\n  margin-left: -20px;\n  list-style: none;\n  *zoom: 1;\n}\n\n.thumbnails:before,\n.thumbnails:after {\n  display: table;\n  content: \"\";\n}\n\n.thumbnails:after {\n  clear: both;\n}\n\n.row-fluid .thumbnails {\n  margin-left: 0;\n}\n\n.thumbnails > li {\n  float: left;\n  margin-bottom: 18px;\n  margin-left: 20px;\n}\n\n.thumbnail {\n  display: block;\n  padding: 4px;\n  line-height: 1;\n  border: 1px solid #ddd;\n  -webkit-border-radius: 4px;\n     -moz-border-radius: 4px;\n          border-radius: 4px;\n  -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075);\n     -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075);\n          box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n\na.thumbnail:hover {\n  border-color: #0088cc;\n  -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);\n     -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);\n          box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);\n}\n\n.thumbnail > img {\n  display: block;\n  max-width: 100%;\n  margin-right: auto;\n  margin-left: auto;\n}\n\n.thumbnail .caption {\n  padding: 9px;\n}\n\n.label,\n.badge {\n  font-size: 10.998px;\n  font-weight: bold;\n  line-height: 14px;\n  color: #ffffff;\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n  white-space: nowrap;\n  vertical-align: baseline;\n  background-color: #999999;\n}\n\n.label {\n  padding: 1px 4px 2px;\n  -webkit-border-radius: 3px;\n     -moz-border-radius: 3px;\n          border-radius: 3px;\n}\n\n.badge {\n  padding: 1px 9px 2px;\n  -webkit-border-radius: 9px;\n     -moz-border-radius: 9px;\n          border-radius: 9px;\n}\n\na.label:hover,\na.badge:hover {\n  color: #ffffff;\n  text-decoration: none;\n  cursor: pointer;\n}\n\n.label-important,\n.badge-important {\n  background-color: #b94a48;\n}\n\n.label-important[href],\n.badge-important[href] {\n  background-color: #953b39;\n}\n\n.label-warning,\n.badge-warning {\n  background-color: #f89406;\n}\n\n.label-warning[href],\n.badge-warning[href] {\n  background-color: #c67605;\n}\n\n.label-success,\n.badge-success {\n  background-color: #468847;\n}\n\n.label-success[href],\n.badge-success[href] {\n  background-color: #356635;\n}\n\n.label-info,\n.badge-info {\n  background-color: #3a87ad;\n}\n\n.label-info[href],\n.badge-info[href] {\n  background-color: #2d6987;\n}\n\n.label-inverse,\n.badge-inverse {\n  background-color: #333333;\n}\n\n.label-inverse[href],\n.badge-inverse[href] {\n  background-color: #1a1a1a;\n}\n\n@-webkit-keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n\n@-moz-keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n\n@-ms-keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n\n@-o-keyframes progress-bar-stripes {\n  from {\n    background-position: 0 0;\n  }\n  to {\n    background-position: 40px 0;\n  }\n}\n\n@keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n\n.progress {\n  height: 18px;\n  margin-bottom: 18px;\n  overflow: hidden;\n  background-color: #f7f7f7;\n  background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);\n  background-image: -ms-linear-gradient(top, #f5f5f5, #f9f9f9);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));\n  background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);\n  background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);\n  background-image: linear-gradient(top, #f5f5f5, #f9f9f9);\n  background-repeat: repeat-x;\n  -webkit-border-radius: 4px;\n     -moz-border-radius: 4px;\n          border-radius: 4px;\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0);\n  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n     -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n          box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n\n.progress .bar {\n  width: 0;\n  height: 18px;\n  font-size: 12px;\n  color: #ffffff;\n  text-align: center;\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n  background-color: #0e90d2;\n  background-image: -moz-linear-gradient(top, #149bdf, #0480be);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));\n  background-image: -webkit-linear-gradient(top, #149bdf, #0480be);\n  background-image: -o-linear-gradient(top, #149bdf, #0480be);\n  background-image: linear-gradient(top, #149bdf, #0480be);\n  background-image: -ms-linear-gradient(top, #149bdf, #0480be);\n  background-repeat: repeat-x;\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0);\n  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n     -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n      -ms-box-sizing: border-box;\n          box-sizing: border-box;\n  -webkit-transition: width 0.6s ease;\n     -moz-transition: width 0.6s ease;\n      -ms-transition: width 0.6s ease;\n       -o-transition: width 0.6s ease;\n          transition: width 0.6s ease;\n}\n\n.progress-striped .bar {\n  background-color: #149bdf;\n  background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));\n  background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  -webkit-background-size: 40px 40px;\n     -moz-background-size: 40px 40px;\n       -o-background-size: 40px 40px;\n          background-size: 40px 40px;\n}\n\n.progress.active .bar {\n  -webkit-animation: progress-bar-stripes 2s linear infinite;\n     -moz-animation: progress-bar-stripes 2s linear infinite;\n      -ms-animation: progress-bar-stripes 2s linear infinite;\n       -o-animation: progress-bar-stripes 2s linear infinite;\n          animation: progress-bar-stripes 2s linear infinite;\n}\n\n.progress-danger .bar {\n  background-color: #dd514c;\n  background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);\n  background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));\n  background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);\n  background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);\n  background-image: linear-gradient(top, #ee5f5b, #c43c35);\n  background-repeat: repeat-x;\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);\n}\n\n.progress-danger.progress-striped .bar {\n  background-color: #ee5f5b;\n  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));\n  background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n\n.progress-success .bar {\n  background-color: #5eb95e;\n  background-image: -moz-linear-gradient(top, #62c462, #57a957);\n  background-image: -ms-linear-gradient(top, #62c462, #57a957);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));\n  background-image: -webkit-linear-gradient(top, #62c462, #57a957);\n  background-image: -o-linear-gradient(top, #62c462, #57a957);\n  background-image: linear-gradient(top, #62c462, #57a957);\n  background-repeat: repeat-x;\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);\n}\n\n.progress-success.progress-striped .bar {\n  background-color: #62c462;\n  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));\n  background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n\n.progress-info .bar {\n  background-color: #4bb1cf;\n  background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);\n  background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));\n  background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);\n  background-image: -o-linear-gradient(top, #5bc0de, #339bb9);\n  background-image: linear-gradient(top, #5bc0de, #339bb9);\n  background-repeat: repeat-x;\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);\n}\n\n.progress-info.progress-striped .bar {\n  background-color: #5bc0de;\n  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));\n  background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n\n.progress-warning .bar {\n  background-color: #faa732;\n  background-image: -moz-linear-gradient(top, #fbb450, #f89406);\n  background-image: -ms-linear-gradient(top, #fbb450, #f89406);\n  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));\n  background-image: -webkit-linear-gradient(top, #fbb450, #f89406);\n  background-image: -o-linear-gradient(top, #fbb450, #f89406);\n  background-image: linear-gradient(top, #fbb450, #f89406);\n  background-repeat: repeat-x;\n  filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);\n}\n\n.progress-warning.progress-striped .bar {\n  background-color: #fbb450;\n  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));\n  background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n\n.accordion {\n  margin-bottom: 18px;\n}\n\n.accordion-group {\n  margin-bottom: 2px;\n  border: 1px solid #e5e5e5;\n  -webkit-border-radius: 4px;\n     -moz-border-radius: 4px;\n          border-radius: 4px;\n}\n\n.accordion-heading {\n  border-bottom: 0;\n}\n\n.accordion-heading .accordion-toggle {\n  display: block;\n  padding: 8px 15px;\n}\n\n.accordion-toggle {\n  cursor: pointer;\n}\n\n.accordion-inner {\n  padding: 9px 15px;\n  border-top: 1px solid #e5e5e5;\n}\n\n.carousel {\n  position: relative;\n  margin-bottom: 18px;\n  line-height: 1;\n}\n\n.carousel-inner {\n  position: relative;\n  width: 100%;\n  overflow: hidden;\n}\n\n.carousel .item {\n  position: relative;\n  display: none;\n  -webkit-transition: 0.6s ease-in-out left;\n     -moz-transition: 0.6s ease-in-out left;\n      -ms-transition: 0.6s ease-in-out left;\n       -o-transition: 0.6s ease-in-out left;\n          transition: 0.6s ease-in-out left;\n}\n\n.carousel .item > img {\n  display: block;\n  line-height: 1;\n}\n\n.carousel .active,\n.carousel .next,\n.carousel .prev {\n  display: block;\n}\n\n.carousel .active {\n  left: 0;\n}\n\n.carousel .next,\n.carousel .prev {\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n\n.carousel .next {\n  left: 100%;\n}\n\n.carousel .prev {\n  left: -100%;\n}\n\n.carousel .next.left,\n.carousel .prev.right {\n  left: 0;\n}\n\n.carousel .active.left {\n  left: -100%;\n}\n\n.carousel .active.right {\n  left: 100%;\n}\n\n.carousel-control {\n  position: absolute;\n  top: 40%;\n  left: 15px;\n  width: 40px;\n  height: 40px;\n  margin-top: -20px;\n  font-size: 60px;\n  font-weight: 100;\n  line-height: 30px;\n  color: #ffffff;\n  text-align: center;\n  background: #222222;\n  border: 3px solid #ffffff;\n  -webkit-border-radius: 23px;\n     -moz-border-radius: 23px;\n          border-radius: 23px;\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n}\n\n.carousel-control.right {\n  right: 15px;\n  left: auto;\n}\n\n.carousel-control:hover {\n  color: #ffffff;\n  text-decoration: none;\n  opacity: 0.9;\n  filter: alpha(opacity=90);\n}\n\n.carousel-caption {\n  position: absolute;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  padding: 10px 15px 5px;\n  background: #333333;\n  background: rgba(0, 0, 0, 0.75);\n}\n\n.carousel-caption h4,\n.carousel-caption p {\n  color: #ffffff;\n}\n\n.hero-unit {\n  padding: 60px;\n  margin-bottom: 30px;\n  background-color: #eeeeee;\n  -webkit-border-radius: 6px;\n     -moz-border-radius: 6px;\n          border-radius: 6px;\n}\n\n.hero-unit h1 {\n  margin-bottom: 0;\n  font-size: 60px;\n  line-height: 1;\n  letter-spacing: -1px;\n  color: inherit;\n}\n\n.hero-unit p {\n  font-size: 18px;\n  font-weight: 200;\n  line-height: 27px;\n  color: inherit;\n}\n\n.pull-right {\n  float: right;\n}\n\n.pull-left {\n  float: left;\n}\n\n.hide {\n  display: none;\n}\n\n.show {\n  display: block;\n}\n\n.invisible {\n  visibility: hidden;\n}\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/static/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <title>spring-boot-demo-websocket-socketio</title>\n    <link href=\"bootstrap.css\" rel=\"stylesheet\">\n    <link href=\"https://cdn.bootcss.com/layer/2.3/skin/layer.css\" rel=\"stylesheet\">\n    <style>\n        body {\n            padding: 20px;\n        }\n\n        #console {\n            height: 400px;\n            overflow: auto;\n        }\n\n        .username-msg {\n            color: orange;\n        }\n\n        .connect-msg {\n            color: green;\n        }\n\n        .disconnect-msg {\n            color: red;\n        }\n\n        .broadcast {\n            color: red;\n        }\n\n        .send-msg {\n            color: #888\n        }\n\n        .sys-msg {\n            color: #888\n        }\n\n    </style>\n\n    <script src=\"js/socket.io/socket.io.js\"></script>\n    <script src=\"js/moment.min.js\"></script>\n    <script src=\"js/jquery-1.10.1.min.js\"></script>\n    <script src=\"https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js\"></script>\n    <script src=\"https://cdn.bootcss.com/layer/2.3/layer.js\"></script>\n    <script>\n        // const token = 'user' + Math.floor((Math.random() * 1000) + 1);\n        const token = '252276744';\n        const url = `https://www.xiaoyuan2.cn:567?userId=252276744`; //http://81.68.131.40:9394\n        const socket = io.connect(url);\n        socket.on('connect', function () {\n            output(`<span class=\"connect-msg\">系统通知: ${token}成功连接至websocket服务器</span>`);\n        });\n\n        socket.on('join', function (data) {\n            output(`<span class=\"sys-msg\">${data.groupId} 群通知: 新人 ${data.userId} 请爆照</span>`);\n        });\n\n        socket.on('chat', function (data) {\n            output(`<span class=\"username-msg\">系统通知: 收到来自 ${data.fromUid} 的悄悄话: ${data.message}</span>`);\n        });\n\n        socket.on('group', function (data) {\n            output(`<span class=\"username-msg\">${data.groupId} 群消息: ${data.fromUid} 说: ${data.message}</span>`);\n        });\n\n        socket.on('disconnect', function () {\n            output(`<span class=\"disconnect-msg\">系统通知: ${token}已从websocket服务器断开连接</span>`);\n        });\n\n        socket.on('broadcast', function (data) {\n            output(`<span class=\"broadcast\">${data.message}</span>`);\n        });\n\n        function sendConnect() {\n            socket.connect();\n        }\n\n        function sendDisconnect() {\n            socket.disconnect();\n        }\n\n        function sendBroadcast() {\n            axios.post('/demo/send/broadcast', {\n                message: '系统广播通知: 当前时间 ' + moment().format('YYYY-MM-DD HH:mm:ss.SSS')\n            }).then((response) => {\n                const {flag, message} = response.data;\n                if (flag) {\n                    layer.msg(message, {icon: 6});\n                } else {\n                    layer.msg(message, {icon: 5});\n                }\n            });\n        }\n\n        function sendJoin() {\n            let joinRequest = {\n                userId: token,\n                groupId: \"666\"\n\n            };\n            socket.emit('join', joinRequest);\n        }\n\n        function sendGroup() {\n            let message = $('#msg').val();\n\n            if (message === '') {\n                layer.msg('你不说点什么嘛?', {icon: 5});\n                return;\n            }\n\n            $('#msg').val('');\n            let groupRequest = {\n                fromUid: token,\n                groupId: \"666\",\n                message: message\n            };\n            socket.emit('group', groupRequest, function (data) {\n                if (data) {\n                    layer.msg(data, {icon: 5});\n                }\n            });\n        }\n\n        function sendChat() {\n            let toUserId = $('#to').val();\n            let message = $('#msg').val();\n\n            if (toUserId === '') {\n                layer.msg('请输入对方昵称', {icon: 5});\n                return;\n            }\n            if (message === '') {\n                layer.msg('你不说点什么嘛?', {icon: 5});\n                return;\n            }\n            $('#to').val('');\n            $('#msg').val('');\n\n            let singleRequest = {\n              fromUserId: token,\n              toUserId: toUserId,\n              message: message\n            };\n            socket.emit('chat', singleRequest, function (data) {\n                output(`<span class=\"username-msg\">系统通知: 你刚刚和 ${singleRequest.toUid} 说了句悄悄话</span>`);\n                if (data && data.flag) {\n                    output(`<span class=\"username-msg\">系统通知: 悄悄话, ${data.message}</span>`);\n                } else {\n                    output(`<span class=\"disconnect-msg\">系统通知: 悄悄话, ${data.message}</span>`);\n                }\n            });\n        }\n\n        function output(message) {\n            let currentTime = \"<span class='time'>\" + moment().format('YYYY-MM-DD HH:mm:ss.SSS') + \"</span>\";\n            let element = $(\"<div>\" + currentTime + \" \" + message + \"</div>\");\n            $('#console').prepend(element);\n        }\n\n    </script>\n</head>\n<body>\n<h1>spring-boot-demo-websocket-socketio</h1>\n<br/>\n<div id=\"console\" class=\"well\">\n</div>\n<form class=\"well form-inline\" onsubmit=\"return false;\">\n    <input id=\"msg\" class=\"input-xlarge\" type=\"text\" placeholder=\"随便输点啥\"/>\n    <input id=\"to\" class=\"input-xlarge\" type=\"text\" placeholder=\"私聊发给谁\"/>\n    <button type=\"button\" onClick=\"sendJoin()\" class=\"btn\" id=\"join\">加入群聊</button>\n    <button type=\"button\" onClick=\"sendGroup()\" class=\"btn\" id=\"send\">群聊</button>\n    <button type=\"button\" onClick=\"sendChat()\" class=\"btn\" id=\"chat\">私聊</button>\n    <button type=\"button\" onClick=\"sendBroadcast()\" class=\"btn\">广播消息</button>\n    <button type=\"button\" onClick=\"sendConnect()\" class=\"btn\">连接</button>\n    <button type=\"button\" onClick=\"sendDisconnect()\" class=\"btn\">断开</button>\n</form>\n</body>\n</html>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/static/index2.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <title>spring-boot-demo-websocket-socketio</title>\n    <link href=\"bootstrap.css\" rel=\"stylesheet\">\n    <link href=\"https://cdn.bootcss.com/layer/2.3/skin/layer.css\" rel=\"stylesheet\">\n    <style>\n        body {\n            padding: 20px;\n        }\n\n        #console {\n            height: 400px;\n            overflow: auto;\n        }\n\n        .username-msg {\n            color: orange;\n        }\n\n        .connect-msg {\n            color: green;\n        }\n\n        .disconnect-msg {\n            color: red;\n        }\n\n        .broadcast {\n            color: red;\n        }\n\n        .send-msg {\n            color: #888\n        }\n\n        .sys-msg {\n            color: #888\n        }\n\n    </style>\n\n    <script src=\"js/socket.io/socket.io.js\"></script>\n    <script src=\"js/moment.min.js\"></script>\n    <script src=\"js/jquery-1.10.1.min.js\"></script>\n    <script src=\"https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js\"></script>\n    <script src=\"https://cdn.bootcss.com/layer/2.3/layer.js\"></script>\n    <script>\n        // const token = 'user' + Math.floor((Math.random() * 1000) + 1);\n        const token = '252276745';\n        const url = `http://localhost:9393?userId=252276745`; //http://81.68.131.40:9394  https://www.xiaoyuan2.cn:568\n        const socket = io.connect(url);\n        socket.on('connect', function () {\n            output(`<span class=\"connect-msg\">系统通知: ${token}成功连接至websocket服务器</span>`);\n        });\n\n        socket.on('join', function (data) {\n            output(`<span class=\"sys-msg\">${data.groupId} 群通知: 新人 ${data.userId} 请爆照</span>`);\n        });\n\n        socket.on('chat', function (data) {\n            output(`<span class=\"username-msg\">系统通知: 收到来自 ${data.fromUid} 的悄悄话: ${data.message}</span>`);\n        });\n\n        socket.on('group', function (data) {\n            output(`<span class=\"username-msg\">${data.groupId} 群消息: ${data.fromUid} 说: ${data.message}</span>`);\n        });\n\n        socket.on('disconnect', function () {\n            output(`<span class=\"disconnect-msg\">系统通知: ${token}已从websocket服务器断开连接</span>`);\n        });\n\n        socket.on('broadcast', function (data) {\n            output(`<span class=\"broadcast\">${data.message}</span>`);\n        });\n\n        function sendConnect() {\n            socket.connect();\n        }\n\n        function sendDisconnect() {\n            socket.disconnect();\n        }\n\n        function sendBroadcast() {\n            axios.post('/demo/send/broadcast', {\n                message: '系统广播通知: 当前时间 ' + moment().format('YYYY-MM-DD HH:mm:ss.SSS')\n            }).then((response) => {\n                const {flag, message} = response.data;\n                if (flag) {\n                    layer.msg(message, {icon: 6});\n                } else {\n                    layer.msg(message, {icon: 5});\n                }\n            });\n        }\n\n        function sendJoin() {\n            let joinRequest = {\n                userId: token,\n                groupId: \"666\"\n\n            };\n            socket.emit('join', joinRequest);\n        }\n\n        function sendGroup() {\n            let message = $('#msg').val();\n\n            if (message === '') {\n                layer.msg('你不说点什么嘛?', {icon: 5});\n                return;\n            }\n\n            $('#msg').val('');\n            let groupRequest = {\n                fromUid: token,\n                groupId: \"666\",\n                message: message\n            };\n            socket.emit('group', groupRequest, function (data) {\n                if (data) {\n                    layer.msg(data, {icon: 5});\n                }\n            });\n        }\n\n        function sendChat() {\n            let toUserId = $('#to').val();\n            let message = $('#msg').val();\n\n            if (toUserId === '') {\n                layer.msg('请输入对方昵称', {icon: 5});\n                return;\n            }\n            if (message === '') {\n                layer.msg('你不说点什么嘛?', {icon: 5});\n                return;\n            }\n            $('#to').val('');\n            $('#msg').val('');\n\n            let singleRequest = {\n              fromUserId: token,\n              toUserId: toUserId,\n              message: message\n            };\n            socket.emit('chat', singleRequest, function (data) {\n                output(`<span class=\"username-msg\">系统通知: 你刚刚和 ${singleRequest.toUid} 说了句悄悄话</span>`);\n                if (data && data.flag) {\n                    output(`<span class=\"username-msg\">系统通知: 悄悄话, ${data.message}</span>`);\n                } else {\n                    output(`<span class=\"disconnect-msg\">系统通知: 悄悄话, ${data.message}</span>`);\n                }\n            });\n        }\n\n        function output(message) {\n            let currentTime = \"<span class='time'>\" + moment().format('YYYY-MM-DD HH:mm:ss.SSS') + \"</span>\";\n            let element = $(\"<div>\" + currentTime + \" \" + message + \"</div>\");\n            $('#console').prepend(element);\n        }\n\n    </script>\n</head>\n<body>\n<h1>spring-boot-demo-websocket-socketio</h1>\n<br/>\n<div id=\"console\" class=\"well\">\n</div>\n<form class=\"well form-inline\" onsubmit=\"return false;\">\n    <input id=\"msg\" class=\"input-xlarge\" type=\"text\" placeholder=\"随便输点啥\"/>\n    <input id=\"to\" class=\"input-xlarge\" type=\"text\" placeholder=\"私聊发给谁\"/>\n    <button type=\"button\" onClick=\"sendJoin()\" class=\"btn\" id=\"join\">加入群聊</button>\n    <button type=\"button\" onClick=\"sendGroup()\" class=\"btn\" id=\"send\">群聊</button>\n    <button type=\"button\" onClick=\"sendChat()\" class=\"btn\" id=\"chat\">私聊</button>\n    <button type=\"button\" onClick=\"sendBroadcast()\" class=\"btn\">广播消息</button>\n    <button type=\"button\" onClick=\"sendConnect()\" class=\"btn\">连接</button>\n    <button type=\"button\" onClick=\"sendDisconnect()\" class=\"btn\">断开</button>\n</form>\n</body>\n</html>\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/static/js/socket.io/socket.io.js",
    "content": "!function(e){if(\"object\"==typeof exports&&\"undefined\"!=typeof module)module.exports=e();else if(\"function\"==typeof define&&define.amd)define([],e);else{var f;\"undefined\"!=typeof window?f=window:\"undefined\"!=typeof global?f=global:\"undefined\"!=typeof self&&(f=self),f.io=e()}}(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error(\"Cannot find module '\"+o+\"'\")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}({1:[function(_dereq_,module,exports){module.exports=_dereq_(\"./lib/\")},{\"./lib/\":2}],2:[function(_dereq_,module,exports){var url=_dereq_(\"./url\");var parser=_dereq_(\"socket.io-parser\");var Manager=_dereq_(\"./manager\");var debug=_dereq_(\"debug\")(\"socket.io-client\");module.exports=exports=lookup;var cache=exports.managers={};function lookup(uri,opts){if(typeof uri==\"object\"){opts=uri;uri=undefined}opts=opts||{};var parsed=url(uri);var source=parsed.source;var id=parsed.id;var io;if(opts.forceNew||opts[\"force new connection\"]||false===opts.multiplex){debug(\"ignoring socket cache for %s\",source);io=Manager(source,opts)}else{if(!cache[id]){debug(\"new io instance for %s\",source);cache[id]=Manager(source,opts)}io=cache[id]}return io.socket(parsed.path)}exports.protocol=parser.protocol;exports.connect=lookup;exports.Manager=_dereq_(\"./manager\");exports.Socket=_dereq_(\"./socket\")},{\"./manager\":3,\"./socket\":5,\"./url\":6,debug:10,\"socket.io-parser\":46}],3:[function(_dereq_,module,exports){var url=_dereq_(\"./url\");var eio=_dereq_(\"engine.io-client\");var Socket=_dereq_(\"./socket\");var Emitter=_dereq_(\"component-emitter\");var parser=_dereq_(\"socket.io-parser\");var on=_dereq_(\"./on\");var bind=_dereq_(\"component-bind\");var object=_dereq_(\"object-component\");var debug=_dereq_(\"debug\")(\"socket.io-client:manager\");var indexOf=_dereq_(\"indexof\");var Backoff=_dereq_(\"backo2\");module.exports=Manager;function Manager(uri,opts){if(!(this instanceof Manager))return new Manager(uri,opts);if(uri&&\"object\"==typeof uri){opts=uri;uri=undefined}opts=opts||{};opts.path=opts.path||\"/socket.io\";this.nsps={};this.subs=[];this.opts=opts;this.reconnection(opts.reconnection!==false);this.reconnectionAttempts(opts.reconnectionAttempts||Infinity);this.reconnectionDelay(opts.reconnectionDelay||1e3);this.reconnectionDelayMax(opts.reconnectionDelayMax||5e3);this.randomizationFactor(opts.randomizationFactor||.5);this.backoff=new Backoff({min:this.reconnectionDelay(),max:this.reconnectionDelayMax(),jitter:this.randomizationFactor()});this.timeout(null==opts.timeout?2e4:opts.timeout);this.readyState=\"closed\";this.uri=uri;this.connected=[];this.encoding=false;this.packetBuffer=[];this.encoder=new parser.Encoder;this.decoder=new parser.Decoder;this.autoConnect=opts.autoConnect!==false;if(this.autoConnect)this.open()}Manager.prototype.emitAll=function(){this.emit.apply(this,arguments);for(var nsp in this.nsps){this.nsps[nsp].emit.apply(this.nsps[nsp],arguments)}};Manager.prototype.updateSocketIds=function(){for(var nsp in this.nsps){this.nsps[nsp].id=this.engine.id}};Emitter(Manager.prototype);Manager.prototype.reconnection=function(v){if(!arguments.length)return this._reconnection;this._reconnection=!!v;return this};Manager.prototype.reconnectionAttempts=function(v){if(!arguments.length)return this._reconnectionAttempts;this._reconnectionAttempts=v;return this};Manager.prototype.reconnectionDelay=function(v){if(!arguments.length)return this._reconnectionDelay;this._reconnectionDelay=v;this.backoff&&this.backoff.setMin(v);return this};Manager.prototype.randomizationFactor=function(v){if(!arguments.length)return this._randomizationFactor;this._randomizationFactor=v;this.backoff&&this.backoff.setJitter(v);return this};Manager.prototype.reconnectionDelayMax=function(v){if(!arguments.length)return this._reconnectionDelayMax;this._reconnectionDelayMax=v;this.backoff&&this.backoff.setMax(v);return this};Manager.prototype.timeout=function(v){if(!arguments.length)return this._timeout;this._timeout=v;return this};Manager.prototype.maybeReconnectOnOpen=function(){if(!this.reconnecting&&this._reconnection&&this.backoff.attempts===0){this.reconnect()}};Manager.prototype.open=Manager.prototype.connect=function(fn){debug(\"readyState %s\",this.readyState);if(~this.readyState.indexOf(\"open\"))return this;debug(\"opening %s\",this.uri);this.engine=eio(this.uri,this.opts);var socket=this.engine;var self=this;this.readyState=\"opening\";this.skipReconnect=false;var openSub=on(socket,\"open\",function(){self.onopen();fn&&fn()});var errorSub=on(socket,\"error\",function(data){debug(\"connect_error\");self.cleanup();self.readyState=\"closed\";self.emitAll(\"connect_error\",data);if(fn){var err=new Error(\"Connection error\");err.data=data;fn(err)}else{self.maybeReconnectOnOpen()}});if(false!==this._timeout){var timeout=this._timeout;debug(\"connect attempt will timeout after %d\",timeout);var timer=setTimeout(function(){debug(\"connect attempt timed out after %d\",timeout);openSub.destroy();socket.close();socket.emit(\"error\",\"timeout\");self.emitAll(\"connect_timeout\",timeout)},timeout);this.subs.push({destroy:function(){clearTimeout(timer)}})}this.subs.push(openSub);this.subs.push(errorSub);return this};Manager.prototype.onopen=function(){debug(\"open\");this.cleanup();this.readyState=\"open\";this.emit(\"open\");var socket=this.engine;this.subs.push(on(socket,\"data\",bind(this,\"ondata\")));this.subs.push(on(this.decoder,\"decoded\",bind(this,\"ondecoded\")));this.subs.push(on(socket,\"error\",bind(this,\"onerror\")));this.subs.push(on(socket,\"close\",bind(this,\"onclose\")))};Manager.prototype.ondata=function(data){this.decoder.add(data)};Manager.prototype.ondecoded=function(packet){this.emit(\"packet\",packet)};Manager.prototype.onerror=function(err){debug(\"error\",err);this.emitAll(\"error\",err)};Manager.prototype.socket=function(nsp){var socket=this.nsps[nsp];if(!socket){socket=new Socket(this,nsp);this.nsps[nsp]=socket;var self=this;socket.on(\"connect\",function(){socket.id=self.engine.id;if(!~indexOf(self.connected,socket)){self.connected.push(socket)}})}return socket};Manager.prototype.destroy=function(socket){var index=indexOf(this.connected,socket);if(~index)this.connected.splice(index,1);if(this.connected.length)return;this.close()};Manager.prototype.packet=function(packet){debug(\"writing packet %j\",packet);var self=this;if(!self.encoding){self.encoding=true;this.encoder.encode(packet,function(encodedPackets){for(var i=0;i<encodedPackets.length;i++){self.engine.write(encodedPackets[i])}self.encoding=false;self.processPacketQueue()})}else{self.packetBuffer.push(packet)}};Manager.prototype.processPacketQueue=function(){if(this.packetBuffer.length>0&&!this.encoding){var pack=this.packetBuffer.shift();this.packet(pack)}};Manager.prototype.cleanup=function(){var sub;while(sub=this.subs.shift())sub.destroy();this.packetBuffer=[];this.encoding=false;this.decoder.destroy()};Manager.prototype.close=Manager.prototype.disconnect=function(){this.skipReconnect=true;this.backoff.reset();this.readyState=\"closed\";this.engine&&this.engine.close()};Manager.prototype.onclose=function(reason){debug(\"close\");this.cleanup();this.backoff.reset();this.readyState=\"closed\";this.emit(\"close\",reason);if(this._reconnection&&!this.skipReconnect){this.reconnect()}};Manager.prototype.reconnect=function(){if(this.reconnecting||this.skipReconnect)return this;var self=this;if(this.backoff.attempts>=this._reconnectionAttempts){debug(\"reconnect failed\");this.backoff.reset();this.emitAll(\"reconnect_failed\");this.reconnecting=false}else{var delay=this.backoff.duration();debug(\"will wait %dms before reconnect attempt\",delay);this.reconnecting=true;var timer=setTimeout(function(){if(self.skipReconnect)return;debug(\"attempting reconnect\");self.emitAll(\"reconnect_attempt\",self.backoff.attempts);self.emitAll(\"reconnecting\",self.backoff.attempts);if(self.skipReconnect)return;self.open(function(err){if(err){debug(\"reconnect attempt error\");self.reconnecting=false;self.reconnect();self.emitAll(\"reconnect_error\",err.data)}else{debug(\"reconnect success\");self.onreconnect()}})},delay);this.subs.push({destroy:function(){clearTimeout(timer)}})}};Manager.prototype.onreconnect=function(){var attempt=this.backoff.attempts;this.reconnecting=false;this.backoff.reset();this.updateSocketIds();this.emitAll(\"reconnect\",attempt)}},{\"./on\":4,\"./socket\":5,\"./url\":6,backo2:7,\"component-bind\":8,\"component-emitter\":9,debug:10,\"engine.io-client\":11,indexof:42,\"object-component\":43,\"socket.io-parser\":46}],4:[function(_dereq_,module,exports){module.exports=on;function on(obj,ev,fn){obj.on(ev,fn);return{destroy:function(){obj.removeListener(ev,fn)}}}},{}],5:[function(_dereq_,module,exports){var parser=_dereq_(\"socket.io-parser\");var Emitter=_dereq_(\"component-emitter\");var toArray=_dereq_(\"to-array\");var on=_dereq_(\"./on\");var bind=_dereq_(\"component-bind\");var debug=_dereq_(\"debug\")(\"socket.io-client:socket\");var hasBin=_dereq_(\"has-binary\");module.exports=exports=Socket;var events={connect:1,connect_error:1,connect_timeout:1,disconnect:1,error:1,reconnect:1,reconnect_attempt:1,reconnect_failed:1,reconnect_error:1,reconnecting:1};var emit=Emitter.prototype.emit;function Socket(io,nsp){this.io=io;this.nsp=nsp;this.json=this;this.ids=0;this.acks={};if(this.io.autoConnect)this.open();this.receiveBuffer=[];this.sendBuffer=[];this.connected=false;this.disconnected=true}Emitter(Socket.prototype);Socket.prototype.subEvents=function(){if(this.subs)return;var io=this.io;this.subs=[on(io,\"open\",bind(this,\"onopen\")),on(io,\"packet\",bind(this,\"onpacket\")),on(io,\"close\",bind(this,\"onclose\"))]};Socket.prototype.open=Socket.prototype.connect=function(){if(this.connected)return this;this.subEvents();this.io.open();if(\"open\"==this.io.readyState)this.onopen();return this};Socket.prototype.send=function(){var args=toArray(arguments);args.unshift(\"message\");this.emit.apply(this,args);return this};Socket.prototype.emit=function(ev){if(events.hasOwnProperty(ev)){emit.apply(this,arguments);return this}var args=toArray(arguments);var parserType=parser.EVENT;if(hasBin(args)){parserType=parser.BINARY_EVENT}var packet={type:parserType,data:args};if(\"function\"==typeof args[args.length-1]){debug(\"emitting packet with ack id %d\",this.ids);this.acks[this.ids]=args.pop();packet.id=this.ids++}if(this.connected){this.packet(packet)}else{this.sendBuffer.push(packet)}return this};Socket.prototype.packet=function(packet){packet.nsp=this.nsp;this.io.packet(packet)};Socket.prototype.onopen=function(){debug(\"transport is open - connecting\");if(\"/\"!=this.nsp){this.packet({type:parser.CONNECT})}};Socket.prototype.onclose=function(reason){debug(\"close (%s)\",reason);this.connected=false;this.disconnected=true;delete this.id;this.emit(\"disconnect\",reason)};Socket.prototype.onpacket=function(packet){if(packet.nsp!=this.nsp)return;switch(packet.type){case parser.CONNECT:this.onconnect();break;case parser.EVENT:this.onevent(packet);break;case parser.BINARY_EVENT:this.onevent(packet);break;case parser.ACK:this.onack(packet);break;case parser.BINARY_ACK:this.onack(packet);break;case parser.DISCONNECT:this.ondisconnect();break;case parser.ERROR:this.emit(\"error\",packet.data);break}};Socket.prototype.onevent=function(packet){var args=packet.data||[];debug(\"emitting event %j\",args);if(null!=packet.id){debug(\"attaching ack callback to event\");args.push(this.ack(packet.id))}if(this.connected){emit.apply(this,args)}else{this.receiveBuffer.push(args)}};Socket.prototype.ack=function(id){var self=this;var sent=false;return function(){if(sent)return;sent=true;var args=toArray(arguments);debug(\"sending ack %j\",args);var type=hasBin(args)?parser.BINARY_ACK:parser.ACK;self.packet({type:type,id:id,data:args})}};Socket.prototype.onack=function(packet){debug(\"calling ack %s with %j\",packet.id,packet.data);var fn=this.acks[packet.id];fn.apply(this,packet.data);delete this.acks[packet.id]};Socket.prototype.onconnect=function(){this.connected=true;this.disconnected=false;this.emit(\"connect\");this.emitBuffered()};Socket.prototype.emitBuffered=function(){var i;for(i=0;i<this.receiveBuffer.length;i++){emit.apply(this,this.receiveBuffer[i])}this.receiveBuffer=[];for(i=0;i<this.sendBuffer.length;i++){this.packet(this.sendBuffer[i])}this.sendBuffer=[]};Socket.prototype.ondisconnect=function(){debug(\"server disconnect (%s)\",this.nsp);this.destroy();this.onclose(\"io server disconnect\")};Socket.prototype.destroy=function(){if(this.subs){for(var i=0;i<this.subs.length;i++){this.subs[i].destroy()}this.subs=null}this.io.destroy(this)};Socket.prototype.close=Socket.prototype.disconnect=function(){if(this.connected){debug(\"performing disconnect (%s)\",this.nsp);this.packet({type:parser.DISCONNECT})}this.destroy();if(this.connected){this.onclose(\"io client disconnect\")}return this}},{\"./on\":4,\"component-bind\":8,\"component-emitter\":9,debug:10,\"has-binary\":38,\"socket.io-parser\":46,\"to-array\":50}],6:[function(_dereq_,module,exports){(function(global){var parseuri=_dereq_(\"parseuri\");var debug=_dereq_(\"debug\")(\"socket.io-client:url\");module.exports=url;function url(uri,loc){var obj=uri;var loc=loc||global.location;if(null==uri)uri=loc.protocol+\"//\"+loc.host;if(\"string\"==typeof uri){if(\"/\"==uri.charAt(0)){if(\"/\"==uri.charAt(1)){uri=loc.protocol+uri}else{uri=loc.hostname+uri}}if(!/^(https?|wss?):\\/\\//.test(uri)){debug(\"protocol-less url %s\",uri);if(\"undefined\"!=typeof loc){uri=loc.protocol+\"//\"+uri}else{uri=\"https://\"+uri}}debug(\"parse %s\",uri);obj=parseuri(uri)}if(!obj.port){if(/^(http|ws)$/.test(obj.protocol)){obj.port=\"80\"}else if(/^(http|ws)s$/.test(obj.protocol)){obj.port=\"443\"}}obj.path=obj.path||\"/\";obj.id=obj.protocol+\"://\"+obj.host+\":\"+obj.port;obj.href=obj.protocol+\"://\"+obj.host+(loc&&loc.port==obj.port?\"\":\":\"+obj.port);return obj}}).call(this,typeof self!==\"undefined\"?self:typeof window!==\"undefined\"?window:{})},{debug:10,parseuri:44}],7:[function(_dereq_,module,exports){module.exports=Backoff;function Backoff(opts){opts=opts||{};this.ms=opts.min||100;this.max=opts.max||1e4;this.factor=opts.factor||2;this.jitter=opts.jitter>0&&opts.jitter<=1?opts.jitter:0;this.attempts=0}Backoff.prototype.duration=function(){var ms=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var rand=Math.random();var deviation=Math.floor(rand*this.jitter*ms);ms=(Math.floor(rand*10)&1)==0?ms-deviation:ms+deviation}return Math.min(ms,this.max)|0};Backoff.prototype.reset=function(){this.attempts=0};Backoff.prototype.setMin=function(min){this.ms=min};Backoff.prototype.setMax=function(max){this.max=max};Backoff.prototype.setJitter=function(jitter){this.jitter=jitter}},{}],8:[function(_dereq_,module,exports){var slice=[].slice;module.exports=function(obj,fn){if(\"string\"==typeof fn)fn=obj[fn];if(\"function\"!=typeof fn)throw new Error(\"bind() requires a function\");var args=slice.call(arguments,2);return function(){return fn.apply(obj,args.concat(slice.call(arguments)))}}},{}],9:[function(_dereq_,module,exports){module.exports=Emitter;function Emitter(obj){if(obj)return mixin(obj)}function mixin(obj){for(var key in Emitter.prototype){obj[key]=Emitter.prototype[key]}return obj}Emitter.prototype.on=Emitter.prototype.addEventListener=function(event,fn){this._callbacks=this._callbacks||{};(this._callbacks[event]=this._callbacks[event]||[]).push(fn);return this};Emitter.prototype.once=function(event,fn){var self=this;this._callbacks=this._callbacks||{};function on(){self.off(event,on);fn.apply(this,arguments)}on.fn=fn;this.on(event,on);return this};Emitter.prototype.off=Emitter.prototype.removeListener=Emitter.prototype.removeAllListeners=Emitter.prototype.removeEventListener=function(event,fn){this._callbacks=this._callbacks||{};if(0==arguments.length){this._callbacks={};return this}var callbacks=this._callbacks[event];if(!callbacks)return this;if(1==arguments.length){delete this._callbacks[event];return this}var cb;for(var i=0;i<callbacks.length;i++){cb=callbacks[i];if(cb===fn||cb.fn===fn){callbacks.splice(i,1);break}}return this};Emitter.prototype.emit=function(event){this._callbacks=this._callbacks||{};var args=[].slice.call(arguments,1),callbacks=this._callbacks[event];if(callbacks){callbacks=callbacks.slice(0);for(var i=0,len=callbacks.length;i<len;++i){callbacks[i].apply(this,args)}}return this};Emitter.prototype.listeners=function(event){this._callbacks=this._callbacks||{};return this._callbacks[event]||[]};Emitter.prototype.hasListeners=function(event){return!!this.listeners(event).length}},{}],10:[function(_dereq_,module,exports){module.exports=debug;function debug(name){if(!debug.enabled(name))return function(){};return function(fmt){fmt=coerce(fmt);var curr=new Date;var ms=curr-(debug[name]||curr);debug[name]=curr;fmt=name+\" \"+fmt+\" +\"+debug.humanize(ms);window.console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}}debug.names=[];debug.skips=[];debug.enable=function(name){try{localStorage.debug=name}catch(e){}var split=(name||\"\").split(/[\\s,]+/),len=split.length;for(var i=0;i<len;i++){name=split[i].replace(\"*\",\".*?\");if(name[0]===\"-\"){debug.skips.push(new RegExp(\"^\"+name.substr(1)+\"$\"))}else{debug.names.push(new RegExp(\"^\"+name+\"$\"))}}};debug.disable=function(){debug.enable(\"\")};debug.humanize=function(ms){var sec=1e3,min=60*1e3,hour=60*min;if(ms>=hour)return(ms/hour).toFixed(1)+\"h\";if(ms>=min)return(ms/min).toFixed(1)+\"m\";if(ms>=sec)return(ms/sec|0)+\"s\";return ms+\"ms\"};debug.enabled=function(name){for(var i=0,len=debug.skips.length;i<len;i++){if(debug.skips[i].test(name)){return false}}for(var i=0,len=debug.names.length;i<len;i++){if(debug.names[i].test(name)){return true}}return false};function coerce(val){if(val instanceof Error)return val.stack||val.message;return val}try{if(window.localStorage)debug.enable(localStorage.debug)}catch(e){}},{}],11:[function(_dereq_,module,exports){module.exports=_dereq_(\"./lib/\")},{\"./lib/\":12}],12:[function(_dereq_,module,exports){module.exports=_dereq_(\"./socket\");module.exports.parser=_dereq_(\"engine.io-parser\")},{\"./socket\":13,\"engine.io-parser\":25}],13:[function(_dereq_,module,exports){(function(global){var transports=_dereq_(\"./transports\");var Emitter=_dereq_(\"component-emitter\");var debug=_dereq_(\"debug\")(\"engine.io-client:socket\");var index=_dereq_(\"indexof\");var parser=_dereq_(\"engine.io-parser\");var parseuri=_dereq_(\"parseuri\");var parsejson=_dereq_(\"parsejson\");var parseqs=_dereq_(\"parseqs\");module.exports=Socket;function noop(){}function Socket(uri,opts){if(!(this instanceof Socket))return new Socket(uri,opts);opts=opts||{};if(uri&&\"object\"==typeof uri){opts=uri;uri=null}if(uri){uri=parseuri(uri);opts.host=uri.host;opts.secure=uri.protocol==\"https\"||uri.protocol==\"wss\";opts.port=uri.port;if(uri.query)opts.query=uri.query}this.secure=null!=opts.secure?opts.secure:global.location&&\"https:\"==location.protocol;if(opts.host){var pieces=opts.host.split(\":\");opts.hostname=pieces.shift();if(pieces.length){opts.port=pieces.pop()}else if(!opts.port){opts.port=this.secure?\"443\":\"80\"}}this.agent=opts.agent||false;this.hostname=opts.hostname||(global.location?location.hostname:\"localhost\");this.port=opts.port||(global.location&&location.port?location.port:this.secure?443:80);this.query=opts.query||{};if(\"string\"==typeof this.query)this.query=parseqs.decode(this.query);this.upgrade=false!==opts.upgrade;this.path=(opts.path||\"/engine.io\").replace(/\\/$/,\"\")+\"/\";this.forceJSONP=!!opts.forceJSONP;this.jsonp=false!==opts.jsonp;this.forceBase64=!!opts.forceBase64;this.enablesXDR=!!opts.enablesXDR;this.timestampParam=opts.timestampParam||\"t\";this.timestampRequests=opts.timestampRequests;this.transports=opts.transports||[\"polling\",\"websocket\"];this.readyState=\"\";this.writeBuffer=[];this.callbackBuffer=[];this.policyPort=opts.policyPort||843;this.rememberUpgrade=opts.rememberUpgrade||false;this.binaryType=null;this.onlyBinaryUpgrades=opts.onlyBinaryUpgrades;this.perMessageDeflate=false!==opts.perMessageDeflate?opts.perMessageDeflate||true:false;this.pfx=opts.pfx||null;this.key=opts.key||null;this.passphrase=opts.passphrase||null;this.cert=opts.cert||null;this.ca=opts.ca||null;this.ciphers=opts.ciphers||null;this.rejectUnauthorized=opts.rejectUnauthorized||null;this.open()}Socket.priorWebsocketSuccess=false;Emitter(Socket.prototype);Socket.protocol=parser.protocol;Socket.Socket=Socket;Socket.Transport=_dereq_(\"./transport\");Socket.transports=_dereq_(\"./transports\");Socket.parser=_dereq_(\"engine.io-parser\");Socket.prototype.createTransport=function(name){debug('creating transport \"%s\"',name);var query=clone(this.query);query.EIO=parser.protocol;query.transport=name;if(this.id)query.sid=this.id;var transport=new transports[name]({agent:this.agent,hostname:this.hostname,port:this.port,secure:this.secure,path:this.path,query:query,forceJSONP:this.forceJSONP,jsonp:this.jsonp,forceBase64:this.forceBase64,enablesXDR:this.enablesXDR,timestampRequests:this.timestampRequests,timestampParam:this.timestampParam,policyPort:this.policyPort,socket:this,pfx:this.pfx,key:this.key,passphrase:this.passphrase,cert:this.cert,ca:this.ca,ciphers:this.ciphers,rejectUnauthorized:this.rejectUnauthorized,perMessageDeflate:this.perMessageDeflate});return transport};function clone(obj){var o={};for(var i in obj){if(obj.hasOwnProperty(i)){o[i]=obj[i]}}return o}Socket.prototype.open=function(){var transport;if(this.rememberUpgrade&&Socket.priorWebsocketSuccess&&this.transports.indexOf(\"websocket\")!=-1){transport=\"websocket\"}else if(0==this.transports.length){var self=this;setTimeout(function(){self.emit(\"error\",\"No transports available\")},0);return}else{transport=this.transports[0]}this.readyState=\"opening\";var transport;try{transport=this.createTransport(transport)}catch(e){this.transports.shift();this.open();return}transport.open();this.setTransport(transport)};Socket.prototype.setTransport=function(transport){debug(\"setting transport %s\",transport.name);var self=this;if(this.transport){debug(\"clearing existing transport %s\",this.transport.name);this.transport.removeAllListeners()}this.transport=transport;transport.on(\"drain\",function(){self.onDrain()}).on(\"packet\",function(packet){self.onPacket(packet)}).on(\"error\",function(e){self.onError(e)}).on(\"close\",function(){self.onClose(\"transport close\")})};Socket.prototype.probe=function(name){debug('probing transport \"%s\"',name);var transport=this.createTransport(name,{probe:1}),failed=false,self=this;Socket.priorWebsocketSuccess=false;function onTransportOpen(){if(self.onlyBinaryUpgrades){var upgradeLosesBinary=!this.supportsBinary&&self.transport.supportsBinary;failed=failed||upgradeLosesBinary}if(failed)return;debug('probe transport \"%s\" opened',name);transport.send([{type:\"ping\",data:\"probe\",options:{compress:true}}]);transport.once(\"packet\",function(msg){if(failed)return;if(\"pong\"==msg.type&&\"probe\"==msg.data){debug('probe transport \"%s\" pong',name);self.upgrading=true;self.emit(\"upgrading\",transport);if(!transport)return;Socket.priorWebsocketSuccess=\"websocket\"==transport.name;debug('pausing current transport \"%s\"',self.transport.name);self.transport.pause(function(){if(failed)return;if(\"closed\"==self.readyState)return;debug(\"changing transport and sending upgrade packet\");cleanup();self.setTransport(transport);transport.send([{type:\"upgrade\",options:{compress:true}}]);self.emit(\"upgrade\",transport);transport=null;self.upgrading=false;self.flush()})}else{debug('probe transport \"%s\" failed',name);var err=new Error(\"probe error\");err.transport=transport.name;self.emit(\"upgradeError\",err)}})}function freezeTransport(){if(failed)return;failed=true;cleanup();transport.close();transport=null}function onerror(err){var error=new Error(\"probe error: \"+err);error.transport=transport.name;freezeTransport();debug('probe transport \"%s\" failed because of error: %s',name,err);self.emit(\"upgradeError\",error)}function onTransportClose(){onerror(\"transport closed\")}function onclose(){onerror(\"socket closed\")}function onupgrade(to){if(transport&&to.name!=transport.name){debug('\"%s\" works - aborting \"%s\"',to.name,transport.name);freezeTransport()}}function cleanup(){transport.removeListener(\"open\",onTransportOpen);transport.removeListener(\"error\",onerror);transport.removeListener(\"close\",onTransportClose);self.removeListener(\"close\",onclose);self.removeListener(\"upgrading\",onupgrade)}transport.once(\"open\",onTransportOpen);transport.once(\"error\",onerror);transport.once(\"close\",onTransportClose);this.once(\"close\",onclose);this.once(\"upgrading\",onupgrade);transport.open()};Socket.prototype.onOpen=function(){debug(\"socket open\");this.readyState=\"open\";Socket.priorWebsocketSuccess=\"websocket\"==this.transport.name;this.emit(\"open\");this.flush();if(\"open\"==this.readyState&&this.upgrade&&this.transport.pause){debug(\"starting upgrade probes\");for(var i=0,l=this.upgrades.length;i<l;i++){this.probe(this.upgrades[i])}}};Socket.prototype.onPacket=function(packet){if(\"opening\"==this.readyState||\"open\"==this.readyState){debug('socket receive: type \"%s\", data \"%s\"',packet.type,packet.data);this.emit(\"packet\",packet);this.emit(\"heartbeat\");switch(packet.type){case\"open\":this.onHandshake(parsejson(packet.data));break;case\"pong\":this.setPing();break;case\"error\":var err=new Error(\"server error\");err.code=packet.data;this.emit(\"error\",err);break;case\"message\":this.emit(\"data\",packet.data);this.emit(\"message\",packet.data);break}}else{debug('packet received with socket readyState \"%s\"',this.readyState)}};Socket.prototype.onHandshake=function(data){this.emit(\"handshake\",data);this.id=data.sid;this.transport.query.sid=data.sid;this.upgrades=this.filterUpgrades(data.upgrades);this.pingInterval=data.pingInterval;this.pingTimeout=data.pingTimeout;this.onOpen();if(\"closed\"==this.readyState)return;this.setPing();this.removeListener(\"heartbeat\",this.onHeartbeat);this.on(\"heartbeat\",this.onHeartbeat)};Socket.prototype.onHeartbeat=function(timeout){clearTimeout(this.pingTimeoutTimer);var self=this;self.pingTimeoutTimer=setTimeout(function(){if(\"closed\"==self.readyState)return;self.onClose(\"ping timeout\")},timeout||self.pingInterval+self.pingTimeout)};Socket.prototype.setPing=function(){var self=this;clearTimeout(self.pingIntervalTimer);self.pingIntervalTimer=setTimeout(function(){debug(\"writing ping packet - expecting pong within %sms\",self.pingTimeout);self.ping();self.onHeartbeat(self.pingTimeout)},self.pingInterval)};Socket.prototype.ping=function(){this.sendPacket(\"ping\")};Socket.prototype.onDrain=function(){for(var i=0;i<this.prevBufferLen;i++){if(this.callbackBuffer[i]){this.callbackBuffer[i]()}}this.writeBuffer.splice(0,this.prevBufferLen);this.callbackBuffer.splice(0,this.prevBufferLen);this.prevBufferLen=0;if(this.writeBuffer.length==0){this.emit(\"drain\")}else{this.flush()}};Socket.prototype.flush=function(){if(\"closed\"!=this.readyState&&this.transport.writable&&!this.upgrading&&this.writeBuffer.length){debug(\"flushing %d packets in socket\",this.writeBuffer.length);this.transport.send(this.writeBuffer);this.prevBufferLen=this.writeBuffer.length;this.emit(\"flush\")}};Socket.prototype.write=Socket.prototype.send=function(msg,options,fn){this.sendPacket(\"message\",msg,options,fn);return this};Socket.prototype.sendPacket=function(type,data,options,fn){if(\"function\"==typeof options){fn=options;options=null}if(\"closing\"==this.readyState||\"closed\"==this.readyState){return}options=options||{};options.compress=false!==options.compress;var packet={type:type,data:data,options:options};this.emit(\"packetCreate\",packet);this.writeBuffer.push(packet);this.callbackBuffer.push(fn);this.flush()};Socket.prototype.close=function(){if(\"opening\"==this.readyState||\"open\"==this.readyState){this.readyState=\"closing\";var self=this;function close(){self.onClose(\"forced close\");debug(\"socket closing - telling transport to close\");self.transport.close()}function cleanupAndClose(){self.removeListener(\"upgrade\",cleanupAndClose);self.removeListener(\"upgradeError\",cleanupAndClose);close()}function waitForUpgrade(){self.once(\"upgrade\",cleanupAndClose);self.once(\"upgradeError\",cleanupAndClose)}if(this.writeBuffer.length){this.once(\"drain\",function(){if(this.upgrading){waitForUpgrade()}else{close()}})}else if(this.upgrading){waitForUpgrade()}else{close()}}return this};Socket.prototype.onError=function(err){debug(\"socket error %j\",err);Socket.priorWebsocketSuccess=false;this.emit(\"error\",err);this.onClose(\"transport error\",err)};Socket.prototype.onClose=function(reason,desc){if(\"opening\"==this.readyState||\"open\"==this.readyState||\"closing\"==this.readyState){debug('socket close with reason: \"%s\"',reason);var self=this;clearTimeout(this.pingIntervalTimer);clearTimeout(this.pingTimeoutTimer);setTimeout(function(){self.writeBuffer=[];self.callbackBuffer=[];self.prevBufferLen=0},0);this.transport.removeAllListeners(\"close\");this.transport.close();this.transport.removeAllListeners();this.readyState=\"closed\";this.id=null;this.emit(\"close\",reason,desc)}};Socket.prototype.filterUpgrades=function(upgrades){var filteredUpgrades=[];for(var i=0,j=upgrades.length;i<j;i++){if(~index(this.transports,upgrades[i]))filteredUpgrades.push(upgrades[i])}return filteredUpgrades}}).call(this,typeof self!==\"undefined\"?self:typeof window!==\"undefined\"?window:{})},{\"./transport\":14,\"./transports\":15,\"component-emitter\":9,debug:22,\"engine.io-parser\":25,indexof:42,parsejson:34,parseqs:35,parseuri:36}],14:[function(_dereq_,module,exports){var parser=_dereq_(\"engine.io-parser\");var Emitter=_dereq_(\"component-emitter\");module.exports=Transport;function Transport(opts){this.path=opts.path;this.hostname=opts.hostname;this.port=opts.port;this.secure=opts.secure;this.query=opts.query;this.timestampParam=opts.timestampParam;this.timestampRequests=opts.timestampRequests;this.readyState=\"\";this.agent=opts.agent||false;this.socket=opts.socket;this.enablesXDR=opts.enablesXDR;this.pfx=opts.pfx;this.key=opts.key;this.passphrase=opts.passphrase;this.cert=opts.cert;this.ca=opts.ca;this.ciphers=opts.ciphers;this.rejectUnauthorized=opts.rejectUnauthorized}Emitter(Transport.prototype);Transport.timestamps=0;Transport.prototype.onError=function(msg,desc){var err=new Error(msg);err.type=\"TransportError\";err.description=desc;this.emit(\"error\",err);return this};Transport.prototype.open=function(){if(\"closed\"==this.readyState||\"\"==this.readyState){this.readyState=\"opening\";this.doOpen()}return this};Transport.prototype.close=function(){if(\"opening\"==this.readyState||\"open\"==this.readyState){this.doClose();this.onClose()}return this};Transport.prototype.send=function(packets){if(\"open\"==this.readyState){this.write(packets)}else{throw new Error(\"Transport not open\")}};Transport.prototype.onOpen=function(){this.readyState=\"open\";this.writable=true;this.emit(\"open\")};Transport.prototype.onData=function(data){var packet=parser.decodePacket(data,this.socket.binaryType);this.onPacket(packet)};Transport.prototype.onPacket=function(packet){this.emit(\"packet\",packet)};Transport.prototype.onClose=function(){this.readyState=\"closed\";this.emit(\"close\")}},{\"component-emitter\":9,\"engine.io-parser\":25}],15:[function(_dereq_,module,exports){(function(global){var XMLHttpRequest=_dereq_(\"xmlhttprequest\");var XHR=_dereq_(\"./polling-xhr\");var JSONP=_dereq_(\"./polling-jsonp\");var websocket=_dereq_(\"./websocket\");exports.polling=polling;exports.websocket=websocket;function polling(opts){var xhr;var xd=false;var xs=false;var jsonp=false!==opts.jsonp;if(global.location){var isSSL=\"https:\"==location.protocol;var port=location.port;if(!port){port=isSSL?443:80}xd=opts.hostname!=location.hostname||port!=opts.port;xs=opts.secure!=isSSL}opts.xdomain=xd;opts.xscheme=xs;xhr=new XMLHttpRequest(opts);if(\"open\"in xhr&&!opts.forceJSONP){return new XHR(opts)}else{if(!jsonp)throw new Error(\"JSONP disabled\");return new JSONP(opts)}}}).call(this,typeof self!==\"undefined\"?self:typeof window!==\"undefined\"?window:{})},{\"./polling-jsonp\":16,\"./polling-xhr\":17,\"./websocket\":19,xmlhttprequest:20}],16:[function(_dereq_,module,exports){(function(global){var Polling=_dereq_(\"./polling\");\nvar inherit=_dereq_(\"component-inherit\");module.exports=JSONPPolling;var rNewline=/\\n/g;var rEscapedNewline=/\\\\n/g;var callbacks;var index=0;function empty(){}function JSONPPolling(opts){Polling.call(this,opts);this.query=this.query||{};if(!callbacks){if(!global.___eio)global.___eio=[];callbacks=global.___eio}this.index=callbacks.length;var self=this;callbacks.push(function(msg){self.onData(msg)});this.query.j=this.index;if(global.document&&global.addEventListener){global.addEventListener(\"beforeunload\",function(){if(self.script)self.script.onerror=empty},false)}}inherit(JSONPPolling,Polling);JSONPPolling.prototype.supportsBinary=false;JSONPPolling.prototype.doClose=function(){if(this.script){this.script.parentNode.removeChild(this.script);this.script=null}if(this.form){this.form.parentNode.removeChild(this.form);this.form=null;this.iframe=null}Polling.prototype.doClose.call(this)};JSONPPolling.prototype.doPoll=function(){var self=this;var script=document.createElement(\"script\");if(this.script){this.script.parentNode.removeChild(this.script);this.script=null}script.async=true;script.src=this.uri();script.onerror=function(e){self.onError(\"jsonp poll error\",e)};var insertAt=document.getElementsByTagName(\"script\")[0];insertAt.parentNode.insertBefore(script,insertAt);this.script=script;var isUAgecko=\"undefined\"!=typeof navigator&&/gecko/i.test(navigator.userAgent);if(isUAgecko){setTimeout(function(){var iframe=document.createElement(\"iframe\");document.body.appendChild(iframe);document.body.removeChild(iframe)},100)}};JSONPPolling.prototype.doWrite=function(data,fn){var self=this;if(!this.form){var form=document.createElement(\"form\");var area=document.createElement(\"textarea\");var id=this.iframeId=\"eio_iframe_\"+this.index;var iframe;form.className=\"socketio\";form.style.position=\"absolute\";form.style.top=\"-1000px\";form.style.left=\"-1000px\";form.target=id;form.method=\"POST\";form.setAttribute(\"accept-charset\",\"utf-8\");area.name=\"d\";form.appendChild(area);document.body.appendChild(form);this.form=form;this.area=area}this.form.action=this.uri();function complete(){initIframe();fn()}function initIframe(){if(self.iframe){try{self.form.removeChild(self.iframe)}catch(e){self.onError(\"jsonp polling iframe removal error\",e)}}try{var html='<iframe src=\"javascript:0\" name=\"'+self.iframeId+'\">';iframe=document.createElement(html)}catch(e){iframe=document.createElement(\"iframe\");iframe.name=self.iframeId;iframe.src=\"javascript:0\"}iframe.id=self.iframeId;self.form.appendChild(iframe);self.iframe=iframe}initIframe();data=data.replace(rEscapedNewline,\"\\\\\\n\");this.area.value=data.replace(rNewline,\"\\\\n\");try{this.form.submit()}catch(e){}if(this.iframe.attachEvent){this.iframe.onreadystatechange=function(){if(self.iframe.readyState==\"complete\"){complete()}}}else{this.iframe.onload=complete}}}).call(this,typeof self!==\"undefined\"?self:typeof window!==\"undefined\"?window:{})},{\"./polling\":18,\"component-inherit\":21}],17:[function(_dereq_,module,exports){(function(global){var XMLHttpRequest=_dereq_(\"xmlhttprequest\");var Polling=_dereq_(\"./polling\");var Emitter=_dereq_(\"component-emitter\");var inherit=_dereq_(\"component-inherit\");var debug=_dereq_(\"debug\")(\"engine.io-client:polling-xhr\");module.exports=XHR;module.exports.Request=Request;function empty(){}function XHR(opts){Polling.call(this,opts);if(global.location){var isSSL=\"https:\"==location.protocol;var port=location.port;if(!port){port=isSSL?443:80}this.xd=opts.hostname!=global.location.hostname||port!=opts.port;this.xs=opts.secure!=isSSL}}inherit(XHR,Polling);XHR.prototype.supportsBinary=true;XHR.prototype.request=function(opts){opts=opts||{};opts.uri=this.uri();opts.xd=this.xd;opts.xs=this.xs;opts.agent=this.agent||false;opts.supportsBinary=this.supportsBinary;opts.enablesXDR=this.enablesXDR;opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;return new Request(opts)};XHR.prototype.doWrite=function(data,fn){var isBinary=typeof data!==\"string\"&&data!==undefined;var req=this.request({method:\"POST\",data:data,isBinary:isBinary});var self=this;req.on(\"success\",fn);req.on(\"error\",function(err){self.onError(\"xhr post error\",err)});this.sendXhr=req};XHR.prototype.doPoll=function(){debug(\"xhr poll\");var req=this.request();var self=this;req.on(\"data\",function(data){self.onData(data)});req.on(\"error\",function(err){self.onError(\"xhr poll error\",err)});this.pollXhr=req};function Request(opts){this.method=opts.method||\"GET\";this.uri=opts.uri;this.xd=!!opts.xd;this.xs=!!opts.xs;this.async=false!==opts.async;this.data=undefined!=opts.data?opts.data:null;this.agent=opts.agent;this.isBinary=opts.isBinary;this.supportsBinary=opts.supportsBinary;this.enablesXDR=opts.enablesXDR;this.pfx=opts.pfx;this.key=opts.key;this.passphrase=opts.passphrase;this.cert=opts.cert;this.ca=opts.ca;this.ciphers=opts.ciphers;this.rejectUnauthorized=opts.rejectUnauthorized;this.create()}Emitter(Request.prototype);Request.prototype.create=function(){var opts={agent:this.agent,xdomain:this.xd,xscheme:this.xs,enablesXDR:this.enablesXDR};opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;var xhr=this.xhr=new XMLHttpRequest(opts);var self=this;try{debug(\"xhr open %s: %s\",this.method,this.uri);xhr.open(this.method,this.uri,this.async);if(this.supportsBinary){xhr.responseType=\"arraybuffer\"}if(\"POST\"==this.method){try{if(this.isBinary){xhr.setRequestHeader(\"Content-type\",\"application/octet-stream\")}else{xhr.setRequestHeader(\"Content-type\",\"text/plain;charset=UTF-8\")}}catch(e){}}if(\"withCredentials\"in xhr){xhr.withCredentials=true}if(this.hasXDR()){xhr.onload=function(){self.onLoad()};xhr.onerror=function(){self.onError(xhr.responseText)}}else{xhr.onreadystatechange=function(){if(4!=xhr.readyState)return;if(200==xhr.status||1223==xhr.status){self.onLoad()}else{setTimeout(function(){self.onError(xhr.status)},0)}}}debug(\"xhr data %s\",this.data);xhr.send(this.data)}catch(e){setTimeout(function(){self.onError(e)},0);return}if(global.document){this.index=Request.requestsCount++;Request.requests[this.index]=this}};Request.prototype.onSuccess=function(){this.emit(\"success\");this.cleanup()};Request.prototype.onData=function(data){this.emit(\"data\",data);this.onSuccess()};Request.prototype.onError=function(err){this.emit(\"error\",err);this.cleanup(true)};Request.prototype.cleanup=function(fromError){if(\"undefined\"==typeof this.xhr||null===this.xhr){return}if(this.hasXDR()){this.xhr.onload=this.xhr.onerror=empty}else{this.xhr.onreadystatechange=empty}if(fromError){try{this.xhr.abort()}catch(e){}}if(global.document){delete Request.requests[this.index]}this.xhr=null};Request.prototype.onLoad=function(){var data;try{var contentType;try{contentType=this.xhr.getResponseHeader(\"Content-Type\").split(\";\")[0]}catch(e){}if(contentType===\"application/octet-stream\"){data=this.xhr.response}else{if(!this.supportsBinary){data=this.xhr.responseText}else{data=\"ok\"}}}catch(e){this.onError(e)}if(null!=data){this.onData(data)}};Request.prototype.hasXDR=function(){return\"undefined\"!==typeof global.XDomainRequest&&!this.xs&&this.enablesXDR};Request.prototype.abort=function(){this.cleanup()};if(global.document){Request.requestsCount=0;Request.requests={};if(global.attachEvent){global.attachEvent(\"onunload\",unloadHandler)}else if(global.addEventListener){global.addEventListener(\"beforeunload\",unloadHandler,false)}}function unloadHandler(){for(var i in Request.requests){if(Request.requests.hasOwnProperty(i)){Request.requests[i].abort()}}}}).call(this,typeof self!==\"undefined\"?self:typeof window!==\"undefined\"?window:{})},{\"./polling\":18,\"component-emitter\":9,\"component-inherit\":21,debug:22,xmlhttprequest:20}],18:[function(_dereq_,module,exports){var Transport=_dereq_(\"../transport\");var parseqs=_dereq_(\"parseqs\");var parser=_dereq_(\"engine.io-parser\");var inherit=_dereq_(\"component-inherit\");var debug=_dereq_(\"debug\")(\"engine.io-client:polling\");module.exports=Polling;var hasXHR2=function(){var XMLHttpRequest=_dereq_(\"xmlhttprequest\");var xhr=new XMLHttpRequest({xdomain:false});return null!=xhr.responseType}();function Polling(opts){var forceBase64=opts&&opts.forceBase64;if(!hasXHR2||forceBase64){this.supportsBinary=false}Transport.call(this,opts)}inherit(Polling,Transport);Polling.prototype.name=\"polling\";Polling.prototype.doOpen=function(){this.poll()};Polling.prototype.pause=function(onPause){var pending=0;var self=this;this.readyState=\"pausing\";function pause(){debug(\"paused\");self.readyState=\"paused\";onPause()}if(this.polling||!this.writable){var total=0;if(this.polling){debug(\"we are currently polling - waiting to pause\");total++;this.once(\"pollComplete\",function(){debug(\"pre-pause polling complete\");--total||pause()})}if(!this.writable){debug(\"we are currently writing - waiting to pause\");total++;this.once(\"drain\",function(){debug(\"pre-pause writing complete\");--total||pause()})}}else{pause()}};Polling.prototype.poll=function(){debug(\"polling\");this.polling=true;this.doPoll();this.emit(\"poll\")};Polling.prototype.onData=function(data){var self=this;debug(\"polling got data %s\",data);var callback=function(packet,index,total){if(\"opening\"==self.readyState){self.onOpen()}if(\"close\"==packet.type){self.onClose();return false}self.onPacket(packet)};parser.decodePayload(data,this.socket.binaryType,callback);if(\"closed\"!=this.readyState){this.polling=false;this.emit(\"pollComplete\");if(\"open\"==this.readyState){this.poll()}else{debug('ignoring poll - transport state \"%s\"',this.readyState)}}};Polling.prototype.doClose=function(){var self=this;function close(){debug(\"writing close packet\");self.write([{type:\"close\"}])}if(\"open\"==this.readyState){debug(\"transport open - closing\");close()}else{debug(\"transport not open - deferring close\");this.once(\"open\",close)}};Polling.prototype.write=function(packets){var self=this;this.writable=false;var callbackfn=function(){self.writable=true;self.emit(\"drain\")};var self=this;parser.encodePayload(packets,this.supportsBinary,function(data){self.doWrite(data,callbackfn)})};Polling.prototype.uri=function(){var query=this.query||{};var schema=this.secure?\"https\":\"http\";var port=\"\";if(false!==this.timestampRequests){query[this.timestampParam]=+new Date+\"-\"+Transport.timestamps++}if(!this.supportsBinary&&!query.sid){query.b64=1}query=parseqs.encode(query);if(this.port&&(\"https\"==schema&&this.port!=443||\"http\"==schema&&this.port!=80)){port=\":\"+this.port}if(query.length){query=\"?\"+query}return schema+\"://\"+this.hostname+port+this.path+query}},{\"../transport\":14,\"component-inherit\":21,debug:22,\"engine.io-parser\":25,parseqs:35,xmlhttprequest:20}],19:[function(_dereq_,module,exports){var Transport=_dereq_(\"../transport\");var parser=_dereq_(\"engine.io-parser\");var parseqs=_dereq_(\"parseqs\");var inherit=_dereq_(\"component-inherit\");var debug=_dereq_(\"debug\")(\"engine.io-client:websocket\");var WebSocket=_dereq_(\"ws\");module.exports=WS;function WS(opts){var forceBase64=opts&&opts.forceBase64;if(forceBase64){this.supportsBinary=false}this.perMessageDeflate=opts.perMessageDeflate;Transport.call(this,opts)}inherit(WS,Transport);WS.prototype.name=\"websocket\";WS.prototype.supportsBinary=true;WS.prototype.doOpen=function(){if(!this.check()){return}var self=this;var uri=this.uri();var protocols=void 0;var opts={agent:this.agent,perMessageDeflate:this.perMessageDeflate};opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;this.ws=new WebSocket(uri,protocols,opts);if(this.ws.binaryType===undefined){this.supportsBinary=false}this.ws.binaryType=\"arraybuffer\";this.addEventListeners()};WS.prototype.addEventListeners=function(){var self=this;this.ws.onopen=function(){self.onOpen()};this.ws.onclose=function(){self.onClose()};this.ws.onmessage=function(ev){self.onData(ev.data)};this.ws.onerror=function(e){self.onError(\"websocket error\",e)}};if(\"undefined\"!=typeof navigator&&/iPad|iPhone|iPod/i.test(navigator.userAgent)){WS.prototype.onData=function(data){var self=this;setTimeout(function(){Transport.prototype.onData.call(self,data)},0)}}WS.prototype.write=function(packets){var self=this;this.writable=false;for(var i=0,l=packets.length;i<l;i++){var packet=packets[i];parser.encodePacket(packet,this.supportsBinary,function(data){try{self.ws.send(data,packet.options)}catch(e){debug(\"websocket closed before onclose event\")}})}function ondrain(){self.writable=true;self.emit(\"drain\")}setTimeout(ondrain,0)};WS.prototype.onClose=function(){Transport.prototype.onClose.call(this)};WS.prototype.doClose=function(){if(typeof this.ws!==\"undefined\"){this.ws.close()}};WS.prototype.uri=function(){var query=this.query||{};var schema=this.secure?\"wss\":\"ws\";var port=\"\";if(this.port&&(\"wss\"==schema&&this.port!=443||\"ws\"==schema&&this.port!=80)){port=\":\"+this.port}if(this.timestampRequests){query[this.timestampParam]=+new Date}if(!this.supportsBinary){query.b64=1}query=parseqs.encode(query);if(query.length){query=\"?\"+query}return schema+\"://\"+this.hostname+port+this.path+query};WS.prototype.check=function(){return!!WebSocket&&!(\"__initialize\"in WebSocket&&this.name===WS.prototype.name)}},{\"../transport\":14,\"component-inherit\":21,debug:22,\"engine.io-parser\":25,parseqs:35,ws:37}],20:[function(_dereq_,module,exports){var hasCORS=_dereq_(\"has-cors\");module.exports=function(opts){var xdomain=opts.xdomain;var xscheme=opts.xscheme;var enablesXDR=opts.enablesXDR;try{if(\"undefined\"!=typeof XMLHttpRequest&&(!xdomain||hasCORS)){return new XMLHttpRequest}}catch(e){}try{if(\"undefined\"!=typeof XDomainRequest&&!xscheme&&enablesXDR){return new XDomainRequest}}catch(e){}if(!xdomain){try{return new ActiveXObject(\"Microsoft.XMLHTTP\")}catch(e){}}}},{\"has-cors\":40}],21:[function(_dereq_,module,exports){module.exports=function(a,b){var fn=function(){};fn.prototype=b.prototype;a.prototype=new fn;a.prototype.constructor=a}},{}],22:[function(_dereq_,module,exports){exports=module.exports=_dereq_(\"./debug\");exports.log=log;exports.formatArgs=formatArgs;exports.save=save;exports.load=load;exports.useColors=useColors;exports.colors=[\"lightseagreen\",\"forestgreen\",\"goldenrod\",\"dodgerblue\",\"darkorchid\",\"crimson\"];function useColors(){return\"WebkitAppearance\"in document.documentElement.style||window.console&&(console.firebug||console.exception&&console.table)||navigator.userAgent.toLowerCase().match(/firefox\\/(\\d+)/)&&parseInt(RegExp.$1,10)>=31}exports.formatters.j=function(v){return JSON.stringify(v)};function formatArgs(){var args=arguments;var useColors=this.useColors;args[0]=(useColors?\"%c\":\"\")+this.namespace+(useColors?\" %c\":\" \")+args[0]+(useColors?\"%c \":\" \")+\"+\"+exports.humanize(this.diff);if(!useColors)return args;var c=\"color: \"+this.color;args=[args[0],c,\"color: inherit\"].concat(Array.prototype.slice.call(args,1));var index=0;var lastC=0;args[0].replace(/%[a-z%]/g,function(match){if(\"%\"===match)return;index++;if(\"%c\"===match){lastC=index}});args.splice(lastC,0,c);return args}function log(){return\"object\"==typeof console&&\"function\"==typeof console.log&&Function.prototype.apply.call(console.log,console,arguments)}function save(namespaces){try{if(null==namespaces){localStorage.removeItem(\"debug\")}else{localStorage.debug=namespaces}}catch(e){}}function load(){var r;try{r=localStorage.debug}catch(e){}return r}exports.enable(load())},{\"./debug\":23}],23:[function(_dereq_,module,exports){exports=module.exports=debug;exports.coerce=coerce;exports.disable=disable;exports.enable=enable;exports.enabled=enabled;exports.humanize=_dereq_(\"ms\");exports.names=[];exports.skips=[];exports.formatters={};var prevColor=0;var prevTime;function selectColor(){return exports.colors[prevColor++%exports.colors.length]}function debug(namespace){function disabled(){}disabled.enabled=false;function enabled(){var self=enabled;var curr=+new Date;var ms=curr-(prevTime||curr);self.diff=ms;self.prev=prevTime;self.curr=curr;prevTime=curr;if(null==self.useColors)self.useColors=exports.useColors();if(null==self.color&&self.useColors)self.color=selectColor();var args=Array.prototype.slice.call(arguments);args[0]=exports.coerce(args[0]);if(\"string\"!==typeof args[0]){args=[\"%o\"].concat(args)}var index=0;args[0]=args[0].replace(/%([a-z%])/g,function(match,format){if(match===\"%\")return match;index++;var formatter=exports.formatters[format];if(\"function\"===typeof formatter){var val=args[index];match=formatter.call(self,val);args.splice(index,1);index--}return match});if(\"function\"===typeof exports.formatArgs){args=exports.formatArgs.apply(self,args)}var logFn=enabled.log||exports.log||console.log.bind(console);logFn.apply(self,args)}enabled.enabled=true;var fn=exports.enabled(namespace)?enabled:disabled;fn.namespace=namespace;return fn}function enable(namespaces){exports.save(namespaces);var split=(namespaces||\"\").split(/[\\s,]+/);var len=split.length;for(var i=0;i<len;i++){if(!split[i])continue;namespaces=split[i].replace(/\\*/g,\".*?\");if(namespaces[0]===\"-\"){exports.skips.push(new RegExp(\"^\"+namespaces.substr(1)+\"$\"))}else{exports.names.push(new RegExp(\"^\"+namespaces+\"$\"))}}}function disable(){exports.enable(\"\")}function enabled(name){var i,len;for(i=0,len=exports.skips.length;i<len;i++){if(exports.skips[i].test(name)){return false}}for(i=0,len=exports.names.length;i<len;i++){if(exports.names[i].test(name)){return true}}return false}function coerce(val){if(val instanceof Error)return val.stack||val.message;return val}},{ms:24}],24:[function(_dereq_,module,exports){var s=1e3;var m=s*60;var h=m*60;var d=h*24;var y=d*365.25;module.exports=function(val,options){options=options||{};if(\"string\"==typeof val)return parse(val);return options.long?long(val):short(val)};function parse(str){var match=/^((?:\\d+)?\\.?\\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str);if(!match)return;var n=parseFloat(match[1]);var type=(match[2]||\"ms\").toLowerCase();switch(type){case\"years\":case\"year\":case\"y\":return n*y;case\"days\":case\"day\":case\"d\":return n*d;case\"hours\":case\"hour\":case\"h\":return n*h;case\"minutes\":case\"minute\":case\"m\":return n*m;case\"seconds\":case\"second\":case\"s\":return n*s;case\"ms\":return n}}function short(ms){if(ms>=d)return Math.round(ms/d)+\"d\";if(ms>=h)return Math.round(ms/h)+\"h\";if(ms>=m)return Math.round(ms/m)+\"m\";if(ms>=s)return Math.round(ms/s)+\"s\";return ms+\"ms\"}function long(ms){return plural(ms,d,\"day\")||plural(ms,h,\"hour\")||plural(ms,m,\"minute\")||plural(ms,s,\"second\")||ms+\" ms\"}function plural(ms,n,name){if(ms<n)return;if(ms<n*1.5)return Math.floor(ms/n)+\" \"+name;return Math.ceil(ms/n)+\" \"+name+\"s\"}},{}],25:[function(_dereq_,module,exports){(function(global){var keys=_dereq_(\"./keys\");var hasBinary=_dereq_(\"has-binary\");var sliceBuffer=_dereq_(\"arraybuffer.slice\");var base64encoder=_dereq_(\"base64-arraybuffer\");var after=_dereq_(\"after\");var utf8=_dereq_(\"utf8\");var isAndroid=navigator.userAgent.match(/Android/i);var isPhantomJS=/PhantomJS/i.test(navigator.userAgent);var dontSendBlobs=isAndroid||isPhantomJS;exports.protocol=3;var packets=exports.packets={open:0,close:1,ping:2,pong:3,message:4,upgrade:5,noop:6};var packetslist=keys(packets);var err={type:\"error\",data:\"parser error\"};var Blob=_dereq_(\"blob\");exports.encodePacket=function(packet,supportsBinary,utf8encode,callback){if(\"function\"==typeof supportsBinary){callback=supportsBinary;supportsBinary=false}if(\"function\"==typeof utf8encode){callback=utf8encode;utf8encode=null}var data=packet.data===undefined?undefined:packet.data.buffer||packet.data;if(global.ArrayBuffer&&data instanceof ArrayBuffer){return encodeArrayBuffer(packet,supportsBinary,callback)}else if(Blob&&data instanceof global.Blob){return encodeBlob(packet,supportsBinary,callback)}if(data&&data.base64){return encodeBase64Object(packet,callback)}var encoded=packets[packet.type];if(undefined!==packet.data){encoded+=utf8encode?utf8.encode(String(packet.data)):String(packet.data)}return callback(\"\"+encoded)};function encodeBase64Object(packet,callback){var message=\"b\"+exports.packets[packet.type]+packet.data.data;return callback(message)}function encodeArrayBuffer(packet,supportsBinary,callback){if(!supportsBinary){return exports.encodeBase64Packet(packet,callback)}var data=packet.data;var contentArray=new Uint8Array(data);var resultBuffer=new Uint8Array(1+data.byteLength);resultBuffer[0]=packets[packet.type];for(var i=0;i<contentArray.length;i++){resultBuffer[i+1]=contentArray[i]}return callback(resultBuffer.buffer)}function encodeBlobAsArrayBuffer(packet,supportsBinary,callback){if(!supportsBinary){return exports.encodeBase64Packet(packet,callback)}var fr=new FileReader;fr.onload=function(){packet.data=fr.result;exports.encodePacket(packet,supportsBinary,true,callback)};return fr.readAsArrayBuffer(packet.data)}function encodeBlob(packet,supportsBinary,callback){if(!supportsBinary){return exports.encodeBase64Packet(packet,callback)}if(dontSendBlobs){return encodeBlobAsArrayBuffer(packet,supportsBinary,callback)}var length=new Uint8Array(1);length[0]=packets[packet.type];var blob=new Blob([length.buffer,packet.data]);return callback(blob)}exports.encodeBase64Packet=function(packet,callback){var message=\"b\"+exports.packets[packet.type];if(Blob&&packet.data instanceof Blob){var fr=new FileReader;fr.onload=function(){var b64=fr.result.split(\",\")[1];callback(message+b64)};return fr.readAsDataURL(packet.data)}var b64data;try{b64data=String.fromCharCode.apply(null,new Uint8Array(packet.data))}catch(e){var typed=new Uint8Array(packet.data);var basic=new Array(typed.length);for(var i=0;i<typed.length;i++){basic[i]=typed[i]}b64data=String.fromCharCode.apply(null,basic)}message+=global.btoa(b64data);return callback(message)};exports.decodePacket=function(data,binaryType,utf8decode){if(typeof data==\"string\"||data===undefined){if(data.charAt(0)==\"b\"){return exports.decodeBase64Packet(data.substr(1),binaryType)}if(utf8decode){try{data=utf8.decode(data)}catch(e){return err}}var type=data.charAt(0);if(Number(type)!=type||!packetslist[type]){return err}if(data.length>1){return{type:packetslist[type],data:data.substring(1)}}else{return{type:packetslist[type]}}}var asArray=new Uint8Array(data);var type=asArray[0];var rest=sliceBuffer(data,1);if(Blob&&binaryType===\"blob\"){rest=new Blob([rest])}return{type:packetslist[type],data:rest}};exports.decodeBase64Packet=function(msg,binaryType){var type=packetslist[msg.charAt(0)];if(!global.ArrayBuffer){return{type:type,data:{base64:true,data:msg.substr(1)}}}var data=base64encoder.decode(msg.substr(1));if(binaryType===\"blob\"&&Blob){data=new Blob([data])}return{type:type,data:data}};exports.encodePayload=function(packets,supportsBinary,callback){if(typeof supportsBinary==\"function\"){callback=supportsBinary;supportsBinary=null}var isBinary=hasBinary(packets);if(supportsBinary&&isBinary){if(Blob&&!dontSendBlobs){return exports.encodePayloadAsBlob(packets,callback)}return exports.encodePayloadAsArrayBuffer(packets,callback)}if(!packets.length){return callback(\"0:\")}function setLengthHeader(message){return message.length+\":\"+message}function encodeOne(packet,doneCallback){exports.encodePacket(packet,!isBinary?false:supportsBinary,true,function(message){doneCallback(null,setLengthHeader(message))})}map(packets,encodeOne,function(err,results){return callback(results.join(\"\"))})};function map(ary,each,done){var result=new Array(ary.length);var next=after(ary.length,done);var eachWithIndex=function(i,el,cb){each(el,function(error,msg){result[i]=msg;cb(error,result)})};for(var i=0;i<ary.length;i++){eachWithIndex(i,ary[i],next)}}exports.decodePayload=function(data,binaryType,callback){if(typeof data!=\"string\"){return exports.decodePayloadAsBinary(data,binaryType,callback)}if(typeof binaryType===\"function\"){callback=binaryType;binaryType=null}var packet;if(data==\"\"){return callback(err,0,1)}var length=\"\",n,msg;for(var i=0,l=data.length;i<l;i++){var chr=data.charAt(i);if(\":\"!=chr){length+=chr}else{if(\"\"==length||length!=(n=Number(length))){return callback(err,0,1)}msg=data.substr(i+1,n);if(length!=msg.length){return callback(err,0,1)}if(msg.length){packet=exports.decodePacket(msg,binaryType,true);if(err.type==packet.type&&err.data==packet.data){return callback(err,0,1)}var ret=callback(packet,i+n,l);if(false===ret)return}i+=n;length=\"\"}}if(length!=\"\"){return callback(err,0,1)}};exports.encodePayloadAsArrayBuffer=function(packets,callback){if(!packets.length){return callback(new ArrayBuffer(0))}function encodeOne(packet,doneCallback){exports.encodePacket(packet,true,true,function(data){return doneCallback(null,data)})}map(packets,encodeOne,function(err,encodedPackets){var totalLength=encodedPackets.reduce(function(acc,p){var len;if(typeof p===\"string\"){len=p.length}else{len=p.byteLength}return acc+len.toString().length+len+2},0);var resultArray=new Uint8Array(totalLength);var bufferIndex=0;encodedPackets.forEach(function(p){var isString=typeof p===\"string\";var ab=p;if(isString){var view=new Uint8Array(p.length);for(var i=0;i<p.length;i++){view[i]=p.charCodeAt(i)}ab=view.buffer}if(isString){resultArray[bufferIndex++]=0}else{resultArray[bufferIndex++]=1}var lenStr=ab.byteLength.toString();for(var i=0;i<lenStr.length;i++){resultArray[bufferIndex++]=parseInt(lenStr[i])}resultArray[bufferIndex++]=255;var view=new Uint8Array(ab);for(var i=0;i<view.length;i++){resultArray[bufferIndex++]=view[i]}});return callback(resultArray.buffer)})};exports.encodePayloadAsBlob=function(packets,callback){function encodeOne(packet,doneCallback){exports.encodePacket(packet,true,true,function(encoded){var binaryIdentifier=new Uint8Array(1);binaryIdentifier[0]=1;if(typeof encoded===\"string\"){var view=new Uint8Array(encoded.length);for(var i=0;i<encoded.length;i++){view[i]=encoded.charCodeAt(i)}encoded=view.buffer;binaryIdentifier[0]=0}var len=encoded instanceof ArrayBuffer?encoded.byteLength:encoded.size;var lenStr=len.toString();var lengthAry=new Uint8Array(lenStr.length+1);for(var i=0;i<lenStr.length;i++){lengthAry[i]=parseInt(lenStr[i])}lengthAry[lenStr.length]=255;if(Blob){var blob=new Blob([binaryIdentifier.buffer,lengthAry.buffer,encoded]);doneCallback(null,blob)}})}map(packets,encodeOne,function(err,results){return callback(new Blob(results))})};exports.decodePayloadAsBinary=function(data,binaryType,callback){if(typeof binaryType===\"function\"){callback=binaryType;binaryType=null}var bufferTail=data;var buffers=[];var numberTooLong=false;while(bufferTail.byteLength>0){var tailArray=new Uint8Array(bufferTail);var isString=tailArray[0]===0;var msgLength=\"\";for(var i=1;;i++){if(tailArray[i]==255)break;if(msgLength.length>310){numberTooLong=true;break}msgLength+=tailArray[i]}if(numberTooLong)return callback(err,0,1);bufferTail=sliceBuffer(bufferTail,2+msgLength.length);msgLength=parseInt(msgLength);var msg=sliceBuffer(bufferTail,0,msgLength);if(isString){try{msg=String.fromCharCode.apply(null,new Uint8Array(msg))}catch(e){var typed=new Uint8Array(msg);msg=\"\";for(var i=0;i<typed.length;i++){msg+=String.fromCharCode(typed[i])}}}buffers.push(msg);bufferTail=sliceBuffer(bufferTail,msgLength)}var total=buffers.length;buffers.forEach(function(buffer,i){callback(exports.decodePacket(buffer,binaryType,true),i,total)})}}).call(this,typeof self!==\"undefined\"?self:typeof window!==\"undefined\"?window:{})},{\"./keys\":26,after:27,\"arraybuffer.slice\":28,\"base64-arraybuffer\":29,blob:30,\"has-binary\":31,utf8:33}],26:[function(_dereq_,module,exports){module.exports=Object.keys||function keys(obj){var arr=[];var has=Object.prototype.hasOwnProperty;for(var i in obj){if(has.call(obj,i)){arr.push(i)}}return arr}},{}],27:[function(_dereq_,module,exports){module.exports=after;function after(count,callback,err_cb){var bail=false;err_cb=err_cb||noop;proxy.count=count;return count===0?callback():proxy;function proxy(err,result){if(proxy.count<=0){throw new Error(\"after called too many times\")}--proxy.count;if(err){bail=true;callback(err);callback=err_cb}else if(proxy.count===0&&!bail){callback(null,result)}}}function noop(){}},{}],28:[function(_dereq_,module,exports){module.exports=function(arraybuffer,start,end){var bytes=arraybuffer.byteLength;start=start||0;end=end||bytes;if(arraybuffer.slice){return arraybuffer.slice(start,end)}if(start<0){start+=bytes}if(end<0){end+=bytes}if(end>bytes){end=bytes}if(start>=bytes||start>=end||bytes===0){return new ArrayBuffer(0)}var abv=new Uint8Array(arraybuffer);var result=new Uint8Array(end-start);for(var i=start,ii=0;i<end;i++,ii++){result[ii]=abv[i]}return result.buffer}},{}],29:[function(_dereq_,module,exports){(function(chars){\"use strict\";exports.encode=function(arraybuffer){var bytes=new Uint8Array(arraybuffer),i,len=bytes.length,base64=\"\";for(i=0;i<len;i+=3){base64+=chars[bytes[i]>>2];base64+=chars[(bytes[i]&3)<<4|bytes[i+1]>>4];base64+=chars[(bytes[i+1]&15)<<2|bytes[i+2]>>6];base64+=chars[bytes[i+2]&63]}if(len%3===2){base64=base64.substring(0,base64.length-1)+\"=\"}else if(len%3===1){base64=base64.substring(0,base64.length-2)+\"==\"}return base64};exports.decode=function(base64){var bufferLength=base64.length*.75,len=base64.length,i,p=0,encoded1,encoded2,encoded3,encoded4;if(base64[base64.length-1]===\"=\"){bufferLength--;if(base64[base64.length-2]===\"=\"){bufferLength--}}var arraybuffer=new ArrayBuffer(bufferLength),bytes=new Uint8Array(arraybuffer);for(i=0;i<len;i+=4){encoded1=chars.indexOf(base64[i]);encoded2=chars.indexOf(base64[i+1]);encoded3=chars.indexOf(base64[i+2]);encoded4=chars.indexOf(base64[i+3]);bytes[p++]=encoded1<<2|encoded2>>4;bytes[p++]=(encoded2&15)<<4|encoded3>>2;bytes[p++]=(encoded3&3)<<6|encoded4&63}return arraybuffer}})(\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\")},{}],30:[function(_dereq_,module,exports){(function(global){var BlobBuilder=global.BlobBuilder||global.WebKitBlobBuilder||global.MSBlobBuilder||global.MozBlobBuilder;var blobSupported=function(){try{var b=new Blob([\"hi\"]);return b.size==2}catch(e){return false}}();var blobBuilderSupported=BlobBuilder&&BlobBuilder.prototype.append&&BlobBuilder.prototype.getBlob;function BlobBuilderConstructor(ary,options){options=options||{};var bb=new BlobBuilder;for(var i=0;i<ary.length;i++){bb.append(ary[i])}return options.type?bb.getBlob(options.type):bb.getBlob()}module.exports=function(){if(blobSupported){return global.Blob}else if(blobBuilderSupported){return BlobBuilderConstructor}else{return undefined}}()}).call(this,typeof self!==\"undefined\"?self:typeof window!==\"undefined\"?window:{})},{}],31:[function(_dereq_,module,exports){(function(global){var isArray=_dereq_(\"isarray\");module.exports=hasBinary;function hasBinary(data){function _hasBinary(obj){if(!obj)return false;if(global.Buffer&&global.Buffer.isBuffer(obj)||global.ArrayBuffer&&obj instanceof ArrayBuffer||global.Blob&&obj instanceof Blob||global.File&&obj instanceof File){return true}if(isArray(obj)){for(var i=0;i<obj.length;i++){if(_hasBinary(obj[i])){return true}}}else if(obj&&\"object\"==typeof obj){if(obj.toJSON){obj=obj.toJSON()}for(var key in obj){if(obj.hasOwnProperty(key)&&_hasBinary(obj[key])){return true}}}return false}return _hasBinary(data)}}).call(this,typeof self!==\"undefined\"?self:typeof window!==\"undefined\"?window:{})},{isarray:32}],32:[function(_dereq_,module,exports){module.exports=Array.isArray||function(arr){return Object.prototype.toString.call(arr)==\"[object Array]\"}},{}],33:[function(_dereq_,module,exports){(function(global){(function(root){var freeExports=typeof exports==\"object\"&&exports;var freeModule=typeof module==\"object\"&&module&&module.exports==freeExports&&module;var freeGlobal=typeof global==\"object\"&&global;if(freeGlobal.global===freeGlobal||freeGlobal.window===freeGlobal){root=freeGlobal}var stringFromCharCode=String.fromCharCode;function ucs2decode(string){var output=[];var counter=0;var length=string.length;var value;var extra;while(counter<length){value=string.charCodeAt(counter++);if(value>=55296&&value<=56319&&counter<length){extra=string.charCodeAt(counter++);\nif((extra&64512)==56320){output.push(((value&1023)<<10)+(extra&1023)+65536)}else{output.push(value);counter--}}else{output.push(value)}}return output}function ucs2encode(array){var length=array.length;var index=-1;var value;var output=\"\";while(++index<length){value=array[index];if(value>65535){value-=65536;output+=stringFromCharCode(value>>>10&1023|55296);value=56320|value&1023}output+=stringFromCharCode(value)}return output}function createByte(codePoint,shift){return stringFromCharCode(codePoint>>shift&63|128)}function encodeCodePoint(codePoint){if((codePoint&4294967168)==0){return stringFromCharCode(codePoint)}var symbol=\"\";if((codePoint&4294965248)==0){symbol=stringFromCharCode(codePoint>>6&31|192)}else if((codePoint&4294901760)==0){symbol=stringFromCharCode(codePoint>>12&15|224);symbol+=createByte(codePoint,6)}else if((codePoint&4292870144)==0){symbol=stringFromCharCode(codePoint>>18&7|240);symbol+=createByte(codePoint,12);symbol+=createByte(codePoint,6)}symbol+=stringFromCharCode(codePoint&63|128);return symbol}function utf8encode(string){var codePoints=ucs2decode(string);var length=codePoints.length;var index=-1;var codePoint;var byteString=\"\";while(++index<length){codePoint=codePoints[index];byteString+=encodeCodePoint(codePoint)}return byteString}function readContinuationByte(){if(byteIndex>=byteCount){throw Error(\"Invalid byte index\")}var continuationByte=byteArray[byteIndex]&255;byteIndex++;if((continuationByte&192)==128){return continuationByte&63}throw Error(\"Invalid continuation byte\")}function decodeSymbol(){var byte1;var byte2;var byte3;var byte4;var codePoint;if(byteIndex>byteCount){throw Error(\"Invalid byte index\")}if(byteIndex==byteCount){return false}byte1=byteArray[byteIndex]&255;byteIndex++;if((byte1&128)==0){return byte1}if((byte1&224)==192){var byte2=readContinuationByte();codePoint=(byte1&31)<<6|byte2;if(codePoint>=128){return codePoint}else{throw Error(\"Invalid continuation byte\")}}if((byte1&240)==224){byte2=readContinuationByte();byte3=readContinuationByte();codePoint=(byte1&15)<<12|byte2<<6|byte3;if(codePoint>=2048){return codePoint}else{throw Error(\"Invalid continuation byte\")}}if((byte1&248)==240){byte2=readContinuationByte();byte3=readContinuationByte();byte4=readContinuationByte();codePoint=(byte1&15)<<18|byte2<<12|byte3<<6|byte4;if(codePoint>=65536&&codePoint<=1114111){return codePoint}}throw Error(\"Invalid UTF-8 detected\")}var byteArray;var byteCount;var byteIndex;function utf8decode(byteString){byteArray=ucs2decode(byteString);byteCount=byteArray.length;byteIndex=0;var codePoints=[];var tmp;while((tmp=decodeSymbol())!==false){codePoints.push(tmp)}return ucs2encode(codePoints)}var utf8={version:\"2.0.0\",encode:utf8encode,decode:utf8decode};if(typeof define==\"function\"&&typeof define.amd==\"object\"&&define.amd){define(function(){return utf8})}else if(freeExports&&!freeExports.nodeType){if(freeModule){freeModule.exports=utf8}else{var object={};var hasOwnProperty=object.hasOwnProperty;for(var key in utf8){hasOwnProperty.call(utf8,key)&&(freeExports[key]=utf8[key])}}}else{root.utf8=utf8}})(this)}).call(this,typeof self!==\"undefined\"?self:typeof window!==\"undefined\"?window:{})},{}],34:[function(_dereq_,module,exports){(function(global){var rvalidchars=/^[\\],:{}\\s]*$/;var rvalidescape=/\\\\(?:[\"\\\\\\/bfnrt]|u[0-9a-fA-F]{4})/g;var rvalidtokens=/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g;var rvalidbraces=/(?:^|:|,)(?:\\s*\\[)+/g;var rtrimLeft=/^\\s+/;var rtrimRight=/\\s+$/;module.exports=function parsejson(data){if(\"string\"!=typeof data||!data){return null}data=data.replace(rtrimLeft,\"\").replace(rtrimRight,\"\");if(global.JSON&&JSON.parse){return JSON.parse(data)}if(rvalidchars.test(data.replace(rvalidescape,\"@\").replace(rvalidtokens,\"]\").replace(rvalidbraces,\"\"))){return new Function(\"return \"+data)()}}}).call(this,typeof self!==\"undefined\"?self:typeof window!==\"undefined\"?window:{})},{}],35:[function(_dereq_,module,exports){exports.encode=function(obj){var str=\"\";for(var i in obj){if(obj.hasOwnProperty(i)){if(str.length)str+=\"&\";str+=encodeURIComponent(i)+\"=\"+encodeURIComponent(obj[i])}}return str};exports.decode=function(qs){var qry={};var pairs=qs.split(\"&\");for(var i=0,l=pairs.length;i<l;i++){var pair=pairs[i].split(\"=\");qry[decodeURIComponent(pair[0])]=decodeURIComponent(pair[1])}return qry}},{}],36:[function(_dereq_,module,exports){var re=/^(?:(?![^:@]+:[^:@\\/]*@)(http|https|ws|wss):\\/\\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\\/?#]*)(?::(\\d*))?)(((\\/(?:[^?#](?![^?#\\/]*\\.[^?#\\/.]+(?:[?#]|$)))*\\/?)?([^?#\\/]*))(?:\\?([^#]*))?(?:#(.*))?)/;var parts=[\"source\",\"protocol\",\"authority\",\"userInfo\",\"user\",\"password\",\"host\",\"port\",\"relative\",\"path\",\"directory\",\"file\",\"query\",\"anchor\"];module.exports=function parseuri(str){var src=str,b=str.indexOf(\"[\"),e=str.indexOf(\"]\");if(b!=-1&&e!=-1){str=str.substring(0,b)+str.substring(b,e).replace(/:/g,\";\")+str.substring(e,str.length)}var m=re.exec(str||\"\"),uri={},i=14;while(i--){uri[parts[i]]=m[i]||\"\"}if(b!=-1&&e!=-1){uri.source=src;uri.host=uri.host.substring(1,uri.host.length-1).replace(/;/g,\":\");uri.authority=uri.authority.replace(\"[\",\"\").replace(\"]\",\"\").replace(/;/g,\":\");uri.ipv6uri=true}return uri}},{}],37:[function(_dereq_,module,exports){var global=function(){return this}();var WebSocket=global.WebSocket||global.MozWebSocket;module.exports=WebSocket?ws:null;function ws(uri,protocols,opts){var instance;if(protocols){instance=new WebSocket(uri,protocols)}else{instance=new WebSocket(uri)}return instance}if(WebSocket)ws.prototype=WebSocket.prototype},{}],38:[function(_dereq_,module,exports){(function(global){var isArray=_dereq_(\"isarray\");module.exports=hasBinary;function hasBinary(data){function _hasBinary(obj){if(!obj)return false;if(global.Buffer&&global.Buffer.isBuffer(obj)||global.ArrayBuffer&&obj instanceof ArrayBuffer||global.Blob&&obj instanceof Blob||global.File&&obj instanceof File){return true}if(isArray(obj)){for(var i=0;i<obj.length;i++){if(_hasBinary(obj[i])){return true}}}else if(obj&&\"object\"==typeof obj){if(obj.toJSON){obj=obj.toJSON()}for(var key in obj){if(Object.prototype.hasOwnProperty.call(obj,key)&&_hasBinary(obj[key])){return true}}}return false}return _hasBinary(data)}}).call(this,typeof self!==\"undefined\"?self:typeof window!==\"undefined\"?window:{})},{isarray:39}],39:[function(_dereq_,module,exports){module.exports=_dereq_(32)},{}],40:[function(_dereq_,module,exports){var global=_dereq_(\"global\");try{module.exports=\"XMLHttpRequest\"in global&&\"withCredentials\"in new global.XMLHttpRequest}catch(err){module.exports=false}},{global:41}],41:[function(_dereq_,module,exports){module.exports=function(){return this}()},{}],42:[function(_dereq_,module,exports){var indexOf=[].indexOf;module.exports=function(arr,obj){if(indexOf)return arr.indexOf(obj);for(var i=0;i<arr.length;++i){if(arr[i]===obj)return i}return-1}},{}],43:[function(_dereq_,module,exports){var has=Object.prototype.hasOwnProperty;exports.keys=Object.keys||function(obj){var keys=[];for(var key in obj){if(has.call(obj,key)){keys.push(key)}}return keys};exports.values=function(obj){var vals=[];for(var key in obj){if(has.call(obj,key)){vals.push(obj[key])}}return vals};exports.merge=function(a,b){for(var key in b){if(has.call(b,key)){a[key]=b[key]}}return a};exports.length=function(obj){return exports.keys(obj).length};exports.isEmpty=function(obj){return 0==exports.length(obj)}},{}],44:[function(_dereq_,module,exports){var re=/^(?:(?![^:@]+:[^:@\\/]*@)(http|https|ws|wss):\\/\\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\\/?#]*)(?::(\\d*))?)(((\\/(?:[^?#](?![^?#\\/]*\\.[^?#\\/.]+(?:[?#]|$)))*\\/?)?([^?#\\/]*))(?:\\?([^#]*))?(?:#(.*))?)/;var parts=[\"source\",\"protocol\",\"authority\",\"userInfo\",\"user\",\"password\",\"host\",\"port\",\"relative\",\"path\",\"directory\",\"file\",\"query\",\"anchor\"];module.exports=function parseuri(str){var m=re.exec(str||\"\"),uri={},i=14;while(i--){uri[parts[i]]=m[i]||\"\"}return uri}},{}],45:[function(_dereq_,module,exports){(function(global){var isArray=_dereq_(\"isarray\");var isBuf=_dereq_(\"./is-buffer\");exports.deconstructPacket=function(packet){var buffers=[];var packetData=packet.data;function _deconstructPacket(data){if(!data)return data;if(isBuf(data)){var placeholder={_placeholder:true,num:buffers.length};buffers.push(data);return placeholder}else if(isArray(data)){var newData=new Array(data.length);for(var i=0;i<data.length;i++){newData[i]=_deconstructPacket(data[i])}return newData}else if(\"object\"==typeof data&&!(data instanceof Date)){var newData={};for(var key in data){newData[key]=_deconstructPacket(data[key])}return newData}return data}var pack=packet;pack.data=_deconstructPacket(packetData);pack.attachments=buffers.length;return{packet:pack,buffers:buffers}};exports.reconstructPacket=function(packet,buffers){var curPlaceHolder=0;function _reconstructPacket(data){if(data&&data._placeholder){var buf=buffers[data.num];return buf}else if(isArray(data)){for(var i=0;i<data.length;i++){data[i]=_reconstructPacket(data[i])}return data}else if(data&&\"object\"==typeof data){for(var key in data){data[key]=_reconstructPacket(data[key])}return data}return data}packet.data=_reconstructPacket(packet.data);packet.attachments=undefined;return packet};exports.removeBlobs=function(data,callback){function _removeBlobs(obj,curKey,containingObject){if(!obj)return obj;if(global.Blob&&obj instanceof Blob||global.File&&obj instanceof File){pendingBlobs++;var fileReader=new FileReader;fileReader.onload=function(){if(containingObject){containingObject[curKey]=this.result}else{bloblessData=this.result}if(!--pendingBlobs){callback(bloblessData)}};fileReader.readAsArrayBuffer(obj)}else if(isArray(obj)){for(var i=0;i<obj.length;i++){_removeBlobs(obj[i],i,obj)}}else if(obj&&\"object\"==typeof obj&&!isBuf(obj)){for(var key in obj){_removeBlobs(obj[key],key,obj)}}}var pendingBlobs=0;var bloblessData=data;_removeBlobs(bloblessData);if(!pendingBlobs){callback(bloblessData)}}}).call(this,typeof self!==\"undefined\"?self:typeof window!==\"undefined\"?window:{})},{\"./is-buffer\":47,isarray:48}],46:[function(_dereq_,module,exports){var debug=_dereq_(\"debug\")(\"socket.io-parser\");var json=_dereq_(\"json3\");var isArray=_dereq_(\"isarray\");var Emitter=_dereq_(\"component-emitter\");var binary=_dereq_(\"./binary\");var isBuf=_dereq_(\"./is-buffer\");exports.protocol=4;exports.types=[\"CONNECT\",\"DISCONNECT\",\"EVENT\",\"BINARY_EVENT\",\"ACK\",\"BINARY_ACK\",\"ERROR\"];exports.CONNECT=0;exports.DISCONNECT=1;exports.EVENT=2;exports.ACK=3;exports.ERROR=4;exports.BINARY_EVENT=5;exports.BINARY_ACK=6;exports.Encoder=Encoder;exports.Decoder=Decoder;function Encoder(){}Encoder.prototype.encode=function(obj,callback){debug(\"encoding packet %j\",obj);if(exports.BINARY_EVENT==obj.type||exports.BINARY_ACK==obj.type){encodeAsBinary(obj,callback)}else{var encoding=encodeAsString(obj);callback([encoding])}};function encodeAsString(obj){var str=\"\";var nsp=false;str+=obj.type;if(exports.BINARY_EVENT==obj.type||exports.BINARY_ACK==obj.type){str+=obj.attachments;str+=\"-\"}if(obj.nsp&&\"/\"!=obj.nsp){nsp=true;str+=obj.nsp}if(null!=obj.id){if(nsp){str+=\",\";nsp=false}str+=obj.id}if(null!=obj.data){if(nsp)str+=\",\";str+=json.stringify(obj.data)}debug(\"encoded %j as %s\",obj,str);return str}function encodeAsBinary(obj,callback){function writeEncoding(bloblessData){var deconstruction=binary.deconstructPacket(bloblessData);var pack=encodeAsString(deconstruction.packet);var buffers=deconstruction.buffers;buffers.unshift(pack);callback(buffers)}binary.removeBlobs(obj,writeEncoding)}function Decoder(){this.reconstructor=null}Emitter(Decoder.prototype);Decoder.prototype.add=function(obj){var packet;if(\"string\"==typeof obj){packet=decodeString(obj);if(exports.BINARY_EVENT==packet.type||exports.BINARY_ACK==packet.type){this.reconstructor=new BinaryReconstructor(packet);if(this.reconstructor.reconPack.attachments===0){this.emit(\"decoded\",packet)}}else{this.emit(\"decoded\",packet)}}else if(isBuf(obj)||obj.base64){if(!this.reconstructor){throw new Error(\"got binary data when not reconstructing a packet\")}else{packet=this.reconstructor.takeBinaryData(obj);if(packet){this.reconstructor=null;this.emit(\"decoded\",packet)}}}else{throw new Error(\"Unknown type: \"+obj)}};function decodeString(str){var p={};var i=0;p.type=Number(str.charAt(0));if(null==exports.types[p.type])return error();if(exports.BINARY_EVENT==p.type||exports.BINARY_ACK==p.type){var buf=\"\";while(str.charAt(++i)!=\"-\"){buf+=str.charAt(i);if(i+1==str.length)break}if(buf!=Number(buf)||str.charAt(i)!=\"-\"){throw new Error(\"Illegal attachments\")}p.attachments=Number(buf)}if(\"/\"==str.charAt(i+1)){p.nsp=\"\";while(++i){var c=str.charAt(i);if(\",\"==c)break;p.nsp+=c;if(i+1==str.length)break}}else{p.nsp=\"/\"}var next=str.charAt(i+1);if(\"\"!==next&&Number(next)==next){p.id=\"\";while(++i){var c=str.charAt(i);if(null==c||Number(c)!=c){--i;break}p.id+=str.charAt(i);if(i+1==str.length)break}p.id=Number(p.id)}if(str.charAt(++i)){try{p.data=json.parse(str.substr(i))}catch(e){return error()}}debug(\"decoded %s as %j\",str,p);return p}Decoder.prototype.destroy=function(){if(this.reconstructor){this.reconstructor.finishedReconstruction()}};function BinaryReconstructor(packet){this.reconPack=packet;this.buffers=[]}BinaryReconstructor.prototype.takeBinaryData=function(binData){this.buffers.push(binData);if(this.buffers.length==this.reconPack.attachments){var packet=binary.reconstructPacket(this.reconPack,this.buffers);this.finishedReconstruction();return packet}return null};BinaryReconstructor.prototype.finishedReconstruction=function(){this.reconPack=null;this.buffers=[]};function error(data){return{type:exports.ERROR,data:\"parser error\"}}},{\"./binary\":45,\"./is-buffer\":47,\"component-emitter\":9,debug:10,isarray:48,json3:49}],47:[function(_dereq_,module,exports){(function(global){module.exports=isBuf;function isBuf(obj){return global.Buffer&&global.Buffer.isBuffer(obj)||global.ArrayBuffer&&obj instanceof ArrayBuffer}}).call(this,typeof self!==\"undefined\"?self:typeof window!==\"undefined\"?window:{})},{}],48:[function(_dereq_,module,exports){module.exports=_dereq_(32)},{}],49:[function(_dereq_,module,exports){(function(window){var getClass={}.toString,isProperty,forEach,undef;var isLoader=typeof define===\"function\"&&define.amd;var nativeJSON=typeof JSON==\"object\"&&JSON;var JSON3=typeof exports==\"object\"&&exports&&!exports.nodeType&&exports;if(JSON3&&nativeJSON){JSON3.stringify=nativeJSON.stringify;JSON3.parse=nativeJSON.parse}else{JSON3=window.JSON=nativeJSON||{}}var isExtended=new Date(-0xc782b5b800cec);try{isExtended=isExtended.getUTCFullYear()==-109252&&isExtended.getUTCMonth()===0&&isExtended.getUTCDate()===1&&isExtended.getUTCHours()==10&&isExtended.getUTCMinutes()==37&&isExtended.getUTCSeconds()==6&&isExtended.getUTCMilliseconds()==708}catch(exception){}function has(name){if(has[name]!==undef){return has[name]}var isSupported;if(name==\"bug-string-char-index\"){isSupported=\"a\"[0]!=\"a\"}else if(name==\"json\"){isSupported=has(\"json-stringify\")&&has(\"json-parse\")}else{var value,serialized='{\"a\":[1,true,false,null,\"\\\\u0000\\\\b\\\\n\\\\f\\\\r\\\\t\"]}';if(name==\"json-stringify\"){var stringify=JSON3.stringify,stringifySupported=typeof stringify==\"function\"&&isExtended;if(stringifySupported){(value=function(){return 1}).toJSON=value;try{stringifySupported=stringify(0)===\"0\"&&stringify(new Number)===\"0\"&&stringify(new String)=='\"\"'&&stringify(getClass)===undef&&stringify(undef)===undef&&stringify()===undef&&stringify(value)===\"1\"&&stringify([value])==\"[1]\"&&stringify([undef])==\"[null]\"&&stringify(null)==\"null\"&&stringify([undef,getClass,null])==\"[null,null,null]\"&&stringify({a:[value,true,false,null,\"\\x00\\b\\n\\f\\r\t\"]})==serialized&&stringify(null,value)===\"1\"&&stringify([1,2],null,1)==\"[\\n 1,\\n 2\\n]\"&&stringify(new Date(-864e13))=='\"-271821-04-20T00:00:00.000Z\"'&&stringify(new Date(864e13))=='\"+275760-09-13T00:00:00.000Z\"'&&stringify(new Date(-621987552e5))=='\"-000001-01-01T00:00:00.000Z\"'&&stringify(new Date(-1))=='\"1969-12-31T23:59:59.999Z\"'}catch(exception){stringifySupported=false}}isSupported=stringifySupported}if(name==\"json-parse\"){var parse=JSON3.parse;if(typeof parse==\"function\"){try{if(parse(\"0\")===0&&!parse(false)){value=parse(serialized);var parseSupported=value[\"a\"].length==5&&value[\"a\"][0]===1;if(parseSupported){try{parseSupported=!parse('\"\t\"')}catch(exception){}if(parseSupported){try{parseSupported=parse(\"01\")!==1}catch(exception){}}if(parseSupported){try{parseSupported=parse(\"1.\")!==1}catch(exception){}}}}}catch(exception){parseSupported=false}}isSupported=parseSupported}}return has[name]=!!isSupported}if(!has(\"json\")){var functionClass=\"[object Function]\";var dateClass=\"[object Date]\";var numberClass=\"[object Number]\";var stringClass=\"[object String]\";var arrayClass=\"[object Array]\";var booleanClass=\"[object Boolean]\";var charIndexBuggy=has(\"bug-string-char-index\");if(!isExtended){var floor=Math.floor;var Months=[0,31,59,90,120,151,181,212,243,273,304,334];var getDay=function(year,month){return Months[month]+365*(year-1970)+floor((year-1969+(month=+(month>1)))/4)-floor((year-1901+month)/100)+floor((year-1601+month)/400)}}if(!(isProperty={}.hasOwnProperty)){isProperty=function(property){var members={},constructor;if((members.__proto__=null,members.__proto__={toString:1},members).toString!=getClass){isProperty=function(property){var original=this.__proto__,result=property in(this.__proto__=null,this);this.__proto__=original;return result}}else{constructor=members.constructor;isProperty=function(property){var parent=(this.constructor||constructor).prototype;return property in this&&!(property in parent&&this[property]===parent[property])}}members=null;return isProperty.call(this,property)}}var PrimitiveTypes={\"boolean\":1,number:1,string:1,undefined:1};var isHostType=function(object,property){var type=typeof object[property];return type==\"object\"?!!object[property]:!PrimitiveTypes[type]};forEach=function(object,callback){var size=0,Properties,members,property;(Properties=function(){this.valueOf=0}).prototype.valueOf=0;members=new Properties;for(property in members){if(isProperty.call(members,property)){size++}}Properties=members=null;if(!size){members=[\"valueOf\",\"toString\",\"toLocaleString\",\"propertyIsEnumerable\",\"isPrototypeOf\",\"hasOwnProperty\",\"constructor\"];forEach=function(object,callback){var isFunction=getClass.call(object)==functionClass,property,length;var hasProperty=!isFunction&&typeof object.constructor!=\"function\"&&isHostType(object,\"hasOwnProperty\")?object.hasOwnProperty:isProperty;for(property in object){if(!(isFunction&&property==\"prototype\")&&hasProperty.call(object,property)){callback(property)}}for(length=members.length;property=members[--length];hasProperty.call(object,property)&&callback(property));}}else if(size==2){forEach=function(object,callback){var members={},isFunction=getClass.call(object)==functionClass,property;for(property in object){if(!(isFunction&&property==\"prototype\")&&!isProperty.call(members,property)&&(members[property]=1)&&isProperty.call(object,property)){callback(property)}}}}else{forEach=function(object,callback){var isFunction=getClass.call(object)==functionClass,property,isConstructor;for(property in object){if(!(isFunction&&property==\"prototype\")&&isProperty.call(object,property)&&!(isConstructor=property===\"constructor\")){callback(property)}}if(isConstructor||isProperty.call(object,property=\"constructor\")){callback(property)}}}return forEach(object,callback)};if(!has(\"json-stringify\")){var Escapes={92:\"\\\\\\\\\",34:'\\\\\"',8:\"\\\\b\",12:\"\\\\f\",10:\"\\\\n\",13:\"\\\\r\",9:\"\\\\t\"};var leadingZeroes=\"000000\";var toPaddedString=function(width,value){return(leadingZeroes+(value||0)).slice(-width)};var unicodePrefix=\"\\\\u00\";var quote=function(value){var result='\"',index=0,length=value.length,isLarge=length>10&&charIndexBuggy,symbols;if(isLarge){symbols=value.split(\"\")}for(;index<length;index++){var charCode=value.charCodeAt(index);switch(charCode){case 8:case 9:case 10:case 12:case 13:case 34:case 92:result+=Escapes[charCode];break;default:if(charCode<32){result+=unicodePrefix+toPaddedString(2,charCode.toString(16));break}result+=isLarge?symbols[index]:charIndexBuggy?value.charAt(index):value[index]}}return result+'\"'};var serialize=function(property,object,callback,properties,whitespace,indentation,stack){var value,className,year,month,date,time,hours,minutes,seconds,milliseconds,results,element,index,length,prefix,result;try{value=object[property]}catch(exception){}if(typeof value==\"object\"&&value){className=getClass.call(value);if(className==dateClass&&!isProperty.call(value,\"toJSON\")){if(value>-1/0&&value<1/0){if(getDay){date=floor(value/864e5);for(year=floor(date/365.2425)+1970-1;getDay(year+1,0)<=date;year++);for(month=floor((date-getDay(year,0))/30.42);getDay(year,month+1)<=date;month++);date=1+date-getDay(year,month);time=(value%864e5+864e5)%864e5;hours=floor(time/36e5)%24;minutes=floor(time/6e4)%60;seconds=floor(time/1e3)%60;milliseconds=time%1e3}else{year=value.getUTCFullYear();month=value.getUTCMonth();date=value.getUTCDate();hours=value.getUTCHours();minutes=value.getUTCMinutes();seconds=value.getUTCSeconds();milliseconds=value.getUTCMilliseconds()}value=(year<=0||year>=1e4?(year<0?\"-\":\"+\")+toPaddedString(6,year<0?-year:year):toPaddedString(4,year))+\"-\"+toPaddedString(2,month+1)+\"-\"+toPaddedString(2,date)+\"T\"+toPaddedString(2,hours)+\":\"+toPaddedString(2,minutes)+\":\"+toPaddedString(2,seconds)+\".\"+toPaddedString(3,milliseconds)+\"Z\"}else{value=null}}else if(typeof value.toJSON==\"function\"&&(className!=numberClass&&className!=stringClass&&className!=arrayClass||isProperty.call(value,\"toJSON\"))){value=value.toJSON(property)}}if(callback){value=callback.call(object,property,value)}if(value===null){return\"null\"}className=getClass.call(value);if(className==booleanClass){return\"\"+value}else if(className==numberClass){return value>-1/0&&value<1/0?\"\"+value:\"null\"}else if(className==stringClass){return quote(\"\"+value)}if(typeof value==\"object\"){for(length=stack.length;length--;){if(stack[length]===value){throw TypeError()}}stack.push(value);results=[];prefix=indentation;indentation+=whitespace;if(className==arrayClass){for(index=0,length=value.length;index<length;index++){element=serialize(index,value,callback,properties,whitespace,indentation,stack);results.push(element===undef?\"null\":element)}result=results.length?whitespace?\"[\\n\"+indentation+results.join(\",\\n\"+indentation)+\"\\n\"+prefix+\"]\":\"[\"+results.join(\",\")+\"]\":\"[]\"}else{forEach(properties||value,function(property){var element=serialize(property,value,callback,properties,whitespace,indentation,stack);if(element!==undef){results.push(quote(property)+\":\"+(whitespace?\" \":\"\")+element)}});result=results.length?whitespace?\"{\\n\"+indentation+results.join(\",\\n\"+indentation)+\"\\n\"+prefix+\"}\":\"{\"+results.join(\",\")+\"}\":\"{}\"}stack.pop();return result}};JSON3.stringify=function(source,filter,width){var whitespace,callback,properties,className;if(typeof filter==\"function\"||typeof filter==\"object\"&&filter){if((className=getClass.call(filter))==functionClass){callback=filter}else if(className==arrayClass){properties={};for(var index=0,length=filter.length,value;index<length;value=filter[index++],(className=getClass.call(value),className==stringClass||className==numberClass)&&(properties[value]=1));}}if(width){if((className=getClass.call(width))==numberClass){if((width-=width%1)>0){for(whitespace=\"\",width>10&&(width=10);whitespace.length<width;whitespace+=\" \");}}else if(className==stringClass){whitespace=width.length<=10?width:width.slice(0,10)}}return serialize(\"\",(value={},value[\"\"]=source,value),callback,properties,whitespace,\"\",[])}}if(!has(\"json-parse\")){var fromCharCode=String.fromCharCode;var Unescapes={92:\"\\\\\",34:'\"',47:\"/\",98:\"\\b\",116:\"\t\",110:\"\\n\",102:\"\\f\",114:\"\\r\"};var Index,Source;var abort=function(){Index=Source=null;throw SyntaxError()};var lex=function(){var source=Source,length=source.length,value,begin,position,isSigned,charCode;while(Index<length){charCode=source.charCodeAt(Index);switch(charCode){case 9:case 10:case 13:case 32:Index++;break;case 123:case 125:case 91:case 93:case 58:case 44:value=charIndexBuggy?source.charAt(Index):source[Index];Index++;return value;case 34:for(value=\"@\",Index++;Index<length;){charCode=source.charCodeAt(Index);if(charCode<32){abort()}else if(charCode==92){charCode=source.charCodeAt(++Index);switch(charCode){case 92:case 34:case 47:case 98:case 116:case 110:case 102:case 114:value+=Unescapes[charCode];Index++;break;case 117:begin=++Index;for(position=Index+4;Index<position;Index++){charCode=source.charCodeAt(Index);if(!(charCode>=48&&charCode<=57||charCode>=97&&charCode<=102||charCode>=65&&charCode<=70)){abort()}}value+=fromCharCode(\"0x\"+source.slice(begin,Index));break;default:abort()}}else{if(charCode==34){break}charCode=source.charCodeAt(Index);begin=Index;while(charCode>=32&&charCode!=92&&charCode!=34){charCode=source.charCodeAt(++Index)}value+=source.slice(begin,Index)}}if(source.charCodeAt(Index)==34){Index++;return value}abort();default:begin=Index;if(charCode==45){isSigned=true;charCode=source.charCodeAt(++Index)}if(charCode>=48&&charCode<=57){if(charCode==48&&(charCode=source.charCodeAt(Index+1),charCode>=48&&charCode<=57)){abort()}isSigned=false;for(;Index<length&&(charCode=source.charCodeAt(Index),charCode>=48&&charCode<=57);Index++);if(source.charCodeAt(Index)==46){position=++Index;for(;position<length&&(charCode=source.charCodeAt(position),charCode>=48&&charCode<=57);position++);if(position==Index){abort()}Index=position}charCode=source.charCodeAt(Index);if(charCode==101||charCode==69){charCode=source.charCodeAt(++Index);if(charCode==43||charCode==45){Index++}for(position=Index;position<length&&(charCode=source.charCodeAt(position),charCode>=48&&charCode<=57);position++);if(position==Index){abort()}Index=position}return+source.slice(begin,Index)}if(isSigned){abort()}if(source.slice(Index,Index+4)==\"true\"){Index+=4;return true}else if(source.slice(Index,Index+5)==\"false\"){Index+=5;return false}else if(source.slice(Index,Index+4)==\"null\"){Index+=4;return null}abort()}}return\"$\"};var get=function(value){var results,hasMembers;if(value==\"$\"){abort()}if(typeof value==\"string\"){if((charIndexBuggy?value.charAt(0):value[0])==\"@\"){return value.slice(1)}if(value==\"[\"){results=[];for(;;hasMembers||(hasMembers=true)){value=lex();if(value==\"]\"){break}if(hasMembers){if(value==\",\"){value=lex();if(value==\"]\"){abort()}}else{abort()}}if(value==\",\"){abort()}results.push(get(value))}return results}else if(value==\"{\"){results={};for(;;hasMembers||(hasMembers=true)){value=lex();if(value==\"}\"){break}if(hasMembers){if(value==\",\"){value=lex();if(value==\"}\"){abort()}}else{abort()}}if(value==\",\"||typeof value!=\"string\"||(charIndexBuggy?value.charAt(0):value[0])!=\"@\"||lex()!=\":\"){abort()}results[value.slice(1)]=get(lex())}return results}abort()}return value};var update=function(source,property,callback){var element=walk(source,property,callback);if(element===undef){delete source[property]}else{source[property]=element}};var walk=function(source,property,callback){var value=source[property],length;if(typeof value==\"object\"&&value){if(getClass.call(value)==arrayClass){for(length=value.length;length--;){update(value,length,callback)}}else{forEach(value,function(property){update(value,property,callback)})}}return callback.call(source,property,value)};JSON3.parse=function(source,callback){var result,value;Index=0;Source=\"\"+source;result=get(lex());if(lex()!=\"$\"){abort()}Index=Source=null;return callback&&getClass.call(callback)==functionClass?walk((value={},value[\"\"]=result,value),\"\",callback):result}}}if(isLoader){define(function(){return JSON3})}})(this)},{}],50:[function(_dereq_,module,exports){module.exports=toArray;function toArray(list,index){var array=[];index=index||0;for(var i=index||0;i<list.length;i++){array[i-index]=list[i]}return array}},{}]},{},[1])(1)});"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/wordsfilter/sensi_words.txt",
    "content": "兼职\n有意者\n到货\n代购\n扣扣\n微店\n兼职\n兼值\n淘宝\n小姐\n妓女\n包夜\n狼友\n技师\n推油\n胸推\n毒龙\n口爆\n兼职\n楼凤\n足交\n口暴\n口交\n桑拿\n吞精\n咪咪\n婊子\n乳方\n操逼\n全职\n性伴侣\n网购\n专业代理\n帮忙点一下\n帮忙点下\n请点击进入\n详情请进入\n私人侦探\n私家侦探\n针孔摄象\n调查婚外情\n信用卡提现\n无抵押贷款\n广告代理\n原音铃声\n借腹生子\n找个妈妈\n找个爸爸\n代孕妈妈\n代生孩子\n代开发票\n腾讯客服电话\n销售热线\n免费订购热线\n低价出售\n款到发货\n回复可见\n连锁加盟\n加盟连锁\n免费二级域名\n免费使用\n免费索取\n蚁力神\n婴儿汤\n售肾\n刻章办\n买小车\n套牌车\n玛雅网\n电脑传讯\n视频来源\n下载速度\n高清在线\n全集在线\n在线播放\ntxt下载\n六位qq\n6位qq\n位的qq\n个qb\n送qb\n用刀横向切腹\n完全自杀手册\n四海帮\n足球投注\n地下钱庄\n中国复兴党\n阿波罗网\n曾道人\n六合彩\n改卷内幕\n替考试\n隐形耳机\n出售答案\n考中答案\n答an\nda案\n资金周转\n救市\n股市圈钱\n崩盘\n资金短缺\n证监会\n质押贷款\n小额贷款\n周小川\n刘明康\n尚福林\n孔丹\n出售雷管\n炸药\n炸药\n出售炸药\n出售炸药 电话\n制作火药配方\n炸药出售\n出售雷管炸药\n火药配方\n在家里做原子弹\n硝酸甘油炸弹制作\nTNT 炸弹的制作\n硝铵炸药配方\n硝酸甘油制作\n炸弹配方\n简易炸药\n火药制作简易炸弹\n炸弹制作\n硝酸甘油\n硝酸甘油炸弹制作方法\n手把手教你做炸弹\n恐怖分子傻瓜手册\n氢弹手工制作方法\n起爆器\n自制燃烧弹\n燃烧弹\n弹药制造方法\n液体炸药配方\n炸药配方与制作\n自制炸药配方\ntnt炸药成分\n炸药的制作\ntnt炸药配方\n各种炸药配方大全\n炸药的成分\n燃烧弹的制作方\n黑火药的配方\n黑火药的制作\n黑火药比例\n燃烧弹 制作\n黑火药配方比例\n黑火药的成分\n原子弹配方\n原子弹DIY教程\nC4炸药\n雷管出售\n制作硝化甘油\nDIY核弹\n核弹制作方法\n原子弹制作手册\n简易原子弹教程\n黑火药配方\n自制炸药方法\n教你怎么用土办法做武器\n莫洛托夫鸡尾酒的配方\nzz炸弹的制作方法\nC4炸药的成分\n炸药制造生产工艺\n原子弹制造完全手册\n「红烧兔子」大餐\n汽车炸弹制作\n人体炸弹制作流程\n炸弹制作从入门\n鸡尾酒炸弹制作\n集束炸弹制作\n硝酸铵的简便制取方法\n莫洛托夫燃烧弹\n塑胶炸弹\n用火药混合塑料\n高性能爆炸物质混合\n原子弹 制作方法\n手教你制作原子弹\n轻型巡航导弹的制作\n黑索金的制造过程\nZha药制造进程\n购买自爆材料\n可塑炸弹制作方法\nC4塑胶炸药\n水浴法制TNT\n简易核弹头的制造\n高爆炸药的基本配方\n如何制硝化甘油\n燃烧剂的简易制备\n简易大威力炸弹\n炸弹制作入门\n硝酸甘油炸药制作\n基本配方是硝酸甘\n燃烧剂的简易制作\n制作性能优良的燃烧弹\n酸甘油炸药\n简易导弹制作\n核弹头的制造\n可塑炸弹制作\n炸药硝酸甘油制法\n原子弹制作方法\n火药的配方\n手教你做原子弹\n怎么用土办法做武器\n手把手教你制作原子弹\n燃烧弹的制作方法\n怎么自制燃烧弹\nTNT炸弹制作\n高爆炸药基本配方\n爆破和杀伤燃烧弹\n炸弹制作从入门到精\n写的炸弹制作教程\n原子弹制作 方法\n教你制作原子弹\n下硝化甘油的制作方法\nDIY原子弹\n原子弹制作\n炸药及其制造方法\nATOM弹制造\n三分钟教你原子弹\n火药原理\n简易炸弹\n简易燃烧弹\n土炸药成份\n你怎么用土办法做武器\n燃烧弹制作\n燃烧瓶的制作方法\n炸弹制作手册 硫酸\n原子弹制作方法DIY\n汽油三分之一白糖混合\n炸药的制作配方\n燃烧弹的制作方\n炸药的配方\n如何自制燃烧弹\n标准炸弹教程\n黑火药和硝酸钾农药混合\n恐怖分子自己制造炸弹\n手柄制作炸弹引爆器\n制作炸弹首先要\n燃烧瓶制作\n出售炸药QQ\n出售气枪\n出售气枪 QQ\n出售气枪 电话\n出售手枪\n出售手枪 QQ\n出售手枪 电话\n出售猎枪\n出售猎枪 QQ\n出售猎枪 电话\n出售汽枪\n出售步枪\n出售狙击枪\n出售手狗\n出售气狗\n出售猎狗\n【手枪出售】联系电话\n64式手枪出售\n92式手枪出售\n出售:高仿真枪\n出售54式手枪\n出售M1911\n出售各类猎狗\n出售国产军用54手枪\n出售火枪 –游戏\n出售进口金属枪模\n出售仿真枪\n出售麻醉枪免收任何定金\n出售手枪=货到付款\n出售外国枪模\n出售袖珍手枪\n娥眉气枪出售\n防身手枪QQ\n防身武器手枪\n仿真枪QQ\n钢珠枪小口径步枪\n高压狗QQ\n高压气枪气枪子弹\n高压气枪|电狗专卖\n高压气枪|气枪货到付款\n高压气枪专卖店\n各类军用枪\n各类军用枪支\n各式气枪出售\n工字牌气枪出售专卖\n气枪\n工字汽狗麻醉枪\n供应军用手枪\n供应汽枪\n购买枪支QQ\n国产汽狗专卖|气枪货\n集团出售手枪\n集团出售手枪气枪\n进口气枪气枪子弹\n军用54出售\n军用77出售\n军用手枪钢珠枪\n里有汽枪出售\n猎枪出售/枪\n猎枪仿真枪QQ\n猎枪麻醉枪钢珠枪\n猎枪哪里有卖\n猎枪手枪买卖\n猎枪销售手枪销售气枪\n猎枪专卖店\n猎枪子弹底火专卖\n麻醉枪出售可验货\n麻醉枪猎枪军用\n买卖54狗\n买卖64狗\n买卖警察枪支\n买卖军狗\n买卖军用枪支\n卖64手枪\n卖92手枪\n卖钢珠枪QQ\n哪里能买到自制手枪\n哪里有钢珠枪\n哪里有连发猎枪买卖\n哪里有卖双管猎\n哪里有气枪卖\n哪里有枪出售\n哪里有真枪出售\n哪里有真枪卖买\n哪能买到真枪\n期出售手枪气枪猎\n气狗 高压气枪\n气狗出售 进口气狗\n气枪专卖网\n气枪/出售/专卖网\n气枪/猎枪/钢珠枪\n气枪仿真枪出售\n气枪购买|气枪组装\n气枪货到付款汽狗出售\n气枪价格 仿真气枪\n气枪价格，气枪专卖店\n气枪看货加QQ\n气枪联系QQ\n气枪猎枪麻醉枪\n气枪迷魂药\n汽狗高压汽枪\n铅弹 上海工字气枪\n全金属仿真枪专卖\n热武器QQ\n三箭气枪出售\n手枪气* 枪猎\n手槍买卖自制手枪\n售枪购买手枪\n售手枪QQ\n司马仿真枪模型QQ\n淘宝气枪货到付款\n提供气狗专卖店\n提供专业及时的气狗\n秃鹰气枪货到付款\n锡峰气枪出售\n仙鹤气枪出售\n销售/专卖/买卖77式手枪\n销售电手狗\n销售气手狗\n销售小口径步枪\n销售运动步枪\n小口径秃鹰沙漠\n有军用枪支出售\n长期出 售手枪\n折叠狗QQ\n转让 猎枪\n子弹货到付款\n自制手枪哪里买\n自制手枪哪里有卖\n阻击枪/汽枪/高压气枪\n组装手枪\n最新销售汽狗\n出售警用手铐|仿真枪\n猎枪|真枪\n手枪|阻击枪\n弓弩专营\n弓弩专卖网货到付款\n弓弩专卖网\n弓弩专卖货到付款\n弓弩专卖店网\n弓弩专卖店\n弓弩专卖\n弓弩制作\n弓弩直销\n弓弩有限公司\n弓弩营销中心\n弓弩一百专卖店\n弓弩销售\n弓弩网\n弓弩网\n弓弩图纸\n弓弩特许经销\n弓弩狩猎网\n自制手弩\n追风弓弩麻醉箭专卖\n专业弓弩网\n中国战神军用弓弩\n中国弩弓专卖\n中国弓弩专卖网\n中国弓弩直销\n中国弓弩网\n中国弓弩狩猎网\n中国弓驽网\n制作简易弓弩\n郑州弓弩专卖\n赵氏弓弩专卖网\n赵氏弓弩专卖店\n赵氏弓弩专卖\n赵氏弓弩销售\n小型弓弩专卖店\n小猎人弓弩网\n狩猎器材弓弩专卖\n狩猎器材弓弩\n狩猎弓弩专卖网\n狩猎弓弩专卖\n狩猎弓弩麻醉箭\n手枪式折叠三用弩\n三利达弓弩专卖网\n三利达弓弩直营\n三利达弓弩配件\n三步倒药箭批发\n三步倒弩箭专卖\n三步倒麻醉弩箭销售\n三步倒麻醉箭专卖\n三步倒麻醉箭\n三步倒捕狗药\n军用弓弩专卖网\n军用弓弩专卖店\n军用弓弩批发\n军用弓弩公司\n供应三利达弓弩麻醉箭\n供应三步倒麻醉箭\n供应秦氏弓弩\n供应弩用麻醉箭\n供应弩捕狗箭\n供应麻醉箭三步倒\n供应麻醉箭批发\n供应麻醉箭\n供应军用弩折叠弩\n供应军用弓弩专卖\n供应精品弓弩\n供应弓弩麻醉箭\n供应弓弩\n供应钢珠弓弩\n弓弩商城专卖\n弓弩商城\n弓弩亲兄弟货到付款\n弓弩批发\n弓弩免定金货到付款\n弓弩麻醉箭\n弓弩麻醉镖\n弓弩论坛\n钢珠弓弩专卖网\n钢珠弓弩专卖店\n打狗弓弩三步倒\n麻醉弓弩专卖店\n出售军刀\n出售军刺\n出售弹簧刀\n出售三棱刀\n出售跳刀\n军刀网\n南方军刀网\n户外军刀网\n三棱军刺专卖\n出售开山刀军刺\n西点军刀网\n军刀专 卖\n戈博军刀\n阿兰德龙户外\n出售军品军刀\n勃朗宁军刀\n军刀军品网\n阿兰得龙野营刀具网\n出售军刺军刀\n警用刀具出售\n折刀专卖网\n阳江军品军刀网\n野营刀专卖\n砍刀精品折刀专卖\n匕首蝴蝶甩刀专卖\n军刀专卖军刺\n军刀专卖刀具批发\n军刀图片砍刀\n军刀网军刀专卖\n军刀价格军用刀具\n军品军刺网\n军刀军刺甩棍\n阳江刀具批发网\n北方先锋军刀\n正品军刺出售\n野营军刀出售\n开山刀砍刀出售\n仿品军刺出售\n军刀直刀专卖\n手工猎刀专卖\n自动跳刀专卖\n军刀电棍销售\n军刀甩棍销售\n美国军刀出售\n极端武力折刀\n防卫棍刀户外刀具\n阿兰德龙野营刀\n仿品军刺网\n野营砍刀户外军刀\n手工猎刀户外刀具\n中国户外刀具网\n西点军品军刀网\n野营开山刀军刺\n三利达弓弩军刀\n尼泊尔军刀出售\n防卫野营砍刀出售\n防卫著名军刀出售\n防卫棍刀出售\n防卫甩棍出售\n防卫电棍出售\n军刺野营砍刀出售\n著名精品折刀出售\n战术军刀出售\n刺刀专卖网\n户外军刀出售\n阳江刀具直销网\n冷钢刀具直销网\n防卫刀具直销网\n极端武力直销网\n刀具直销网\n军刀直销网\n直刀匕首直销网\n军刀匕首直销网\n折刀砍刀军品网\n野营刀具军品网\n阳江刀具军品网\n冷钢刀具军品网\n防卫刀具军品网\n极端武力军品网\n军用刀具军品网\n军刀直刀军品网\n折刀砍刀专卖\n野营刀具专卖\n阳江刀具专卖\n冷钢刀具专卖\n防卫刀具专卖\n出售美军现役军刀\n习近平\n平近习\n习太子\n习明泽\n老习\n温家宝\n温加宝\n温x\n温jia宝\n温宝宝\n温加饱\n温加保\n张培莉\n温云松\n温如春\n温jb\n胡温\n胡x\n胡jt\n胡boss\n胡总\n胡王八\n胡jintao\n胡j涛\n胡惊涛\n胡景涛\n胡紧掏\n湖紧掏\n胡紧套\n锦涛\n胡派\n胡主席\n刘永清\n胡海峰\n胡海清\n江泽民\n民泽江\n江胡\n江哥\n江主席\n江书记\n江浙闽\n江沢民\n江浙民\n择民\n则民\n茳泽民\nzemin\nze民\n江core\n江x\n江派\n江zm\n江戏子\n江蛤蟆\n江某某\n江贼\n江猪\n江氏集团\n江绵恒\n江绵康\n王冶坪\n江泽慧\n邓小平\n平小邓\n邓xp\n邓晓平\n邓朴方\n邓榕\n邓质方\n毛泽东\n猫泽东\n猫则东\n猫贼洞\n毛zd\n毛zx\nz东\nze东\n泽d\nzedong\n毛太祖\n毛相\n主席画像\n改革历程\n朱镕基\n朱容基\n朱镕鸡\n朱容鸡\n朱云来\n李鹏\n李peng\n里鹏\n李月月鸟\n李小鹏\n李小琳\n华主席\n华国\n国锋\n国峰\n锋同志\n白春礼\n薄熙来\n薄一波\n蔡赴朝\n蔡武\n曹刚川\n常万全\n陈炳德\n陈德铭\n陈建国\n陈良宇\n陈绍基\n陈同海\n陈至立\n戴秉国\n丁一平\n董建华\n杜德印\n杜世成\n傅锐\n郭伯雄\n郭金龙\n贺国强\n胡春华\n耀邦\n华建敏\n黄华华\n黄丽满\n黄兴国\n回良玉\n贾庆林\n贾廷安\n靖志远\n李长春\n李春城\n李建国\n李克强\n李岚清\n李沛瑶\n李荣融\n李瑞环\n李铁映\n李先念\n李学举\n李源潮\n栗智\n梁光烈\n廖锡龙\n林树森\n林炎志\n林左鸣\n令计划\n柳斌杰\n刘奇葆\n刘少奇\n刘延东\n刘云山\n刘志军\n龙新民\n路甬祥\n罗箭\n吕祖善\n马飚\n马恺\n孟建柱\n欧广源\n强卫\n沈跃跃\n宋平顺\n粟戎生\n苏树林\n孙家正\n铁凝\n屠光绍\n王东明\n汪东兴\n王鸿举\n王沪宁\n王乐泉\n王洛林\n王岐山\n王胜俊\n王太华\n王学军\n王兆国\n王振华\n吴邦国\n吴定富\n吴官正\n无官正\n吴胜利\n吴仪\n奚国华\n习仲勋\n徐才厚\n许其亮\n徐绍史\n杨洁篪\n叶剑英\n由喜贵\n于幼军\n俞正声\n袁纯清\n曾培炎\n曾庆红\n曾宪梓\n曾荫权\n张德江\n张定发\n张高丽\n张立昌\n张荣坤\n张志国\n赵洪祝\n紫阳\n周生贤\n周永康\n朱海仑\n中南海\n大陆当局\n中国当局\n北京当局\n共产党\n党产共\n共贪党\n阿共\n产党共\n公产党\n工产党\n共c党\n共x党\n共铲\n共惨\n供铲党\n供铲谠\n供铲裆\n共残党\n共残主义\n共产主义的幽灵\n拱铲\n老共\n中共\n中珙\n中gong\ngc党\n贡挡\ngong党\ng产\n狗产蛋\n共残裆\n恶党\n邪党\n共产专制\n共产王朝\n裆中央\n土共\n土g\n共狗\ng匪\n共匪\n仇共\n症腐\n政腐\n政付\n正府\n政俯\n政f\n政zhi\n挡中央\n档中央\n中央领导\n中国zf\n中央zf\n国wu院\n中华帝国\ngong和\n大陆官方\n北京政权\n江泽民\n胡锦涛\n温家宝\n习近平\n习仲勋\n贺国强\n贺子珍\n周永康\n李长春\n李德生\n王岐山\n姚依林\n回良玉\n李源潮\n李干成\n戴秉国\n黄镇\n刘延东\n刘瑞龙\n俞正声\n黄敬\n薄熙\n薄一波\n周小川\n周建南\n温云松\n徐明\n江泽慧\n江绵恒\n江绵康\n李小鹏\n李鹏\n李小琳\n朱云来\n朱容基\n法轮功\n李洪志\n新疆骚乱\n爱液\n按摩棒\n拔出来\n爆草\n包二奶\n暴干\n暴奸\n暴乳\n爆乳\n暴淫\n被操\n被插\n被干\n逼奸\n仓井空\n插暴\n操逼\n操黑\n操烂\n肏你\n肏死\n操死\n操我\n厕奴\n插比\n插b\n插逼\n插进\n插你\n插我\n插阴\n潮吹\n潮喷\n成人电影\n成人论坛\n成人色情\n成人网站\n成人文学\n成人小说\n艳情小说\n成人游戏\n吃精\n抽插\n春药\n大波\n大力抽送\n大乳\n荡妇\n荡女\n盗撮\n发浪\n放尿\n肥逼\n粉穴\n风月大陆\n干死你\n干穴\n肛交\n肛门\n龟头\n裹本\n国产av\n好嫩\n豪乳\n黑逼\n后庭\n后穴\n虎骑\n换妻俱乐部\n黄片\n几吧\n鸡吧\n鸡巴\n鸡奸\n妓女\n奸情\n叫床\n脚交\n精液\n就去日\n巨屌\n菊花洞\n菊门\n巨奶\n巨乳\n菊穴\n开苞\n口爆\n口活\n口交\n口射\n口淫\n裤袜\n狂操\n狂插\n浪逼\n浪妇\n浪叫\n浪女\n狼友\n聊性\n凌辱\n漏乳\n露b\n乱交\n乱伦\n轮暴\n轮操\n轮奸\n裸陪\n买春\n美逼\n美少妇\n美乳\n美腿\n美穴\n美幼\n秘唇\n迷奸\n密穴\n蜜穴\n蜜液\n摸奶\n摸胸\n母奸\n奈美\n奶子\n男奴\n内射\n嫩逼\n嫩女\n嫩穴\n捏弄\n女优\n炮友\n砲友\n喷精\n屁眼\n前凸后翘\n强jian\n强暴\n强奸处女\n情趣用品\n情色\n拳交\n全裸\n群交\n人妻\n人兽\n日逼\n日烂\n肉棒\n肉逼\n肉唇\n肉洞\n肉缝\n肉棍\n肉茎\n肉具\n揉乳\n肉穴\n肉欲\n乳爆\n乳房\n乳沟\n乳交\n乳头\n骚逼\n骚比\n骚女\n骚水\n骚穴\n色逼\n色界\n色猫\n色盟\n色情网站\n色区\n色色\n色诱\n色欲\n色b\n少年阿宾\n射爽\n射颜\n食精\n释欲\n兽奸\n兽交\n手淫\n兽欲\n熟妇\n熟母\n熟女\n爽片\n双臀\n死逼\n丝袜\n丝诱\n松岛枫\n酥痒\n汤加丽\n套弄\n体奸\n体位\n舔脚\n舔阴\n调教\n偷欢\n推油\n脱内裤\n文做\n舞女\n无修正\n吸精\n夏川纯\n相奸\n小逼\n校鸡\n小穴\n小xue\n性感妖娆\n性感诱惑\n性虎\n性饥渴\n性技巧\n性交\n性奴\n性虐\n性息\n性欲\n胸推\n穴口\n穴图\n亚情\n颜射\n阳具\n杨思敏\n要射了\n夜勤病栋\n一本道\n一夜欢\n一夜情\n一ye情\n阴部\n淫虫\n阴唇\n淫荡\n阴道\n淫电影\n阴阜\n淫妇\n淫河\n阴核\n阴户\n淫贱\n淫叫\n淫教师\n阴茎\n阴精\n淫浪\n淫媚\n淫糜\n淫魔\n淫母\n淫女\n淫虐\n淫妻\n淫情\n淫色\n淫声浪语\n淫兽学园\n淫书\n淫术炼金士\n淫水\n淫娃\n淫威\n淫亵\n淫样\n淫液\n淫照\n阴b\n应召\n幼交\n欲火\n欲女\n玉乳\n玉穴\n援交\n原味内衣\n援助交际\n招鸡\n招妓\n抓胸\n自慰\n作爱\na片\nfuck\ngay片\ng点\nh动画\nh动漫\n失身粉\n淫荡自慰器\n"
  },
  {
    "path": "PaiZhiCheng/src/main/resources/wordsfilter/sensi_words2.txt",
    "content": "回民吃猪肉\n习近平\nTMD\n毛民进党\n妹妹淫水 流\n机吧\n联国\n1989六四\n性爱电影\n李红智\n梁光烈\n巴黎市长\n成人BT\n免注册淫色电影\nUltraSurf\n鐵血三國志\n性爱图库\n公安\n777黄站\n大龟头顶住嫂嫂的花心深处\n插 穴 射 喷 精液\n兽奸\nPORN\n毛可卡因\n阴茎挺进滋润肉洞里在阴户中抽送\nｆａｌｕｎｄａｆａ\n三八淫成人网\n发^^轮\n毛江折民\n毛拉登\n免费淫*女Ａ片段\nd7se我淫我电影\nmadelove\n淫奸美女穴\n美女极品穴\n…快插我淫穴…\n乱武天下\n玩穴\nｌｉｎｅａｇｅⅡ\n居然花钱请人教性爱姿势(组图)免费淫电影a片\n肛交\ngo-vern-ment\n奸你\n四眼醫生強制飲尿 美巨乳小野悠美第二彈\n坤迈\n高嘉\n兼职女教师\n嫂嫂的穴\n龟头在她的阴道里抽插\n张梅颖\n幼女嫩逼\nmeimei的阴蒂\n淫女性息\n强奸35岁处女全过程/淫妹阴毛小穴\n这年头就这样\n阴屄\n贺龙\n酱猪媳\n毛台湾共和国\n欢乐潜水艇\n陰唇\n淫虎大型色站\n免费淫奸黄色电影a片\ndrop\n免费强奸淫电影\n幸福花園\n老公，他要插进来了\n9JBW\n巨乳美女RINA无码第2弹\n十八电影掰穴特写红免费性视频聊天激情少妇图\n新華內情\nmeinv嫩穴\n麻黄素\n绝品美女が恍惚颜の绝顶快感\n操逼样\n褻\n日本激情淫穴贴图\n女优\n雞吧 阴茎 插 騷穴 嫩穴 小穴\n錢 國 梁\n巨乳\nfuck温总理\n熱血傳奇\n夜色导航\n風騷淫蕩\n麻黃素\n插阿姨的洞洞舔女朋友的下身\n驶你爸\n轮奸做爱高潮录象\n赛你老母\n內射\n李岚清\n芬太尼\n日本妹妹\nfuck生春袋\nｒｆ 行会战争\n卫生部\nfuck手机复制\n法论工\nmeimei的小屄好痒\n毛干拎娘\n黄色免费淫乱电影 淫女色情图片\n幸福花园\n奸他\n朱狨基\nJPEEN\n色情信息\n六岁幼女阴穴近拍\nmeimei被操的淫水直流\n粪便\n私人服务器\nｍａｓｔｅｒ\n希特勒\n猥褻\n马卖马屁\n淫B\n激情淫色自拍\nmi zhuan\n免费淫奸女Ａ片段试看 教你怎样延迟射精18岁以下禁\n又粗又长的肉棒\n新大陆\n吳 方 城\n胡紧套\n肉棒与阴道摩擦\n韩国成人激情淫洞\n丰满小妹的丰褪美腿 嫩得流水的小meimei\n淫妻交换\n你说我说论坛\n政治反对派\n聚商\nhang hai shi ji\n街头对抗靠\nㄙㄞ你爸\n淫色导航\n淫乱护士电影\n淫乱狂穴\n吗逼\n暴政\n香港激情淫影院\n发正念\n坤邁\n抽插18岁处女妹妹淫叫不断\n淫窝窝\n手伸到下体抚mo嫩穴\n中广网\nfuck宋楚瑜\n砍死你\n混 沌决\n神風~ Kamikaze Street Vol。 13 : 里山加与\n婬乱军团\n紮卡維是英雄\n免费午夜激情视频\n曾慶红\n乱伦之淫穴\n国产嫖娼\n淫液下留\n哥哥用力舒服死我了\n騰武\nspace\n毛你二大爷\n吞精骚妹\n真实内容\n新大陸\n天然むすめ 素人~このみ\n小泉純一郎\n色情 网 站 导航\n粉红的的奶头粉嫩的阴唇\n色妹妹淫穴\n热血传奇\n天联世纪\n操逼电影\n英雄世纪\n东突，免费淫色情电影 令人喷血的春宫108姿势(组图)18岁禁看\n跑跑卡丁车\n9之游\n淫b\n裸\n操车臣\n装逼呢\n地震\n肏meimei的小骚屄\n肉感炮友\n跟妹妹肏穴好舒服\nxi you\n反恐委员会\n骡干\n淫液顺着肉棒\n中華民國\n赫鲁晓夫\n狂操小穴\nTokyo Hot n0252 原田友美 高飞车美人教师精液炸沉\n國產嫖娼\n日本鬼子\n国管局\nsvdc\n小阴唇 黏液流下 舌头舔进阴道\n中華民国\n系统公告\nmeinv视频图情色贴图区成人性光牒\n臭机\n毛天安门事件\n聂荣臻\n淫妻交換\n毛人弹\n徐光春\n波霸\nmeimei等你大鸡吧来插\nGAN\n热站政论网\n免费成人图片 色情meinv做爱meinv小穴写真\n又肥又紧的屄儿\n中國狗\n金山\n小穴不断被顶入\n不倫繼母的性猥褻\n龟头在嫂嫂的小穴穴口研磨\n处女被强迫吃春药后被开苞\n毛洪志\n毛淫语连连\n费良勇\n反封鎖技術\nAV女优 激情小电影 熟女俱乐部\n法轮功靠\n熟女人妻 太太自愿拍片被机射在里面\nmo fa qi bing\n届中央政治局委员\n夜聊视频聊天室\n发$$纶\n色网阴道特写强奸33岁处女全过程\n胡新宇\n淫獸學園\n何加棟\n王子杰\n毛看牌软件\n儿子添着妈妈大的阴唇\n淫穴嫩穴\n插meimei小穴\nGCD\nFag\n遗言\n目標軟體\n默克尔\n午夜性爱惊魂\n操淫荡护士穴\n可视视频聊天\n恼杀美巨乳meinv~吉川萌\n地 下 教 會\n幼男搞幼女\n蔡庆林\n鸡吧插进肉穴\n紅十字會\n台湾共和国\n团中央\n吴 宏 达\n红顶商人\n丁子霖\n插那吗B\n掄功\n激情女优\n稀少罕见的美少女蝴蝶屄\nmm的诱人双峰→免费试看片\n唐柏桥\n阿拉法特\n福莱斯特\n刘云山\n葫蘆案\n情色电影立即看\n鋼管舞\n免费淫色情电影 教你如何鉴别处男处女的方法(组图)18岁禁看\nasiasex\n調教\n插深些\n神經病\n狹義道\n肉棒在小穴深初来回抽动\n鸡巴深深地插在妈的阴户里\n点点激情播放性电影\n侧那\n強淫女優\n我的阴茎使劲的抽插她的小穴\n人代会\n性愛 圖庫\n死gd\n精子 淫穴 淫液\n周莹\n肉棒插入小穴\n猛地插入了她的肥穴\n联大\n经血\n毛你姥姥的\nchengmingmag\n骚BB免费色电影\n薄一波\n宏 法\n腚眼\n淫痴\n口头检查\n日本成人激情论坛\n文化大革命\n民为法执\n達賴\n三级片A片下载观看\n死gm\n白肌meinv？琴也\n激情成人网 -用品 -知识 -保健\n抽插21岁处女meimei淫叫不断\n免费淫色情电影 欢迎点击进入观看/林/心如/20岁处女偷拍/免费A。B片\n免费黄色淫秽电影\n小穴流水meimei手指插\ngameline\n六四屠杀\n女優\n小B\n日本帝國\n法谪功\n人民真实\n伸到老师的小穴里\n潮姫 史上最高の潮吹き女\n日本帝国\nmeimei的小阴户\n辣妹狼图~肏穴图\n含住肉棒上下使劲吸吮起来\n百家性大型免费黄站\n陰囊\n輪攻\n精神病\n王策\n小b\n巨额骗储\n毛佳静安定片\n鸟gm\n骚屄\nTeSt\n激情电影免费下载裸体美女乳房图\n日翻\n美臀嫰穴\n毛王兆国\n小卵泡\n日本成人激情网站\n肉棒 好粗啊……\n情色俱乐部美女奸淫。黄色电影裸体电影PP点点激情电影\n數碼精靈\n迅雷下载免费黄色淫乱电影\n6？4靠\n全裸女优\n達赖\n正悟网\n毛小泉纯一郎\n持不同政见\n鸟gM\n淫癡\n台湾淫电影 导航\n外•挂\nwww。gpchina。net\n春色满园淫妻系列\n删 号靠\n美沙酮\nVIP独占~秘密之大乱交(最终话)\nblogbaby\n中华民国\n阴道里的大肉棒来回抽插\n操草妈\nhelper\n毛两腿之间\n淫女论坛\n48xp。com 电影免费\n亲哥哥……meimei的小穴……舒服死了……\n美国参考\n中华民國\n看棋软件\n操美女穴\n毛海洛因\n淫女俱乐部\n絲襪足交\n淫荡┅好舒服\nGAMEmaster\n陈独秀\n淫水愛液\n农村人乱伦事\n女大学生在夜店被朋友轮奸\n拉案\n总书记\n毛毛一鲜\n火热的精液喷射在子宫上\n肉棒捅进阴户里\n香港色情影星美女裸体\n毛性交\n大人の隠れ汤 乱交游戯温泉へようこそ\n因特天盟\n肉具\n司馬義？鉄力瓦爾地\n菊花蕾\nINSERT\n騷棒\n恋爱盒子OL\n颜骑\n騷棍\nbingnews\n耿飙\n近亲相奸 背徳愉悦 9\n耿飚\n护法\n韩国身份证\n法院\nｓａｎ ｇｕｏ ｑｕｎ ｙｉｎｇ ｚｈｕａｎ\n福爾摩薩\n筹码\n免费电影現役看護婦\n泄题\n捅她\n毛日Gm\n你她馬的\nG。M\n一四我\n肥东交警\nTsubaki House-Triple Shot Vol01校花三姐妹\n大众真人真事\n航空母舰\n喷尿\nCAO NI MA\n窝窝色网之家\n毛刘云山\n毛日GM\n第七色明星淫欲\n毛李洪志\nG-M\nFLG\n空中小姐的淫蕩性生活\n淫肉誘惑\n灯草和尚\n方晓日\n肉棒的抽插…。好爽\n挺动我的下体\n国防科工委\n空降\n大奶美逼\n肉逼\n人學生meimei 埼玉援交 vol。2 なな＆まい\n玉穴\n搔meimei色站大全\nｊｉａｏｃｈｕｎ\n淫荡meinv医生\n抽插35岁处女妹妹淫叫不断\n我操你\n倒臺\n操浪妻淫女\n插的我好爽\n汉 奸\n妖媚熟母\n極品黑絲\n激情网站地址\n…好舒服 奸淫 操\n賣財富\n星战前夜\n毛泽東\nTora-Tora-Tora Platinum Vol。9 Runa Mikami\nfuck新华内情\n一夜激情成人聊天室\nming yun\n曾慶紅\n免费淫色论坛\n日本情色论坛\n品穴\n黄色免费淫奸电影我和女友干爽后再她meimei\nmakinglove\nmaking\nｓａｏｂｉ\n日本激情淫电影\n激情电影在线观看\n游戏向导\n老师和我做爱好爽\n阴茎插入肉洞里\nya dian na\nNeoSteam\n色mm穴激情\nfuck新唐人\n火辣图片\n阴户紧紧包含着肉棒\n發正念\n淫色贴图365\n奇域\n百戰天蟲\n上有网\n跑跑\n毛李铁映\n小B样\n福摩萨\n私—服\n一卡多号\n桃色电影\n只允许法规年龄的人注册\n淫奸处女电影论坛\n浮夸风\n骚妈\n鸡巴插入花心\nsalop\n美女淫色贴图区\n处女\n插 9p\npussy\n六决不\n淫汁飛散大亂交\n情色 美女 -公安机关 -记者 -犯罪嫌疑人\n處女開包\n白虎阴穴\n淫穴贴吧\n又粗又硬的肉棒\n一党独裁\n毛禁书\n售軟體\n骚女\n插 8p\n金正日\n骚妻\n網路創世紀\n發$$倫\n毛昏药\n鸡巴……插死我了\n吴　仪\n商务部\n肉棒插入大肉穴\n狂插阿姨穴\n操的我好爽啊……我又要泄┅┅\n百家性大型色\n插 7p\n和meimei肏屄\n遊行\n偷电\n龟头粗壮赤红\n淫荡女医生穴\n官商勾结\n小鸡巴\n插 6p\npp243淫色导航\nfoseaon\n继母的爱液\n骚妹\n色网阴道特写强奸30岁处女全过程\n民主党\n17大\n火药制作\n法仑攻\n明星合成图\n色meimei大型黄站\n中國遊戲中心\n语音激情视频聊天室\n赛福鼎•艾则孜\n(現映社)オバサマ5人 潮吹き絶叫雨あられ!\n台湾20岁成人\n媽的B\n发**沦\nfuck傻逼\n免费强奸电影\n碩大龜頭緊緊頂著她的阴道\n抽插24岁处女妹妹淫叫不断\n西藏分裂\n56xp。com 电影免费\n邝锦文\n董賤華\nｔａｎｋｂｂ\n毛公安局\nｇｕ ｌｏｎｇ ｑｕｎ ｘｉａ ｚｈｕａｎ\n马克思\n午夜成人电影\nｐｅｎｉｓ\n群魔色舞\n制服狩\n江Core\n千岛湖之旅\n16大\n贛您娘\n大 纪元\n免费淫电影吴宗宪搞笑语录(25禁看)\n贱\n六四\n古龍群俠傳\n鮑嶽橋\n打到共产党\n垮臺\n激情淫女小穴\n高潮集錦\n销魂洞\n吃大便\n血腥內幕\n肉唇\n不要将你的精液射进我的小穴了里啊\n罂粟\n吴 官 正\n机八\n台湾獨立\n足球经理\n換妻雜交\n父女姐弟淫乱\n肉棒插入阴户内\n抽插33岁处女meimei淫叫不断\n重题工\n处女心经\n后面插入处女的肉穴\n毛发伦\n小嘴吐出龟头\n監禁陵辱\n大众成人网\n降半旗\n港激情图源成人色情论坛\n長瀨\n支那\n164网址宽频影院网\n毛法轮大法\n儿子的肉棒好大啊\n發^掄\n無碼淫漫\n香港成人淫电影\n众志成城\nG_m\n余震\nkamikaze girls 07 Nagisa Sasaki\nｆｉｌｓ ｄｅ ｐｕｔｅ\n肉棒嫩逼\n賤\n毛可卡叶\nfuck新闻出版署\n抽插\n肉棒放在阴道口\n打炮\n日穿\n八九民主运动\n靓穴美女\n美骚妇\nfuck信息产业部\n盐酸二乙酰吗啡\n吃鸡巴\n毛烂比\nQQ音速\n媚药少年\n三陪女\n地 下 刊 物\n免费淫乱电影\n胯下呻吟\n阿扁万岁\n欧美淫色贴图网址\n輪暴\n乡巴佬\n轮干\n測試员\nｘｉｎｇ ｊｉ ｊｉａ ｙｕａｎ\n免费痴女吸精\n他媽地\n鄧穎超\n93bb成人小电影\n毛新闻出版署\n傻比\n那嗎逼\n亚洲无码\n妓女自拍做爱电影\n嫩肉 穴口 舌头\n和親姐姐作愛\n肉棒在小穴四周游移轻撩\n干meinv小穴\n大鸡巴干进meimei的小穴\n激情色\n楊思敏\n蒙巴顿\n宫泽Keito 超美系模特儿引退作\nsuck\n车仑女干\n天下無雙\n黑手党\n王鹤寿\n温比\nAV女优和平凡男人1日结婚\n强奸免费淫电影\n遊戲幣\n干八个美女的小穴\n毛对日强硬\nNeko GN Vol。1潮姬 史上最高之潮吹少女\n紅燈區\n易當\n大陆娱乐黄站\n色情导航 排行榜\nｆｕｃｋ\n护士mm乱淫\n舔鸡巴\n自残\n玉蒲团讯雷下载\n最新n0242 东热RQ大乱交2007 Part1\n五月天\n嫖妓指南\n抽插30岁处女妹妹淫叫不断\n龟头顶住她的花心深处\n宁王府论坛\n拔出來\n八嘎\n屁精\n裸聊合法\n科技精英遇难\n2逼 老逼\n法棆功\n死GD\npaper64\nfuck外/挂\nicpcn\n冰风传奇\n梵高\n强奸 小穴\n3P\n溫家宝\n淋病\n陽具\n偷情網\n江賊民\n天图\n操草拟妈\n免费淫奸女Ａ片段试看 强*十八岁美女护士\njy2\nhero108\n死GM\n毛拍肩神药\n视频脱衣秀亚洲少女韩国无码片\n干你娘\n操出售手枪\n肉棒插进湿润花蕊\nfuck外-挂\n免费免注册淫电影\n色女\njx2\n温州娱乐鹿城黄站\n四人幫\n亚洲淫色俱乐部免费淫\n溫家寶\nｔｉｅ ｘｕｅ ｓａｎ ｇｕｏ ｚｈｉ\n魔性的meinv 鈴木麻奈美\n欣赏7感俏護士和医生22潮淫水图\n糞便\nfuck社。会。正。义。力。量\n淫奸妹妹穴\n龟头在阴唇上抽插\nｍｉｎｈｕｉ\n免费处女淫女下载看电影性开苞\n插你爺爺\n骑士\n如果你未满29周岁请自觉离开。\n许家屯\n火辣圖片\n用骚痒难捺的阴部淫荡地摩擦着粗壮的鸡巴\n干你妈\nLIUSI\n激情裸體\n杨建利\n轮\n毛干gm\n手记注册日韩5027部激情电影\n毛买卖枪支\n巨乳護士南つかさ20連發中出\n台独靠\n毛你妈逼\n免费dse淫乱\n挥发型迷药\n中央電視臺\n射精时必须注意的几件事\n那嗎錯比\n抽插52岁处女妹妹淫叫不断\n教你怎样延迟射精38岁以下禁免费淫电影a片\n发@@纶\n禽兽集团%少儿禁\n激情淫色贴图导航\nVULVA\n张 林\n操鸡\n鸟Gm\n日你老母\n毛国家民委\n綠色灵数碼\n江八點\n臀部猛烈的撞击\n輪\n易志熹\n插你爷爷\n懷念十五年前的大學生\n轮奸内射\n李红志\n4f\n大龟头顶住她的花心深处\n沾满蜜水又湿又滑柔软的 阴唇\n午夜情色电影\n大卵泡\n64\n老女嫩B\n3D西游\n公審江澤民\n淫奸妹妹的屄\n猶太豬\n爽死我了\n小B樣\n鸟GM\n共和\n淫电影//现场强奸19岁处女扒开小穴清晰做爱阴道裸体图\n淫穴贴图\n小穴 双手紧抓住 丰臀\nａｄｍｉｎ\n姐姐的肥阴户\n日本激情淫色\n打倒共产党\n插入处女的肉穴深处\n成人激情asp电影通道 黄色免费电影 sp色情播放\n3p\n俠客天下\n測試員\n淫荡美少女\n张 伟 国\n陈慧琳精品\n你二大爺\n它媽的\n你二大爷\n遊戲新幹線\n10 Little Asians Vol。 14～10人微乳素人娘～\n毛密宗\n露出了娇小鲜嫩的小屄洞\nｔｉａｎ ｄｉ ｘｕａｎ ｍｅｎ\n毛色情小电影\npao pao tang\n那嗎瘟比\n嗎啡\n成人激情视频聊天室\n你的小穴好深啊\n免费淫奸女Ａ片段试看 欢迎点击进入观看/林/心如/20岁处女偷拍/免费A。B片\n出售假币\n兒屆中央政治局委員\n毛海洋局\n骑她\n台湾万岁\n毛万税\n毛日gm\nkao\n14se淫色图片\n插烂meimei小屄\n网管\n肥逼\n做爱视频表演黄色小说\nfuck台湾民国\n扑哧一下鸡吧操进了逼里\n哥哥的肉棒好大啊\nKamikaze Premium Vol。 8\n本土無碼\n毛日gM\n无修正\nfantasyjourney\n银行账號\nhrichina\n躶\n淫水 …你的大肉棒\nFku\n黄色性交裸体图_做爱贴图性交成人贴图\n英雄世紀\n夢見る大和撫子 Pretty Little Asians Vol。48\n不注册免费淫电影\n18岁禁淫荡小处女\nggol\n美女视频图情色贴图区成人性光牒\n96\n出售枪支\n毛干gM\nｔｍｄ\n周锋锁\n猪\n鸦片\n激情成人电影\nboke\n免费激情视频聊天室\n数码精灵\n罌粟\n女也\n激情淫色游戏\n总统号\n毛新闻管制\n钦本立\n四人帮\n插 4p\n淫穴贴图论坛\n公告\n粗大的鸡巴穴洞\n操完姐妹操嫂嫂\n国务院\n完美时空\n小嫩穴把大鸡巴包得紧紧的\n龙新民\n足脚交\n注册激情电影\n三八淫\n机叭\n插 5p\n十六大\n吗啡碱\n免费mm黄色淫乱电影\n飙车\n又嫩又紧的阴户\n电影下载\n愛的奉獻\n下载免费淫电影\n林心如合成淫照片 林心如淫照片\n淫电影//现场强奸18岁处女扒开小穴清晰做爱阴道裸体图\n香港淫穴裸体图\n苯環利啶\nyuanmingxinsheng\n伸入蜜穴\n┅┅手握住我的大肉棒\n踢\nbong\n台湾总统\n色网阴道特写强奸21岁处女全过程\nGirlDV热门成人影片\n性虎色网\n插 3p\n愛的奉献\n毛狗日的\n怪兽总动员\n本站10018多部激情电影全部免费试看三天\n操黑\n毛司法部\n民政部\n毛丫的\n女乃\nｔｒｉｃｋｓｔｅｒ\n毛小日本\n肉棒插入阴道里\n操比毛\n半夜我把龟头插进妹妹的深处\nCs\n毛六。四\n林伯渠\n曲线消魂\n爱的乱伦\n乱世三国\n激情淫bb\n战国英雄\nbukkake\n色屋图片\n未亡人柔肌\n陈军\n鸡巴射出精液　 泄了\n毛孙文\n天怒\n斯蒂芬霍金\n黄孟复\n柔软的肉壁紧紧的包著龟头\n網絡公祭\n手淫\n遊戲米果\n脬\n日本成人电影 性生活影片 免费在线成人电影\n旅游局\n本站10002多部激情电影全部免费试看\n肉壶\n免费在线观看\n毛法愣\nＸ到噴屎尿\ndajiyuan\n喪服妻欲望\n日本meimei\n肉棒在子宫口游荡\n日本MM色比图吧\n歡迎G11情色電影院\n鄒家華\n無碼h漫\n朱巨\nBaopi\nGY\n论坛\n独裁\n龟头在阴户抽插\n抽插24岁处女meimei淫叫不断\n在线激情电影\n肉棒舌頭 蜜穴\n雙峰微顫\n有容奶大\n李总统\n操你媽\nGP\n你的逼……真紧\nGN\nGM\n金槍不倒\n藤武\n外挂\n劉曉波\n薄码\n斯诺\n鑫诺\n二B\n免费女Ａ片段\n免费淫电影吴宗宪搞笑语录(22禁看)\n银民吧\n毛肛门\n讨伐\n少女床上春色销魂\n毛麦角酸\nAV\n人民志愿军\n……好粗…好舒服\n狂插meimei穴 淫水流\n大使官\nｓｈｅｎｇ ｄａ ｆｕ ｗｅｎｇ\n岳武\n捏你鸡巴\nＮＥＴ\ntester\n美女偷拍成人小说激情网香港激情图源成人色情论坛脱衣美女做愛\n毛届中央政治局委员\n血腥内幕\n把肉茎抽出插入屁眼里\nｗｕ ｌｉｎ ｗａｉ ｓｈｉ\n帶套肛交\n操你娘\n狗诚\n曹庆泽\n美国成人激情网站\n清纯派meinv可爱的让人想狂干她！【RMVB/195MB】\n信息产业部\nCS\nhdw\n黑屄\n臺\n免费A片\n肉壁\n51淫色导航\njiejie的穴好紧啊\n二b\n套子\n三级片\nBc\n欧美大乳\n法稐功\n操参事室\n366成人网站\nfuck色情\n强奸处女学妹\n捅他\nttwg666\n暴淫\n淫妻浪女小说\n性宝贝淫色导航\n现场直播女主持人教你做爱\n日蛋\nMY\n耣\n陈功\n法论攻\n漢奸\n李旺阳\n阴唇\n几叭\n军长发威\n阴茎在阴道快感\n亚洲激情淫色贴图\n大唐豪侠\n移动联电信午夜激情电影秘密通道\n風騷欲女\n毛郭伯雄\n一九八九年六月四日\ngame master\n龟头操进处女阴道\n浪少妇和医生疯狂做爱\n狭义道\n毛干你娘\n可愛均整清純派美女可愛均整清純派美女\n虐待\n自慰抠穴\nfuck王八蛋\n机巴\n屌7\n洞抽少妇\nkang zhan\nfuck色情服务\n淫网\n星空幻想\n毛反华\nmo你全身\n毛人民银行\n监管局\n视讯情人聊天室\n自杀指南\n外掛\n免费成人影片\nGm\n肏\n毛二屄\n强*十八岁美女护士看完保证你打飞机免费淫电影a片\n捅你\n手伸到儿媳妇下体抚mo嫩穴\n蜜穴裡輕插緩送\n轮功\nmeimei的小嫩穴\n中华讲清\n黑客\n肛\n熟女护士\n唐人互动\nbastard bastard\n超级诱人销魂表情免费播放变身辣妹 插入妈妈白嫩的诱惑\n联通移动秘密通道情色电影午夜成人电影\n65情网色论坛色情电影\n歡樂性今宵\nJ8\n吹萧\n唐人互動\nJB\n谭震林\n毛激情电影\n免费淫奸女片\n流蜜汁\n淫聲浪語\nSM插进湿润的小穴\n盛雪\n猛男狂干美女\n發~掄\n美女裸体在线免费淫电影\nfeng yun\n肉香\n国家分裂\n欧美激情淫色贴图网\n薄碼\nextremeskate\n63xp。com 电影免费\nfawanghuihui\n龙与地下城\n毛干你妈\n發*淪\n人斩少女3赤裸忍者\n林慎立\n肉棒插入少妇嫩穴\n又肥又大的阴户\nLB\n飄流幻境\n厕所盗摄护士jiejie性感\n毛透视眼镜\n兔meimei贴图区\n日b\n高潮集锦\n处女的阴道特别紧\n45xp。com 电影免费\n小乳头\nU R靠\n成人电影，黄色电影，色情电影，免费电影试看\n花花公子\n警奴\n朱总理\n偷窥图片\n风骚少妇\n残忍老爸玩我小穴\nUR\n本站10010多部激情电影全部免费试看三天\n百家性大型黄色站\n抗曰\nFLG媽B發掄\n免费激情聊天室\n少妇房中自淫，和两勇男玩5P，操得直叫爽 操日本淫女穴\n茎\n朱德\nincest\n淫亂熟女\n國家總理\n注册激情电影(切记只允许法规年龄的人注册)注册快感吧！！\n里根\nTW\n金城安娜-痴女教官NO。7\n犯践\n黃色小說\n强奸轮奸美女视频\nWg\n毛干GM\n…好舒服…小穴\n粉嫩人妻在老公面前被人搞\n狂插日韩骚逼打飞机必看\n魔法飞球\n郭 平\n国外激情淫色贴图\n奸淫女老师\n发**抡\n犯踐\n亚洲午夜激情那个电影中心\n运营商\n老毛\nWG\n老比\n高岗\n悠遊網\n發$淪\n毛干Gm\n李山\n毛基督教\n淫妹阴毛小穴\n草\n老母\n性电影免费在线试看\n清华同方\n毛江独裁\n妹妹被操的淫水直流\n本？拉登\n奶头\n99BBS\n嫩穴美女挖嫩穴淫女嫩穴俱乐部\n黄色免费淫乱电影\n亚洲情色套图\n就去日\nfuck外汇局\n日Gm\n遇难\n操淫荡meimei穴\n牛顿\n曰本\n激情淫电影\nQB\n遇難\ntian zhi lian yu\n美女嫩穴 妹穴 亮穴 女人穴\n阿不來提•阿不都熱西提\n肏又大又嫩的屄儿\nj8靠\n日本淫色\n毛乳房\n日GM\nｈｕａｎ ｌｅ ｑｉａｎ ｓｈｕｉ ｔｉｎｇ\n保釣\n肉棒插入小阴户内\npnisse\n【亚洲/无码】Mugen Vol。11 Ai Hazawa羽沢愛！【AVI/ 699MB】\n魔法飛球\n骚妇激情\n我含住他的阴茎\nSM舔穴\n天安门事件\n本站10021多部激情电影全部免费试看\n做爱网有激情自拍裸体\n奸淫电影\njinyong\n监听器\n肛门潮吹 极太合体 原千寻 红音 桜田\nmeimei做爱\n百家性大型色站\n西藏独\n成人激情网站\n硬挺\nｐｏ ｔｉａｎ ｙｉ ｊｉａｎ\n殺你一家\nSb\n色情成人网站\n臺灣島國\n天安門殘案\nallbisexual。com\n扮开嫩穴\nSM\n婷婷口含鸡巴\n十八代\n搔meimei黄站\n日本成人\n龍圖智庫\n毛反动\nSB\n倡议书\n股市民谣\n可待因\n现场强奸18岁处女扒开小穴清晰做爱阴道裸体图\n亚洲成人贴图区论坛\nfuck我妳老爸\n肉棒抵住阴唇 插入\n？(SOD)超天然素材 桐島あゆみ\n舌战法庭\n極品波霸\n色97愛\nERA OF FAITH\n给你爽\n周容重\n喪服妻欲望下载\n奸她\n犯賤\n轉法輪\n抗日\n吳 百 益\n退黨\n你它媽的\n毛反共\n发轮\njiejie的肥阴唇\ngth\n49xp。com 电影免费\n午夜成人激情聊天室\n湿淋淋的骚阴道\n毛人大\n大陆娱乐鹿城黄站\n她妈\nｓｎｋ。ｎｉ8。ｎｅｔ\n苯环已呱啶\n姐姐的嫩阴唇\n摇头玩\n冈峦\n毛口交\n秦青的幸福生活\n亚洲激情无码BT\n阴囊\n毛文物局\ndelete\n色\n赵紫阳\n激情淫色贴图区\n張德江\n肛门\n肉棒用力的抽插着小阴唇\n射进妈妈里面\n成人情色网站\n免费在线黄色小电影\n163网址宽频影院网\n鬼公\nTokyoHot之极品meimei\nㄙㄞ你母\n奶娘\n激情玩穴电影\n苏绍智\n阎安\n错B\n肛門\n黑窑奴役\n性爱护士\n泡泡堂\n操臭化西\nZD\n免费淫乱电影下载\n粗大的龟头缓缓磨擦着湿漉漉的阴户\n告全体网民书\nｓａｏｈｕｏ\n在骚穴里抽插著阳具\nurTNND\nｌｕａｎ ｓｈｉ ｓａｎ ｇｕｏ\nd p p\nｔｈｅ ｗｏｒｌｄ ｏｆ ｌｅｇｅｎｄ\n回良玉\n两会新闻\n銀行帳號\n掌上靈通\n错b\n老师的阴道好紧\n免费看三级电影淫电影\n她娘\n在线小淫虫电影\n淫之学妹喷尿射得好高\n1989年6月4日\n毛江猪\npp点点通\n法伦功\n切七\n哲民\n护士小穴 空姐小穴\nQQ堂\n昏药\nｓａｏｈｕａｏ\n操 淫荡 好爽\nmeinv主持26小时全天激情热舞\n公务员的工资\n奸夫淫妇\n淫糜\n妹妹的阴户\n喷　射\n免费淫 电影a片。母子淫奸乱轮现场\n強制フェラ 失神セックス\n沒有人權\n烏雲其木格\nｇａｍｅ　ＭＡＳＴＥＲ\n玉女吹箫的姿势和技巧图\n联总\n丁香大型成人社区\n緊縛淩辱\n人性本色\n政治局\n日本淫电影\n蒋树声\n遊戲發獎員\n母子姦情\n鲍威尔\nfuck退党\n日B\n龙洲影院日本娱乐性免费电影\n紧缚凌辱\n妳马的\nAVW　FUCK　DOWN!最后性\n日你奶奶\n国家安全\n亚洲区人妖射精图最淫人体网站\nfuck驶你娘\n代表大会\n集團熟痴女川奈-南智子-澤宮有希-櫻田由加里4時間\ngM\n装B靠\n独占动画~女热大陆 file 2\n駛你娘\nfuck系统公告\n她馬的\n日你爺爺\ngy\n天使之恋\nqiangjian\n蒋经国\n操妳祖宗\ngn\ngm\nheatproject\n军妓\n臺湾國\n大鸡吧插妹妹\n阴唇阴道\nblowjobs\n耶和華\n奶大穴肥多条肉棒难满足\n瞿秋白\ngp\ndong fang chuan shuo\nsaga\n藏春阁【全免费】\n发^^论\nJOB\n無碼H漫\nyong bing chuan shuo\n数通\n政府\n用舌头舔着大小阴唇\n他妈的靠\nmeinv嫩穴淫水直流\n你奶奶\ndao jian\nfuck系统\n毛私-服\n省長\ng8\n平可夫\n肏屄\n老骚货\n省长\n包二奶\noperator\n国产骚货\n内射\n搔穴自慰\n项小吉\n那娘老比\n白虎陰穴\n夢城互動\n运营官\n淫电影//教你如何鉴别处男处女的方法\n浴室自拍\n凌辱？妥？学？@～？？Δ畏耪n後～\ngamemaste\nmeteor academy\n黑马@SQ Evolution Vol。3 柳濑遥\n日你爷爷\n拉客\ncS\nbt\n反封锁技术\n集體淫\n流口水啦\nlovebox\n六四点击\n亚洲淫色贴图论坛\n１８摸\n弟弟淫奸姐姐\n肉棒　高潮\n郝建秀\n毛农业部\n毛塞你公\n騷浪\n杨勇\n毛私/服\n日本激情淫色视屏\ncs\n王稼祥\n毛色情\n1001an。com\n臭化西\nK粉\n藏独\n法仑工\n抽插46岁处女妹妹淫叫不断\n成人bt\n膀胱\njiaochun\n学运\n毛国安局\n抽插47岁处女meimei淫叫不断\n淫奸小姨子穴\nｄａ ｈｕａ ｃｈｕｎ ｑｉｕ\n毛虐待\nfuck十五周年\n刘晓竹\n失密\n歡樂時代\n射精\n毛阴部\n不爽不要錢\n爱爱激情电影\n穷逼\n干阿姨小穴\n卖骚\n肉棒插入阿姨 ……爽呀\n傻瓜\n几八\n发论\n水从穴里流出来\n奇淫寶鑒\nｊｉｎｖ\n做爱淫乱小说\n军委\n毛粮食局\n我和二姨在车上舔\n痴女護士\n厕所盗摄meinv淫穴\n嗎個\n色网址联盟\n抽插婷婷\n时事论坛\nmeinv小穴贴图偷拍少女乳房\n賽博先鋒\nBT淫穴\n毛一贯道\n東升\n尚禾\n港澳办\n韩国激情淫色网站\nMai骚\n身寸米青\n法$$仑\n操妹妹湿穴\n性高潮\n轻轻地抽插著阳具\n插嫩穴\n婷婷激情俱乐部导航\n葵\n关卓中\n鹿城黄站百家性\n毛乳波臀浪\n胴体\n色网阴道特写强奸25岁处女全过程\n小姨子她饱满乳峰上的粉红乳晕\n蝴蝶谷淫图片\n国家领导人\nReign Of Tera 2\n美穴\n文 革\n新人×ギリギ 七海\n萋\n阿姨的大阴唇\n出墙红杏\nplayboy\n胸部\n奴隷调教\n我的淫荡老师\n顶级轮奸套图站\n操穴插逼\n藏獨\n免費性愛電影\n阴唇上摩擦着\n凯丰\n久之游\n想插穿她那肥嫩的小骚屄儿\n淫穴自拍\n摸你全身\n陈云\n發@@掄\n戳比\n国民政府\n遺言\n龟头顶住她的阴户\n日本淫荡meimei\nitem\nT。M。D靠\n聖戰\n文九天\n现代情色小说\n梅花网\njx\n婷婷俱乐部\n抽插41岁处女妹妹淫叫不断\n草枝麻\n阳具\n我 操 穴\n毛六四真相\n欢乐性今宵\n杨思敏\n和姐姐淫穴\n老江\n吗啡片\n黄色我该死免费淫奸a片偷拍MM接客宾馆\n超女世界Online\nGAMEMASTE\n洗钱\norgasmus\n黄蓉舔小龙女的乳头\n色情網站\n叔嫂肉欲\n塞白\n眯眯爱大型黄站\n妹妹陰毛\n色淫成人\n小穴\n成人色情\n大师\n押小\n夜夜色娱乐网\n大肉棒…插我……」\n春城娱乐(淫窝)\n骚批\n挺送 肉棒 激烈 撞击\n反社会\n法$$伦\nj8\n肛jiao\n抽插著阿姨的淫穴\n免费淫女在线看电影\n军用运输机\n機戰\n操逼毛\njiejie的阴道好紧啊\n肉棒不斷的抽插著她的肉體阴道口\n免注册淫电影\n淫荡少妇被轮奸\n天安門一代\nｏｎｌｉｎｅｗｏｒｍｓ\n李师付\n狂插喷爱液\n八九學潮\njb\n赛博先锋\nmeng xiang\n毛天安门录像带\n張万年\n肉棒插进阴户里\nh站\n干死客服\n不爽不要钱\n日gM\n美女淫水狂流\n淫穢\n做爱电影\n免费淫奸女Ａ片段试看 强*十八岁meinv护士看完保证你打飞机\nTest\nmeinv 淫穴\nｐｅｎｇ ｐｅｎｇ ｂａｎｇ\n毛鸦片液\n%共產黨爛逼\n丰盈雪白的肌肤、肥嫩饱满的乳房、红晕鲜嫩的小奶头\nwg\n完美時空\n黄色文学\n鸡吧插进\n65xp。com 电影免费\nｊｉｂａ\n蘋果日報\n淫穴\n免费成人电影图片\n抽插34岁处女meimei淫叫不断\n臺灣\n新闻办\n官人我要\n大鸡吧插meimei\n喷你\n騷水\nwG\nmeinv鸡吧图\n操母狗\n日本巨乳淫女\n現場直播~女主持人教你做愛\nfuck新手指导员\n天使之戀\n台湾18成人小说 免费成人电影\n王宝森\n賣軟體\n視娘辣妹\n毛妓院\n骚妇掰B\n超级乐者\n战牌\nur\n轮大\n江湖 淫娘\nyuming\n法$$侖\n鸡歪\ntw\n教你怎样延迟射精39岁以下禁免费淫电影a片\n仙境传说\n毛鸦片渣\n不玩了\n骚妇掰b\n免费A片观看\n小穴被插的好爽啊\n毛换妻\n和妹妹肏穴\n成人情色导航\n阴户抽插\n抽插34岁处女妹妹淫叫不断\n鲍鱼\n现代学生妹操屄水平真是高\n我上小姨子小说\n拔屄\n唐捷\n阴道抽搐一阵暖流自子宫深处涌向龟头\n抽插21岁处女妹妹淫叫不断\n聚友网络\nsm\n淫秽\n拉法叶舰\nsf\n观看淫电影\n新疆国\n國家分裂\n梓霖\nsb\n小女嫩穴\n把舌头伸入jiejie的\n林伟\n新疆國\n墮淫\n日本素人\n午夜激情美穴贴台湾性福联盟\n手记注册日韩5006部激情电影\n全國人大\n蘇秀文\n诛仙\n毛信息产业部\n毛四川独立\nri\n再奸\n法惀功\n發@淪\n爽女美图美搜性感图涩界成人电影网小小鸟绝色meinv写真图\nlisishijian\n轮奸\n毛透视软件\n國民黨\n色慾\n杨周\n何加栋\n肛屄\n肉便器\n屁眼\n毛罗　干\n金发表姨花蕊，少女的狭窄洞口\nSystem\n全身一阵颤抖 大鸡巴\nfuck小肉粒\n騷母\n日gm\n淫女乱搞免费三级片\n伸入jiejie裙内\n騷比\nqb\nａｒｃｈｌｏｒｄ\n毛台办\n法$$倫\n毛恶搞晚会\n法囵功\n强奸23岁处女全过程/*妹阴毛小穴\n天安门残案\n大雞巴\n二奶大奖赛\n毛阴道\n李师傅\n色情\ndick吕秀莲\n配合着蜜洞内肉棒的抽插\n我咧干\n恐怖主義\n五月天成人激情网站\nｘｉａ ｙｉ ｄａｏ\nmannweib\nfuck新华社\n獨立\n毛敬国神社\n干x娘\n又大又粗的肉棒\n又深又紧的穴\n抗战\n奸杀美女大学生\nfuck我操你\n毛他妈\n抗戰\n顶进少妇的花心\n疯狂的抽插起来\nｍａｋｉｎｇｌｏｖｅ\n搶 糧 記\n大麻树脂\n每天24小时循环更新的免费色情内容\nrxjhwg\n激情免费电影\n大鸡巴插进她肉洞里\n騷洞\n51xp。com 电影免费\n成人小说 SM 做爱\n肉棒顶的人家小穴好痒\n乱伦之淫骚穴\n操你妈\n传奇3G\nnetbar\n傅鐵山\n姜春云\n招妓\n口含鸡巴 插入阴道\n噗嗤一声滑入阴道\n浩天银科\n窃听器材\ntantra\n操陈晓宁\n群奸輪射\n色meimei淫穴\n宋慶齡\n女添男精狂射图\n免费淫色情电影 教你怎样延迟射精18岁以下禁免费淫电影a片\nhun\n手记注册日韩5032部激情电影\n达芬奇\n宏象\n六四回忆录\n洪哲胜\n淫荡娇妻打开双腿\n奧巴馬\n夜情\n毛林彪\n抽插54岁处女meimei淫叫不断\n我把艳姨给干了\ntiang tang\n口含龟头\n神出鬼没!3个丁字裤辣妹到处袭击肉棒(中文字幕)\n妹妹的小穴……舒服死了……\n夫妻多p\nｃａｏｂ\n肉棒噗嗤一声滑入阴道\n亚洲免费黄网\n阴茎插入少妇阴道\n点击这里试看美女做爱现场\n日本淫穴电影\n成人影院 激情影院\n电视流氓\n香港激情图源成人色情论坛脱衣meinv做愛\n喇嘛\n體奸\nu  r靠\ngame MASTER\n海盗时代\n我和姨在车上干\n赌马\n性生活\n威而柔\n免费淫奸女Ａ片段试看 处女第一次阴道做爱全过程(全套图片)\nｈｅａｔｐｒｏｊｅｃｔ\n日你爸\n法十轮十功\n大鸡巴插进小穴\n毛你老味\n梅花網\n海明威\nkuaik\n混蛋神风流史\n做爱视频\nfuck台办\n无码淫女\n审计署\n教室里淫乱狂搞\npute\n初流出！ロリプリ\n人美乳甜屄嫩操起来真爽\n性交内射\n操大逼\n发~~纶\nGC365\n麻醉乙醚\n毛十年动乱\n默哀\n夜色聊人\n处女第一次阴道做爱全过程(全套图片)免费淫电影a片\n鑫諾\n毛血腥图片\n自焚\n62xp。com 电影免费\n毛片空姐的嫩穴裸体明星夜总会小姐贴图\nrxjh\nCrazy Fuck : Miki Uehara 上原美纪\nMK 星云\n操逼\n吹簫\n免费淫奸女Ａ片\nａｎ ｈｅｉ ｚｈｉ ｍｅｎ\n情色聊天室\n淫奸医生\nｖｒｔａｎｋ\n系統消息\n联易\n希望之声\n淫电影//现场强奸20岁处女扒开小穴清晰做爱阴道裸体图\nＧＡＭＥ　ｍａｓｔｅｒ　\n断电\n廉政大论坛\n性感meinv淫穴\n经济社会理事会\n成人性交视频\n毛回教\n追查國際\n騷媽\n小唇被他的嘴巴包裹住\n105bb成人小电影\n骚乳\nsan guo ce\n性戰擂臺\n淫奸女电影直接试看\n删号靠\n性交图\n插的妹妹好爽啊\n性交圖\n發^侖\nvoachinese\n一夜性网\n露乳\n洪 传\nｍｅｎｇ ｈｕａｎ ｚｈｉ ｘｉｎｇ\n淫电影论坛\n凱圖科技\n儿子顶到妈妈的花心\n法仑功\n淫奸meimei电影\nrivals\n互友财富\n輪功\n人體攝影\n处女第一次阴道做爱全过程(全套图片)\nluan\n严家祺\n子宫口吮吸着龟头\nfuck新闻办\n回民人吃猪肉\n吸精\nfuck卫生部\n淫色网站导航\n淫蜜\n小泉纯一郎\n齊藤亞樹\n参事室\n毛反政府\n乱伦之淫穴网\n又粗又长的大肉棒\n泳衣+丝袜~让你销魂舒畅\n免费电影吸精痴女\n短信猫\n大 参 考\n强奸22岁处女全过程/*妹阴毛小穴\n未满19岁的请自行离开 ！\n总统的讲话\n淫奸jiejie电影\n痴女/淫乱 看護婦/女医 剃毛/パイパン！【AVI/723MB】\n姐姐的小穴\n情色ＢＴ\nfag\nｋａｎｇ ｚｈａｎ\n张榕明\n發論\n洗肠射尿\n品色永远免费淫电影\n赤裸\n他嗎的\n射顏\n激情视频女半夜播放成人节目免费裸体视频\n林心如做爱网有激情自拍裸体图片\n脚交\n淫奸电影 淫色猎暴淫电影 射雕淫女传 风骚小龙女 美女淫女图-小龙女淫女俱乐部影 在线淫电影\n仙侶奇緣\n北美自由论坛\n英雄online\n删 号\n香港\n发@@论\n毛干死你\n兩國論\n放荡\n狂插 肥穴\n淫奸小姨论坛\n检察院\n啊……快插啊……小屄要美死了……\n射颜\n争鸣论坛\n公务员工资\n充值\n小姨子她饱满乳峰上的粉红乳晕，金发表姨花蕊\nmeimei的淫穴被龟头顶住\n发抡功\n免费注册看免费电影通道\n傅杰\n毛反封锁技术\npenis\n掰开少女穴\n肉丝裤袜\n黄易群侠传\n你妈比\n阴沟\n舔我的龟头\n騷妹\n大鸡巴插进妹妹的小穴\n騷妻\n骑他\n大鸡吧插妹妹淫屄\n張 偉 國\nAngel Cosplay Vol。 2 新村爱加贺美由贵\nTora-Tora Platinum Vol。8 宫泽Keito 完全无修正第二弹\n滕武\n發^倫\n18岁少女破处\nRed Hot Jam Vol。6 露出初体验~Chia\n插後庭\n毛伊拉克\nU  R靠\n非常漂亮的日本美少女，日起火！\n阴水\n欠日\n工作人員\n胡嘉\n50xp。com 电影免费\n騷女\n鹿城大型黄站\n毛老毛子\n龟头用里插入阴道\n装逼\nｒｅ ｘｕｅ ｊｉａｎｇ ｈｕ\n抢尸\n娄义\n大鸡巴顶进妹妹的花心\n他娘的\n肖强\n押大\n运营人\n毛侨办\n免费淫电影a片\n淫蟲\n早洩\n屄屄特寫\n賣騷\n成人贴图区\n好大的阴户\n李维汉\n裸体插淫穴\n淫驴屯\n免费淫电影吴宗宪搞笑语录(28禁看)\n武力统一\n欧美清晰打炮淫乱人妖淫乱图\nshemale\nrxwg\ngameMASTER\n吹箫\n解放军镇压\n三国群英传\n發$$掄\n猛插小护士骚穴\n奸幼\npreteen sex preteen sex\nlong zu\n曹建明\n李红痔\nCAO\n穿著PE校服\n成人夜话\n法仑共\n骑你\n法仑公\n共青团员\n砍翻一条街\naszol\n免费淫奸女Ａ片段试看 瞄準她的肉穴插了進去……\n免费成人图片\n放蕩少婦賓館\ngamemy\n最淫的免费淫图图\n毛天安门一代\n滚那吗老比\n顺着大鸡巴湿淋淋的流下\n媽的騷穴被你插得爽死了\nａ片\n清晰內射\n工作人员\n激情玩淫穴电影\n亚洲乱伦之淫穴\n妹妹吸精\n国内动态清样\n骚B贱\n６。４\n贱逼\n阴毛\n浴室乱伦\n毛廁洞\n酥穴\n东热RQ大乱交2007\n舌头舔弄着小穴的内壁\n成人網站\n亚洲互动\n情色艺术天空\n激情淫色下载\n江口美貴 桃咲あい 早乙女みなき 雨宮せつな\n欧美成人贴图区\n骚\n黑鲍娇\n插入小姐的嫩穴\n追查国际\n成人色情图片\n台湾成人激情视频聊天室\n免费午夜艳情电影\n欠操\n淫奸………啊 好舒服\n肉慾\n毛伊朗\n抗震救灾\n互舔淫穴\n大比\n抗震救災\n狂插少女的嫩bb\nguild wars\n潮喷\n我和妹妹肏穴\n陰核\n出售此号靠\n賀龍\n和婶婶做爱\n香港免费黄色淫乱电影\n草擬媽\n淫奸jiejie穴\neve\n毛乳\n無碼長片\n少妇房中自淫，和两勇男玩15P，操得直叫爽 操日本淫女穴\n发~轮\n幹你妈逼\n日本黄色三级短片\n少妇贴图穴论坛 欧美淫色贴图区\n舌头舔着阴核\n强奸17岁少女全过程\n性爱　电影\n午夜免费视频激情聊天室\nfuck外专局\n马凯\n毛基地组织\n……好舒服 舔穴\n教你怎样延迟射精34岁以下禁免费淫电影a片\n干他\n女幹三虧\n之春\n超美女优MUGEN EX Vol。 7~浜崎ひめ\ntranssexuell\n發@@綸\n騒\n明星裸体写真图 美女网络视讯三点\n搶劫犯\n法~轮\n淫妻爱好者\n盐酸二乙醯吗啡\n杨怀安\n法$$沦\n大陆免费黄站\n新语丝\n抽头\n毛新疆独立\n騷\n免费淫电影下载地址\n风骚小阿姨 -新闻\n事实独立\n欧美激情穴淫奸\n日本女优\n曹你\n芸能人使い捨てM奴隷DX 聖乃マリア nike 新片！\n斑竹\n星際家園\n性奴會\n淫奸meimei论坛\n超爽艳图\nｌｉｕ ｘｉｎｇ ｈｕ ｄｉｅ ｊｉａｎ\n阎王\n美女视频短片\n臺獨\n法~輪\n毛姣西\n残忍的虐待强奸美丽的淑女\n釋迦牟尼\n放蕩\n洪 傳\n黑社會\n压迫\n联梦\nadult\n淫蕩\n母乳と巨乳と淫乱の完熟物語\n那吗错比\n扬振\n潮噴\n干X娘\nfuck私服\n强硬发言\n肉棒插入少妇的阴户内\n台湾青年独立联盟\n温 元 凯\n朱勝文\n肉棍狂插 阴精\n哥哥淫奸妹妹论坛\n法菕功\n干你\n18男人momoB\n抽插25岁处女meimei淫叫不断\n践踏女性\n輪奸大美女\n龟头通过子宫口直抵花心\n金玉天立\n抢劫犯\n肉棒插入阴道\n 瀬咲るな\ng。m\n毛叫小姐\nmeinv嫩穴 妹穴 亮穴 女人穴\n北极冰\n誘姦\nmabinogi\n嫵媚挑逗\nmeinv与猪性交电影\n水滸Q傳\n亚洲成人激情淫穴\n暗黑之门\n妹妹小穴\n淫奸姐姐论坛\n【亚洲/无码】【极品荡女-宝乃ありか 完結版！】【WMV/358MB】\n幹他\n夜色淫女导航\n嫩穴　狂　插\n操操你\n效忠天皇\n抽插小淫穴\n迷幻藥\n大鸡吧顶进小穴\n少妇房中自淫，和两勇男玩6P，操得直叫爽 操日本淫女穴\n淫妹妹成人情色论坛\n毛连战\n舔吮 乳房 干小穴\n幹您娘\n曰你\n開苞\n吞精\n情色成人视频聊天室\n韩国激情淫色贴图\npk1937\n淫獣圣戦 XX+圣獣伝 ツインドールズ\n肉棒插入meimei的肉缝\n本站10016多部激情电影全部免费试看\n毛洪兴\n去氧麻黄堿或安非他命\n斷电\n毛靖国神社\n紫瑞专业\n毛民政部\n大胆少女\n毛生鸦片\n妳娘\n武林群俠傳\n矽穀動力\n计生委\n张伯笠\n淫荡女生\n地球文明\n淫驢屯\n嫩穴meinv挖嫩穴淫女嫩穴俱乐部\n老瘪三\n和阿姨做爱\n一党专制\n操淫荡少妇穴 操淫荡妹妹穴 操淫荡姐姐穴\n和jiejie肏屄\n美腿\n幼交\n陈毅\nOffgamers\n插入姐姐蜜穴\n聚梦\n掌上灵通\n错逼\n淫穴摩擦龟头\nfuck骚\n新华内情\n我就色\n雞女\n操操他\n大鸡巴┅┅太棒了\n笨逼\n淫meimei成人情色论坛\n银行帳号\n鸡巴蜜穴高潮\n雞奸\n毛人民广场\n換妻大會\n亚洲激情影院论坛\n淫虫\n六四屠城\n江油余震\n幹的爽\n我妳老爸\n阿沛\n歡樂潛水艇\n百家性超级淫色图片\n操穴贴图\n淫虐\n张廷发\n免费a片日本淫电影\n被干的喷水的高清图\n刀OL\n打砸抢\n牛逼\n我的性启蒙老师\n毛XX\n淫色电影网站　\n毛推油\n奸情\n滝沢优季 Prisoner Of Love绝爱的监禁牢狱\n…用力干…嫩穴紧插\n东北风情熟女之惑\n阴唇湿润小妹舔乳\n破处\ngranado\n付申奇\n毛卖党求荣\n大 參 考\nmeinv潮吹\n盛华仁\n毛社会主义\n阴穴新玩法\n台湾自由联盟\nfuck孙文\n操淫荡护士\n傻屄\n分裂\n淫民激情文学图库专题\n奥巴马\n法**倫\n抽动肉\n現金交易\n超级淫图\n發~侖\n16dy-圖庫\n小穴流出汁液\n毛石进\nscrew\n老癟三\n陰戶\n日本激情图库脱衣裸体做爱\n手伸到儿媳妇下体抚摸嫩穴\n淫民俱乐部日韩激情片\n情色导航 激情爽电\n調理勞務系\n激情视频在线\n淫妹妹电影论坛\n被奸淫的女教師\n巨乳俏女醫\nggq\n免费淫区隐蔽入口\n雅典娜\n大中华论坛\n深夜進入少女家中玩強奸\nmeinv性交极品大片\n玉蒲团玉女心经\nvip在线成人电影\n*法*轮*功*\nｑｑ音速\n星蓝妹妹\nfuck洗脑班\n發~倫\n军事委员会\n色中色网址大全\n淫荡\nTABLE\n雞吧\ngamegold\n想上你\n拉凳\n李金華\nminghui\n神話R。Y。L\n五星紅旗\nyifeng\n1８歲淫亂\n迷幻药\n法**侖\n小穴被我操的红肿\n肉棒肉洞 肉缝\n代链\n人民大會堂\n毛强奸\n车仑\n毛烂屌\n日本淫色动画贴图\n巨奶\n好嫩\n女尔\n鰢\nSM援交\nwww。ccwcc。cn\n溫比\n多维网\n潮吹\n激情在线\n蔣中正\n大肉棍… …干的 …好舒服\n偷情男女\n后面插入处女肉穴深处\n你娘卡好\nlaqia\n女尻\n情色天堂论坛咪咪情色论坛\nArisa Kumada\n鹿城黄站bt\n快活林情色网\n入穴一遊\n淫水翻腾\n激情淫影片\n367成人网站\n女屄\n毛烂屄\n 夏树唯\n混沌诀\ngcd\n毛鸡叭\n淫荡的小穴\n阴核\ndh315\nmeimei黄站\n*李*洪*志*靠\n淫奸妹妹电影论坛\n色欲\n啊无卵\n酥痒\n蒂巴因\n磨得人家小穴……好用力……好酥\n弟弟淫奸jiejie穴\n免费在线收看淫色电影\n肏又紧又嫩的穴\n美女走光\n日你老娘\nTokyo Neko ATN-002 東京猫 Vol。 2 星川はるか\n官人我要2\n做爱图\n法**伦\n毛私服\n拉手冲\n清晰内射\n干你妈B\n台聯\n饿比\n免费a片\ngay\n台联\n流星蝴蝶劍\n乱伦熟女网\ngan\n叶群\n游戏中心\n母亲の结婚生活完全版\ncheng ji si han\n17岁处女小阴唇张开 淫水流出图强奸免费淫电影\n無修正\n陷阱超人嘩嘩曼\n雞叭\n大卵子\n奶子\n毛宋楚瑜\n法**仑\n龟头已顺利挣开她紧合的阴唇\n大纪园\n处女穴 阴唇 肉缝\n做爱自拍\n毛毛贼东\n龟儿子\n机机歪歪\n毛破鞋\n吸吮着红嫩的乳头\n月经不调\nshengda\n来回去舐她的小穴\n官人我要1\nｋｅｌｅ8\n小卵子\n売春婦\n情感视频欣赏\n宋美齡\n社会主义\n毛meimei淫色\nbjzc\n法黁功\n舌尖不断在肉缝里上下舔着\n激情论坛\n駛你爸\n性爱图片\n亚洲激情淫洞\n紅衛兵\n欲火焚身\n酥癢\n绵阳\nfuck驶你爸\n肉棒触碰前面的肉穴\n幹死客服\n火辣寫真\n妹妹的淫穴被龟头顶住\n宋美龄\n毛外交部\njiejie的小穴\n毛毛厕洞\n严方军\n金毛学生妹\n封神傳說\n林心如做爱网有激情自拍裸体\n苯甲基芽子堿\n女优爽图\n乱伦淫穴\n干你妈b\n学生爱国运动正名\n舌头伸到妈妈的小穴里\nyuanming喷你\n妹妹阴毛\n和熟睡的妹妹做爱教你一手\n中廣網\n激情淫色电视\n毛日本RING\n淫奸小姨子电影\nsegame\n美女性生活贴图\n马三家\n热 站 政 论 网\n鸟g M\n看牌軟體\n生奸内射\n調教虐待\n毛晚年周恩来\n日本激情片论坛\n妹妹的嫩屄\n一九八九六四\n猪容基\n气性坏疽病\n毛塞你娘\n操陈希同\n流星蝴蝶剑\n激情淫穴小妹\n换妻杂交\n戳那吗比\n胸垫\n阿姨和我乱伦理小说\n舌头舔小肉縫\n南蛮子\n张筱雨\n不良詞語@美乳女神RINA（中文字幕）\n毛温加宝\n姐姐的嫩穴 mm嫩穴\nfuck上妳\n鸡巴受不了了┅┅喔┅┅好爽┅┅我要泄了┅┅\n近亲相奸\n插淫女小穴\n你老母的\n香港情色俱乐部\n毛温总理\n創意鷹翔\n戴晶\n李 总 统\n激情視頻\n群奸乱交\n钱 国 梁\n國家主席\n日本成人淫色DVD\nfvc\n聖母\n口肯\n无码精选\n电车之狼\nShine\n大陆黄网导航\nｑｑ自由幻想\nnaked naked\n绵陽\n鸦片渣\n你全家\n卢福坦\na片试看\nfy0\n18dy粉红色高跟鞋\n拐卖\n15年纪念日\n毛原子能机构\njb靠\n吃精\n本站10018多部激情电影全部免费试看\n毛干你妈b\n母子做爱\n早泄\n成人a片\n戒急用忍\n西藏問題\n密穴贴图\nfv(\n高畫質~ 超絕美貌社長秘書 佐佐木渚沙！【WMV/102MB】\n女幹\n淫B浪女\n释欲\n成人激情导航\n她的阴道好紧啊\n激情影片网站\nfuc\npo tian yi jian\n白浆四溅\n采花堂\n扒开她的嫩穴\nfuk\n免费在线激情电影\n放荡少妇\n女干\n淫腔\n19岁女生主动让男生开苞全过程◥免费黄色电影\n帕巴拉•格列朗杰\n毛多维社\n毛鸡八\n日本成人乱伦\n龟头用力刺入淫穴\n汪东兴\n姐弟淫乱小说\n男女蒲典\nd7se淫色穴图片下载\n曹刚川\n西藏問题\n又大又肥的屄\n淫奸jiejie meimei MM 姨妈 舅妈直到流水\n三K黨\ncdjp李鵬*\n毛火棒\n破坏\nfu(\n我和jiejie肏穴\n男人本色\n雪白的屁股对着肉棒\n拳交\n舌头舔小肉缝\n仙界傳\n无码无套\n美女 成人\n鸡\n鸠\n毛meimei免费电影\n姚文元\n釋欲\n鸨\n淫荡校妓\n傻子\n六四回憶錄\n毛江择民\n常劲\n馬克思\nCNN\n鸡掰\n轮公\n灌滿精液\n电影免费试看片\n巨屌\n暴动\nｐｌａｎｅｔｓｉｄｅ\nBnB\n淫奸护士小说\n草你\n色站排行导航\n色女免费黄色小电影\n輪奸\n欢欢娱乐时空\n如此大的肉棒插的小穴快不行了\n发@@轮\nShit Shit\n毛他爹\nｓｏｕｌ ｕｌｔｉｍａｔｉｏｎ ｎａｔｉｏｎ\n少妇被操穴\nKamikaze Premium Vol。 25:特級神風~榊彩彌\n省長的兒媳婦\n又鸟女干\n毛法轮功\n太子党\n阴道好紧插起来那真是爽极了\n騷卵\n阴茎插入肉洞\n海盜王\n美女主持26小时全天激情热舞\n免费裸女视频聊天\n日死\n看高潮屋\n褲襪\n輪姦\n少年阿宾\n毛妈逼\n四川獨立\n情色成人论坛淫图导航\n巨炮兵團\n無盡的任務\n天暢\n紧紧的小穴\nｈｕａｎ ｌｉｎｇ ｙｏｕ ｘｉａ\n絲襪美腿、痴女、女同\n16dy-图库\nGAME\n赛她娘\n回民吃豬肉\n搏客Online\n菜B\n18dy免费\n新絕代雙驕\n淫色\n党卫兵\nfku\n姐姐被插的好爽\n14岁幼女B嫩阴\n耻辱轮奸\nflg\n民主黨派\n大东亚共荣\n美丽女教师的性经历\n小淫女\n毛江八点\n熟女家政妇 山本ちづこ\n厕所盗摄护士jiejie性感美腿图 肉丝美腿艳情淫欲\n巨炮兵团\n亚洲成人电影\n毛斯大林\n妓女\n弟弟的肉棒好硬啊\n抽插41岁处女meimei淫叫不断\n智凡迪\n毛监听王\n抽插19岁处女meimei淫叫不断\n偷拍\n暴動\n鸦片液\n超级舞者\n周天法\n摇頭丸\n支持達賴\n淫妹妹成人娱乐\n蔣樹聲\n可爱卡通小穴\n插的妈妈的小穴好爽\n海盗王\n红灯区\n日比\n免费在线淫电影\n毛新闻出版\nfuck鲜族\n爛B\n嚴雋琪\n欧美淫奸女直接试看\n肉棒插 淫水\n毛人民大会堂\n姐姐和我乱伦理小说\n大煙\n毛红卫兵\n黑马@meinv全身神秘黑让你爽到眼框黑\n民意论坛\n国研室\n又深又紧的阴户\n魔幻森林\n风骚小女孩湿身露体\nfuck上你\n贱女人-淫护士\n大陆淫乱meinv图图片\n逼\n在线播放a级电影\n淫奸小姨子论坛\n肉棒直插花心\n共党\nｑｑ幻想\n抡功\n免费淫电影吴宗宪搞笑语录(26禁看)\n北韩\n全 国 两 会\n新西游记\n恥辱醫護免费下载\n扒屄\nswdol\n高俊\n卖逼\n重生\n勃不起斯懦夫\n江贼民\n肉棒插在阴道里\n給你爽\n插小穴射了\n操护士穴真爽\n温B\n法$$抡\n阴茎被小穴紧紧包住\nzhengqing\n冒险岛\n趙紫陽\n抢劫\n母子奸情\n奸染\n鸭店\n欠干\n毛多维网\nｃｒｏｎｏｕｓ\n发~~沦\n温b\n共军\n欠幹\n骸卒\n姐弟激情淫乱\nlike999\nｘｉａ ｋｅ ｔｉａｎ ｘｉａ\n淩辱\n夏赞忠\n男人看的猛爽电影\n手记注册日韩5026部激情电影\nkuai le xi you\n尉键行\n法*轮\n毛莫索里尼\n喷精 3p\n法@@論\n菜b\nｑｑｔａｎｇ\nｓａｎｇｕｏｈｘ\n清华网管\n性虎色網\n发**仑\n拉丹\n洗浴间爽奸白嫩美乳少女\n姐姐的嫩阴户\n5kuai\n放蕩少婦\n激情性爱电影观看网\n什么什么大冒险\n天安门一代\n毛泽东\nㄙㄞ你娘\n嫩穴任我插 爽\n極品波神\n毛东突暴动和独立\n婚外恋\n毛系统\n毛几届中央政治局委员\nhuo xian ren wu\n共青團員\n台湾盲人美女在家疯狂做爱\n巨商\n毛靠腰\n发**伦\n小穴夹着肉棒摩擦阴道\n变态小日本街头强制脱衣最新加强版多片\n极品成人3gp\n傻B江核心\njiejie和我乱伦理小说\n我操\n淫の方程式\n肉洞 肉棒 肉缝\n群奸亂交\n月卡\n人 民 內 情 真 相\n洗着鸳鸯浴操屄真惬意\n鹿城最新娱乐bt 色情365\nｒｅ ｘｕｅ ｙｉｎｇ ｈａｏ\n天下无双\n狂操 插 干 老板娘\nｆ ｕ ｃ ｋ\nmm激情穴贴图\n騷批\n毛鸟你\nｔｉａｎ ｚｈｉ ｌｉａｎ ｙｕ\n毛法功\n本站10011多部激情电影全部免费试看\n粗长的大肉棒\n百家性淫网站\n官方工作員\nqi yu\nｓｈｅｎｇ ｚｈａｎ\n溫加寶\n苏家屯\n三個呆婊\n美女电影\n100bb成人小电影\n淫逼\nｓｈｕ ｍａ ｊｉｎｇ ｌｉｎｇ\n昱泉\n胡平\n啊……阿姨要洩了……\n吗b\n中宣部是的艾滋病\n亚洲激情淫色电影\n台灣獨立\n精液射入穴中\n张闻天\n哈批\n淫荡妈妈骚姊姊\n阿拉\n奸暴\n塞你老师\nマーメイドになった「恋から」ギャル。岡島優里（全编）\n操出售假币\n淫淫电影\n鋼甲洪流\n18岁 青▲学院中学1年生\n鹽酸二氫埃托啡\nｌｕａｎ ｗｕ ｔｉａｎ ｘｉａ\n处女开苞\nxizang\n色网阴道特写强奸22岁处女全过程\n阴道里的大肉棒用力顶着\n生奸內射\n89动乱\n毛伱妈\n淫奸女图片\n百家性大型黄站妹妹\nTokyo Hot n0249 : 南丽美 菊攻无限中出39连発\n用力扣进小穴\n官方工作员\n环保总局\nhanbiton\n你她马的\n地下教会\n毛卖卡\nfuck威而柔\n狂插少妇穴\n装屄呢\nshen qi\nfuck水去车仑\nQueen8 社长秘书のパンスト\n劳动保障部\n毛售软件\n共鏟黨\n亲哥哥……妹妹的小穴……舒服死了……\n大龟头顶住花心\n林心如掰穴自拍\n笨蛋\n花柳\n法^論\n錢 達\n聊齋艷譚\nfuck台湾党\n本站10001多部激情电影全部免费试看三天\n胡耀邦\n迷歼药\n胡錦涛\n王昊\n文胸\n颜色革命\n粉屄\n精液直射入她的小穴深处\n共匪\n阝月\n塞你老師\n拉皮条\nｔｔｍｄ\nqi ji shi jie\n少妇房中自淫，和两勇男玩2P，操得直叫爽 操日本淫女穴\n毛美国佬\n楊震\n李登柱\n我日\n免费在线看淫电影下载\n鹽酸二乙醯嗎啡\n淫奸女护士电影\n插如妈妈肉穴\n肉棍\n华夏Online\n手指拨弄着穴口\n劳+教+所\n肉棒\n傻逼靠\n屌西\n淫贱\n抽插22岁处女妹妹淫叫不断\n抵制\n骚浪人妻\n男女交欢\n你操得我更爽\n成克杰\n淫贼\n儿子顶到阿姨的花心\n长身丝袜美女~赤く烂れたマンコが痛\n操小姐b\n额尔德尼\n如果你未满23周岁请自觉离开。\n鈤\n恥辱醫護电影在线观看\nｈａｉ ｄａｏ ｗａｎｇ\n痴meinv醫生松島\n獲救\n刘国凯\n成人A片\n法@輪\n淫货\n男女交歡\n法^^輪\n史玉柱\n無碼絲襪\n阴道里嫩肉把鸡巴包得紧紧\ncnd\n政府软弱\n塔利班\n堂品色 永远的免费色堂永远免费\n陈宣良\n妹妹做爱\na$$hole\n大肉棒深深地插\n你马的\nustibet\n教你怎样延迟射精30岁以下禁免费淫电影a片\n杨得志\n聖火\n歡樂城\n毛酱猪媳\n我该死视频做爱\n奸淫护士\ndtfy\n7。22\nsuck你妈的\n少妇野外淫穴电影\n皇軍\n法@轮\n法^^轮\n毛民航局\n8岁女生主动让男生开苞全过程\n毛董文华\n操你媽的\n江擇民\n剑侠世界\n毛天安门档案\n溫總理\n她NND\nｈａｉ ｚｈｉ ｙｕｅ ｚｈａｎｇ\n丁香成人论坛贴图区\n免费淫色meimei电影网\n小逼\n濫交\n淫貨\n姦夫淫婦\n插入阴道里抽插\n发纶\n二B   帝國\n人 民 内 情 真 相\n身寸\n援交自拍\n安全隱秘\n美女出租车内强行进入\n二B   帝国\n杀人\n淫色影院\n婚外戀\n胡紧掏\n孙家正\n连线机\n毛蒋中正\n升达\nｂａｏ皮\n操宾周\n吗的\n55xp。com 电影免费\n毛瘸腿帮\n毛国家主要部委\n暴徒\n淫賤\n小穴的淫骚香味┅好爽\n亚洲淫图\n超級舞者\n兽交\nrenewal\n鸡店\n法^论\n如来\n無碼做愛\n腾讯\n骚女叫春\n程铁军\n成人午夜场\nｄｏｎｇ ｆａｎｇ ｃｈｕａｎ ｓｈｕｏ\n孟建柱\n色97爱\n毛萨达姆\nfuck售ID\nfuck驶你母\n李 总 理\n淫女小电影\n晚年周恩來\n黄色情挑艳情小说网骚meimei成人论坛\nmeinv嫂子的小骚逼\n法$$掄\n售号\n毛计牌软件\n裸露自拍\n毛是鸡\nTokyo Hot n0252 : 原田友美 高飞车美人教师精液轰沈\n腳交\n奸一奸\n丫头养的\nADMIN\nwizardsoft\n超淫荡丝袜meimei\n傻B靠\n做爱\n雞八\n双鞋的故事\n自拍寫真\n共產黨\n美国之音\n插的妈妈好舒服\n帐号\nqi ji\n激情影视\n教你怎样延迟射精25岁以下禁免费淫电影a片\n毛国资委\n毕加索\nfuck性交\n大東亞\n哥哥用力舒服死我吧\ntampon\n乱伦小说\nｂ样\n法@@论\n肉欲\nvrtank\n外专局\n暴干\nchuan qi\nAdmin\n可愛虎牙學生妹\n政變\n知的障害\n滑湿小屄洞\n如果你未满20周岁请自觉离开。\n發輪\nMADELOVE\n无码\n鎷\n日本成人激情短片\n李長春\nｂａｏｐｉ\n免费成人meinv\n舔 b\n美女主持24小时全天激情热舞\n爛逼\n王超华\n可哥精\n和meimei肏穴\n小骚屄儿meimei\n欲女\n饿b\n统计局\n王 秀 麗\n插穴 美穴 ……好爽\n淫奸女护士\n毛迷魂药\nｍｅｎｇ ｈｕａｎ ｘｉ ｙｏｕ\n八 仙\n石戈\n8 仙靠\n小穴流水妹妹手指插\n波佳达\n免费免注册淫色电影\n成人免费黄电影\n登辉\n饿B\nurban-rivals\n厦门大游行\n骚卵\n１９８９ 事件\n手记注册日韩5004部激情电影\n激情淫\n生徒胸觸\n手指不断在肉穴扣弄\n毛林业局\n暴幹\n生徒胸触\nzirui\n姐姐的小穴被大鸡巴插得好舒服哟\n无套肉棒浓稠内射漂亮屄\n邢铮\n操你老妈\n八九\n群p\n尉 健 行\n用舌尖在我的肉棒上舔\nFUCKYOU\n顏騎\n嫂嫂淫穴\nfuck外\\挂\n受精\nｂａｔａｒｄ\n毛干你妈逼\n東突暴動和獨立\n集会\n鸠屎\n抽插 磨蹭 穴 B 逼\n媽個B\n大陆鹿城\n和meimei玩插穴\n嘻遊中國\n钱达\n哀悼\n1989#天安门\n骚包\n售卡\n你老母\n藏婦會\n血洒天安门\n性器肛门~原千寻、桜井\nG m\n天浴\nｘ到噴屎尿\n成人18免费电影\n腾人\nzhongguohun\n毛她妈的\n姚月谦\n博讯\n色狼\n購金\n强奸19岁处女全过程/*妹阴毛小穴\nG M\n胡錦濤\nTibet独立\n天下貳\n毛花柳\n毛军事委员会\n軍妓\n毛小肉粒\n垃圾游戏靠\n擠母奶\n妈妈的阴道好紧啊\n成人午夜場\nsaobi\n亚洲激情成人电影\n东洋屄\n天安门母亲\n群P\n无须注册淫奸小电影 图片\n插小穴meimei直流水\n鸡吧菊花穴\n騾幹\n江西大余池江中学女教师(绝对真实)」\n援交妹\n强*十八岁美女护士看完保证你打飞机免费电影\n色猫\n月經不調\n木子论坛\n抽叉姐姐\n暴奸\nfuck傻B\n法论公\n幹你媽\n抽插45岁处女妹妹淫叫不断\n法论共\n老师干的我好爽\n辽宁女\n台湾成人黄色网站\nFuck You\n安全局\n八仙\n轻轻地抽动著阳具\n妈批\n美乳艷娘\nTENGREN\n龟头紧顶她的子宫口\n铀\n發**倫\n亂倫熟女\n大字报\n人民解放军\n又嫩又紧的屄\n柯庆施\n操你老娘\n何候华\n黄克诚\n亚洲激情淫电影\n李 祿\n法十輪十功\n美少女FUCK限界大挑戦\nf a l u n\n李长春\nPUSSY\n信長之野望\n李 禄\n母子乱乱论\n现金交易\n色色成人\n赛妳娘\n抽插42岁处女妹妹淫叫不断\n榮毅仁\n毛黑社会\n毛黑手党\n少女 被 插\n獸欲\nｘｉｎ ｍｏ ｊｉｅ\n妖美巨尻物语 - 风间ゆみ\n碧香亭\n89运动\n美女主持脱衣\n(國產)視頻-网吧老板玩穴\n肉棒压着阴蒂\n少林傳奇\n毛天安门屠杀\n湿穴\nｋａｉ ｔｉａｎ\n偷拍美穴\n色情排行榜 色情导航\n教养院\n毛人民\n共产\n厕所盗摄美女淫穴\n点击观看*林心如做爱*色情电影*免费淫电影a片\n口内爆射\n日爽\n嫩鲍鱼\n淫奸护士电影试看\n午夜淫电影\n淫水自乐\n最新99bb)有名女优~松岛やや\n戈扬\n美女直播做爱\n河利秀失控全裸+深夜成人節目\n都江堰\nDELETE\n搔妹妹色站大全\n香港激情图源成人色情论坛脱衣meinv做爱\nfuck四川独立\n裸体贴图香港成人电影网\n可怜系新人美少女甘南三才\n激情淫色贴图\n做爱在线观看\nKamikaze Girls Vol。29 铃房ありさ\n江猪媳\nRed Hot Jam Vol。10 高树圣良\n骚屄儿\n肉洞\n成人性爱视频 电影 聊天室\n去你妈的\n疯狂jiejie淫穴\nEarly Melissa Lauren\n李志绥\n完蛋操\n鲁迅\ninsert\nｇａｍｅｍａｓｔｅｒ\n馬英九\n燕玲论坛\ng m\n集体性爱\n工力\n鸡巴对准柔软的阴肉缝沟\n肉棒的抽插……干的好爽\n毛乔石\n插meimei淫屄\n法~論\n用他的舌尖舔着我的乳头\n亚洲淫娃\nfuck外_挂\njiejie和我乱伦理电影\n毛卖国\nSARS\nsphincter\n台灣國\n肉棒插入meinv的肉缝\n法论功\n西藏\n正悟網\n用你粗大的肉棒\n魅惑巨乳\n台灣国\n性保姆被雇主掀翻在高潮\n陈一谘\nfuck食屎\n達賴喇嘛\n日本RING\nfuck台湾独立\n銷售\n强奸19岁处女全过程/淫妹阴毛小穴\ngangbang\n毛刘少奇\n草妈\n本站是国家合法电影网请您用手机注册～！\n两国论\n风艳阁\n大法\n人肉搜索\n教你怎样延迟射精31岁以下禁免费淫电影a片\n揉阴核与嫩肉\n风骚侍女\n天下贰\nｙｉｎｇ ｘｉｏｎｇ ｓｈｉ ｊｉ\n猛戳阴穴\n腾仁\n大波\nMM屄\n插穴手淫\n毛蒋经国\n布什\n奸淫嫂嫂\n鸡巴\n腾任\n裸陪\n肉沟\n台独分子\n尸虫\n弟弟淫奸熟睡jiejie\n小姨和我淫BB小说\n幹你娘\n护士掰开阴道插B\nｐａｏ ｐａｏ ｄａｏ\n跟meimei肏穴好舒服\n一夜性網\n李总理\nhuan le qian shui ting\n17game\n小穴淫图\n少妇床上的激情\n顶进她的花蕊\n博訊\n天安门录像带\n牛魔王\n红十字會\n质检局\n一次可看四位meinv同床較勁很可愛的女主角\n骚穴淫液\n抽插19岁处女妹妹淫叫不断\n成人激情影院\npeng peng bang\n淫西\n肉棒在乳沟来回猛抽\n哭夭\n吸精少女\n回民暴\ndpp\n狂操丰满小姨穴\n明星淫圖\n帝國主義\n将肉棒对准她的穴口\n法^輪\n陰\n贱货\n三级电影网香港色情服务\n红楼美穴肛交淫图\n淫奸电影 淫色猎暴淫电影\n公车奸美女淫奸女电影试看。\n大鸡吧插又肥又嫩的屄儿\nTora Tora Gold Vol。18 : 冈田さな Sana Okada\n你大爷\n明星淫图\n欧美成人帖图区\n你大爺\n少妇房中自淫，和两勇男玩13P，操得直叫爽 操日本淫女穴\n狗操卖逼\n 菊地麗子\n李大師\n懶教\n操你妈靠\n成人自拍\n成人激情电影\n屁蛋\n大纪元\n小穴在上下套动\n丁字裤\n留学生--张丽 双插\nｓｉｌｋｒｏａｄ\n操雞\n中华大众\nｌｅｉ ｔｉｎｇ ｚｈａｎ ｄｕｉ\n求救遭拒\n操朝鲜\n强奸少妇\n毛卖号\n发$$沦\n淫奸美女图 淫奸刘亦菲\n毛泽东靠\n鸡巴 嫩穴 嫩肉\n淫色貼圖\n法^轮\n多维社\n無套內射\n李大师\n强奸处女学妹，摄像机拍下强奸阿姨。\n胡錦淘\n幹你媽b\n火线任务\n强*十八岁meinv护士看完保证你打飞机免费电影\nfuck信访局\n女学生拍我该死\n无界\n中國共產黨\n移动联通秘密通道\n精液射如妈妈的穴\n法~论\n淫師蕩母\n雞\n日本騷貨\n操达赖\nボインお姉さんお色気修行！ みさき優香\n艾滋\n人民内情真相\n省长儿媳妇\n日本激情淫色文章\n系统讯息\n大陆娱乐免费黄站\n多維週刊\njin wu shi jie\n大卫教\n蝴蝶逼\n激情聊天\n阿扁\n新天翼\n厕所性感meinv清晰淫穴\n17Game\n性福联盟\n插那吗比\n日本领事馆\n隂\n信访局\n淫meimei电影\n新光明\never quest\n名人大道\n毛美利坚\n利瓦伊汉\n滚那吗\n最强美乳初解禁\n歌华\n狂插meimei小穴\n去你妈\nhanbit\n摸咪咪\n社会论坛\n曾道人\n美女主持25小时全天激情热舞\n毛太子党\n打野炮\n御の二代目\n毛退党\ngun gun qiu\n陰屄\n那吗瘟比\n毛台湾狗\n淫电影网\n台盟\n淫乱小穴\n八仙靠\n免费秀聊视舞聊天网\nFLG 妈批\n原子弹\n私！服\n建设部\n中國性愛城\n戀愛盒子\n金枪不倒\n豔婦淫女\n李 總 理\n精子\n风骚淫荡\nSex Sex\n尾行\n手记注册日韩5033部激情电影\nwanwang\nParty\n欧美激情淫影院\n色女脱衣秀\n独立\n丰盈雪白的肌肤肥嫩饱满的乳房红晕鲜嫩的小奶头\n妈的骚穴 操 穴 逼\n操他妈\n毛你姥\n变态~幼男插嫩屄\n靠\n嫩穴【极品】阴蒂\n姘头\n毛马英九\n成人百強\nchao nv\n毛黎阳评\n成人百强\n天游\n六合彩\n妓女自拍\n駛你母\n猪猡\n交換夫妻\n自治机关\n不用注册免费小电影骚b免费色电影免费\nfuck台湾国\n騷屄\n87bb成人小电影\n毛你娘\n萨马兰奇\n温元凯\n開發\n張立昌\n厉无畏\nwangyou99\n嫂狂欢隔壁小弟打手枪\n拉皮條\n免费裸体美女性交电影\n胡錦滔\n傻批\n滥比\n搔妹妹黄站\n网址情色成人大全\n监禁陵辱\nPink Puncher)Double FACE\n你姥姥\n帕巴拉？格列朗傑\n玉杵\nd f d z\n愈快乐愈堕落\n传销\n法@@轮\n色情論壇\nBarefoot Confidential 46\n欢城\n你娘咧\n阴茎在殷素素的阴道里抽插\nVoyage\n呆比\n恥辱醫護bt下载\n陈水扁\n偷歡\n干姐姐小穴\n投毒杀人\n牛B\nfuck靠\nmarket\n法(輪)功\n阴户\nmeimei　MM激情\n日王\n后备专员\n色情服务\n免费淫色电影\n輪公\n肉棒在阴道内抽插\n毛你妈\n花心成人论坛\nmeinv性生活贴图\n免费成人美女\n特派员\n舔她小穴\n大家论坛\n正邪大决战\n世界经济导报\n法@@輪\n裝備展示\n肉棒狂插女人小嫩穴\n手记注册日韩5034部激情电影\n免费午夜爽片\ndse淫乱小说在线看\n舌头舔进阴道\n偷欢\n成人激情爽电影\n艳照\n她妈地\n强奸十八岁美女护士看完保证你打飞机免费电影网站\n梦幻国度\n自拍淫荡骚妇们的性福\nmeinv下体淫水直流\n毛王震\n滿洲國\n幼女嫩穴\n六四參加者\n和两勇男玩37P\n成人网站导航\nｔｉａｎ ｌｏｎｇ ｂａ ｂｕ\n在线玩美美免费电影\n法$论\n新疆独\nGM001\n六四风波\nsnk。ni8。net\n黄菊\ncai hong qi shi\nav成人网\n新生网\n扒开她的小穴穴\n氣性壞疽病\n张晓丞\n毛静坐\nｂｉａｏ子\n六四動亂\n乳此動人\n奴事件\n和两勇男玩38P\n性交無碼\nadmin靠\n滚那吗错比\n法*論\n免费淫电影吴宗宪搞笑语录(24禁看)\n针孔偷拍\n熟婦人妻\nfuck新闻管制\n89動亂\n作秀\n插逼清晰图\n手机注册看激情电影\npiss\n和两勇男玩39P\nrfa\n本？拉登\n毛meimei黄站\n米开朗基罗\n温暖湿润的感觉笼罩龟头周围\n本站10015多部激情电影全部免费试看\n核工业基地\n强奸轮奸meinv视频\nwowjingye\n自杀手册\n愛圖公園\n集体淫\n鸡巴插进小穴\n两性电影在线免费欣赏\n手分开嫩肉缝\n90bb成人小电影\n肉棒插干她的小穴\n和两勇男玩33P\n佛祖\n督察\n强奸41岁处女全过程/淫妹阴毛小穴\nchuang shi ji\n狂插美女穴\n罗　干\nchui chui\n台独\n郭盈恩挑战性交吉尼斯(对256男)直播全记录\n和两勇男玩34P\n卡通动漫meinv淫乱\nmo li bao bei\n抽插48岁处女meimei淫叫不断\n插烂她的小屄\n精液射在meimei的子宮\n六四之前\n猛插林心如小穴\n看完保证你打飞机\n肉棒插入美穗子的肉缝\n是雞\n和两勇男玩35P\n群交\n粉红的的奶頭粉嫩的阴唇\n香港淫穴\n網捷信\n欽 本 立\n谢中之\n龟头挤进了姐姐的小穴\n花园bt亚洲激情区\n穴淫贴图女人穴贴图穴淫液贴图\n黃菊\n陰穴新玩法\n成人色网\n和两勇男玩36P\n幼男做爱meimei做爱\nbi xue qing tian\nAV成人影院\n刘俊国\n刘宾深\n毛英雄纪念碑\n教你怎样延迟射精37岁以下禁免费淫电影a片\n操meimei湿穴\n傷亡\ngu long qun xia zhuan\n極品白虎\n嘭嘭幫\namateur\nfuck台湾帝国\n陈良宇\n殺手\n娇嫩的蜜穴 肉穴\nhelp\n大鸡巴 干进 小穴\n张开双腿　菊花穴\n龟头挑逗花蕊嫩肉\n穿豹紋的美女，很有野性美。看她咬著肉根就可看出來。\n超級乐者\n大 中 国 论 坛\n十三點\n滥B\n亚洲性虐\n手指还插在她的淫穴\n發^^綸\n公安局\n口味稍重 CD2\n社。会。正。义。力。量\n台獨\n毛军国主义\n鹿城黄站小说\n淫亂潮吹\n嘭嘭帮\n九霾\n性战擂台\n强*十八岁meinv护士 看完保证你打飞机\n走向圓满\n足球經理\n维护\n台湾午夜色情小说\n鰻の罪と泥鰌の罪とレズ陵辱\n免费淫奸女Ａ片段试看 少妇扒开阴道图\n一起玩游戏网\npuneta puneta\nrmb\n操抽你丫的\n快插吧!┅┅快┅┅\n毛台湾国\n法$論\n走向圓滿\n亚洲电影区\n发~~抡\n六四运动\n浪妻淫女\n法制办\n車侖\n性交电影\n淫奸少女穴\n品色堂\n毛去死\nhero\n日本骚货\n免费电影欧女乱欲\n白液四溅\n做爱激情小电影\n魔鬼曲線風騷女优青木玲 - FACE 68\n动漫色图\n最淫官员\nmuhon\n耶稣\n陽痿\n法（輪）功\n看完保证你打飞机免费电影网站\n毛三陪\n操小姨子穴真爽\n陈炳基\n高级卖淫女确实不一般\n黄色\nwowsupplier\n肉棒插入美穗子的肉縫\n熟婦騷器\nminpaonews\n推油\n用力操舒服死我吧\n肥穴姐姐\n亲爱的快操我的B\n耶穌\n民國\n走私\n干出淫水\n騰訊\n欧美无套\n海科數碼\n肥美的阴唇\nPink Puncher~美熟女淫乱乳娘~Erotica : 友惠\n美护士是男人操屄的目标\n民国\n月經\nda hua zhan guo\n鮑彤\n抽插43岁处女妹妹淫叫不断\n怎样延迟射精26岁以下禁\n新生網\n肥嫩的阴部\n中國成人論壇\n网域\nｘｉ ｙｏｕ ｆｕ ｍｏ ｌｕ\n阴缔\n鸡院\n成人淫伦\n淫荡女医生\n激情网站\n臺灣獨立\nrealextreme\nfuck傻比\n法~纶\n毛阳精\n鹿城免费黄站\n淫奸小电影\n手记注册日韩5009部激情电影\n南蛮\n怎样延迟射精19岁以下禁\n监察部\n我该死免费色情电影\n国土资源部\n周旋\n普贤\n就去誘惑\n东亚共荣\n操你爸\n紅 色 恐 怖\nAV-熟女 五十路 -故知新　-藤由美 53-\n成人激情电影在线播放\n数据\n免费淫电影吴宗宪搞笑语录(23禁看)\n激情淫洞网站\n龟头挤进了紧窄湿润小嫩穴\n免费淫*黄色电影a片\n吸吮肉棒\n新金瓶梅\nｘｉｎ ｊｉａｎｇ ｈｕ\n卖b\n林心如毛片\n捅入meimei的阴道\nｚｈａｎｇ ｃｈａｎｇ\n大*FA弟*子\n粉嫩粉嫩的小穴\n做爱淫图\n小幼女开处清晰大图\n阳精\n騷B\n驶你公\nluan wu tian xia\n鳥g M\n抽插40岁处女meimei淫叫不断\n沾满了淫\n毛马卖马屁\n卖B\n淫女在线看电影\n混乱冒险\nav成人电影网\n海科数码\n温逼\n徐建国案件\n顶进嫂子的花心\n色情激情电影观看\n奴役童工\n媽逼\n顶到妈妈娇嫩的花心\n成人激情网站大全\n抽插 …好刺激……好粗…\nfuck天鹅之旅\n情色电影\n国家主席\n毛你妈的\n不用注册免费淫电影\n周鐵農\n本站10004多部激情电影全部免费试看三天\n鸡巴插小穴\n桃园蜜洞\n星际家园\n强制执行 女体崩壊~1\n淫奸女教师穴\n臺灣共產黨\nfuck小泉纯一郎\n發~~論\n中文情色网站 免费母子乱伦\n嗯┅┅嗯┅┅舒服死了……meimei又要丢了\n操吃大便\n小穴流淫水\n李登辉\n毛江贼民\nHomosexual\n免费meimei黄色淫乱\n华夏II Online\n祁培文\n法**抡\n清秀巨乳学妹校园内被轮奸\n毛温馨\nfuck天安门录像带\nMedia\n司马晋\n欠人骑\n美女嫩穴淫水直流\nF。L。G\n人妻色誘\n毛卖财富\n南京大学法学院\n小比样\n插暴\n鹿城百家性大型黄站\n氵去\ngmworkers\n她媽地\n乳霸\n張 宏 堡\n肉棒挤入蜜穴\n淫水涟涟\n他妈靠\n六四之后\n交配\n柔软红嫩的小阴唇\n小嫩雞\n毛国民党\n淫奸姐姐穴\n塞你母\n吸毒\n豬頭\nGovernment\n妈妈屄好痒呀\n愛滋病\n啊… 你弄的我好通啊 不要……不要……\n99Baby Sana Okada in Maid\n走向圆滿\n滾滾球\n殺人犯\n天安門慘案\n柔软红嫩的小阴唇紧紧地护住她的阴道口\n165网址宽频影院网\nfuck是鸡\n和两勇男玩30P\n走向圆满\n白液四濺\n喔┅┅我又要泄┅┅泄了\n人妻色诱\n法~綸\n護士誘惑\n你以为你装逼就能骗我\n成人导航X色成人导航\n激情淫穴\n秘裂\n幼女开雹\n無毛穴\n张 昭 富\n吴邦国\n政治迫害\n血灑天安門\nhui huang\n毛华国锋\n性骚扰\n毛江戏子\n完全免费电影在线看\n和两勇男玩32P\n毛迷奸药\n親日\nu r\n阳具插入小肉穴\n幼女小嫩穴\n和两勇男玩31P\n日本淫荡妹妹\n毛反恐委员会\n激情淫窟\nON9\nPremium)オトナの変态●稚园 神谷姫\n淫情贴图\n靠夭\n假币\n鸡巴插入蜜穴\nlong hun\n妹妹的小嫩穴\njiejie的嫩穴 mm嫩穴\n怎样延迟射精21岁以下禁\ne Idol Vol。19 杏童なつ！【RMVB/698MB】\n完全自由注册看成人电影\n53淫色导航\n李德生\nchong wu wang\nfuck神通加持法\npp点点通色穴激情片\n看牌软件\n朱镕基\n淫女小穴\nLady Sonia-熟女丝袜 13\n情色影院\n用力狂插骚穴 ┅┅好爽\n罢吃\n将则民\n淫靡\nｋｑｋｉｎｇ\n八九事件\n少女露嫩穴 少妇嫩穴 meinv的嫩穴\n嫩 流 插 肉\n初中妹妹的小美穴\n法**掄\n双腿夹著我的腰部使我的肉棒进入的更深\n反中游行\nｂｉｎｇ ｆｅｎｇ ｃｈｕａｎ ｑｉ\n舔b\n哈宝\n阴精\nmo你鸡巴\n大鸡巴小浪穴\n兩個中國\n百纳成人激情影院\n蒙古鞑子\nfuck希拉克\n讓你操\n１８歲淫亂\n肏妹妹淫屄\n五奶小青\n可怜卖菜妇\n侠客天下\n毛牛逼\n爱色cc\n粉红穴\n舔B\nryl\n毛劳+教+所\n美乳\n烂游戏\n性娱情色导航\n野性家庭教師\n网 特\n阴道好紧\n插入jiejie蜜穴\n小MM\n中青旅創格\n王胜俊\n亂倫之公公與兒媳的秘密\n阴穴\njushang\n密穴貼圖\n激情MM\n学生妹\n粗壯的肉棒插入淫蕩的小穴\n绝对女神\nGAMEMASTER\n嫩b\n賤逼\n和两勇男玩7P\n令人喷血的春宫108姿势(组图)21岁禁看\n游衍\n淫荡护士\n游行\n操B指南\n裸聊\n血腥衝突\n几届中央政治局委员\n紫黛\n勞+教+所\n帝國傳奇\n美体艳姿\n群陰會\n国资委\n快樂西遊\n激情导航\n和两勇男玩6P\n小参考\nblueeye\n免费观看淫电影\n法*輪\n黃色文學\n姐都快被你搞死了！\n和两勇男玩49P\n姐弟淫乱\n深喉 Deepthroat This 8\n吾尔开西\n少妇房中自淫，和两勇男玩20P，操得直叫爽 操日本淫女穴\n封杀\nｔｅｎｇｒｅｎ\n档案局\n和两勇男玩5P\nㄙㄞ你公\n黄翔\n三挫仑\n刘丽英\n操春药\n超英赶美\n丝路传说\n夫妻俱樂部\n成人淫乱小说\n爛貨\n日本成人激情影院\n鸡巴 淫穴\njian你\n法轮攻\n反腐败论坛\n圣堂科技\n和两勇男玩4P\n毛性欲\n塞你爸\n激情電影\n賣逼\n美腿丝足\n毛奸\npusium pusium\n情色影院 导航 18岁禁看\nmeinv主持脱衣\n猪毛\n免费在线淫电影网\nＧＭ\n和两勇男玩47P\n床上激情淫乱小搔穴\n抽插52岁处女meimei淫叫不断\n肉棒插进肉洞里\n中央政府\n毛你爸\n日本女优8分钟长片\nfuck温家宝\n法^^论\n台湾一夜激情成人聊天室\n台湾\n滾那嗎瘟比\n握住阴茎看准肉洞猛的一下插了进去\nＧＹ\n毛发论公\n教你怎样延迟射精35岁以下禁免费淫电影a片\nMM的小穴\n和两勇男玩3P\n淫荡少妇穴少妇嫩穴 meinv的嫩穴\n倭寇\nwww。zqrl。com\n性交淫图\n和两勇男玩48P\n罢餐\n大阴唇姐姐\n男女激情淫乱图 林心如合成\n十景缎\n操淫荡少妇 操淫荡meimei 操淫荡jiejie 操淫荡护士 操淫荡 空姐操 老师操女老师 学生操老师 操老师小说\n靖国\n測試\n法$轮\nnavyfield\n游戏蜗牛\nsex\n摸你鸡巴\n和两勇男玩45P\n免费淫色情电影 瞄準她的肉穴插了進去……\n毛一鲜\n淫女吃鸡巴\n处女阴道库\n穷b\n阴壁肌肉紧紧挤压着棒鸡巴\n無產階級\n聖泉學淫\n史迪威\nfuck体育总局\nｍｏ ｆａ ｆｅｉ ｑｉｕ\n嫩B\n法@论\n和两勇男玩46P\n激情淫穴网站\n俞正聲\n林果\n陆定一\n國研\n点对点裸聊\n毛希拉克\n操陈功\n成人贴图网站\n毛撒尿\n法$輪\n革命\n小比樣\n罢食\n毛游戏宫理员\n毛巡查员\n妹妹的穴好多水\n北美仙人球碱\n梁擎墩\nmanager\n揚振\n网捷信\n初二辣妹\n龟头插入肉縫\n李登輝\n一夜九次郎\n狗日靠\n密洞\n色情小电影\n成人色站\n干老师小穴\n胡緊掏\n逼孕套\n專政\n免费淫电影，看完保证你打飞机\n朱琳\n鸡巴便结结实实的插进娇嫩的花蕊\n极品波神\n成人书库\n性欲喘息教師\n淫奸护士jiejie穴\n陽物\nｍｉｒ3\n测試员\nｍｉ ｚｈｕａｎ\n全免费淫色情电影\n快活林导航\n你媽逼\n枪决女犯\n一黨\nｂｉｔｃｈ\n日本激情淫穴网站\n三去车仑功\njiang shan\n小穴收缩颤抖著\n十八淫妹电影\ntriangle\n學生愛國者運動證明\nＧｍ\n毛外专局\n毛妓\n免费淫电影论坛\n方舟子\n露屄\n淫奸女护士电影在线免费观看点击进入\n测試員\n一党专政\n军火价格\n毛李先念\n59xp。com 电影免费\n玉蒲团\n舌尖刮著陰唇上的淫水\n法^^論\n我和小姨淫穴\n激情淫色文章\n屄屄特写\n免费试看片\n我是g和m\n掛機\n毛发论功\n如果你未满33周岁请自觉离开。\n它媽地\nmeinv孩含双棍玩内射\n金毛穴\n白粉\n游戏宫理员\n毛古方迷香\n他爸爸\n六四动乱\n毛贺龙\n日本淫荡女孩\n狂插 浪穴\n中华人民共和国\n淫水丝袜\n静坐\n玉蒲團\n露阴照\n和两勇男玩9P\nrplan\n肉棒插得我好爽\n大龟头戳进了子宫里\n斯蒂芬\n法@論\n淫妻的亂交免费在线看\n色情 激情 淫乱 卡通 高潮\n中华人民共和國\n维权\n产权局\n快樂AV\nfuck小鸡鸡\n最新两性激情小说\n正清网\n鸡巴顶开花瓣\n本站10019多部激情电影全部免费试看三天\n王瑞林\n湿润的嫩穴中\n和两勇男玩8P\n美丽世界\ntrannie\n六四風波\n聯眾\n干你老母\n毛三级片\n国研\n５街区\n免费视讯meimei\n路甬祥\n他媽的\n大麻油\n发**论\n集會\n潤星\n狂插meinv老师\n公车奸meinv淫奸女电影试看。\n我有网\n仁寿警方\n姐姐的阴道好嫩啊\n拉姆斯菲尔德\n真理组织\n日本meinv鲍鱼贴图区\n免费淫色情电影\n干你老比\n天安门档案\n日本mm性激情电影\n發~~輪\n女儿嫩穴操mm嫩穴操女人嫩穴\n又粗又硬的大肉棒\n摸你\n娇小鲜嫩的小屄洞\n欧美激情成人电影\nmeimei的嫩阴唇\n独占Princess Collection 若叶\n和jiejie淫穴\nf a l u n d a f a\n嫩穴 搔穴\n人妻榨乳\n监狱\n驚天動地\n雞掰\n毛六四事件\n愛田由~(Moodyz)女教師 レイプ 輪姦\n淫乱诊所\n透视眼镜\n熟女護士\nｌａｑｉａ\n色网阴道特写强奸23岁处女全过程\n一本道1137~陵辱巨乳~薫まい\n個批\n嫩BB\n毛你老母\n成人午夜场影院\n美女嫂子的小骚逼\n毛二B\n二逼\n誅仙\nshen hua\n毛双峰微颤\n假陽具插穴\n天安们母亲运动\nF_U_C_K\n尻庇\n發綸\n方励之\n性伦乱小说\n21cn\n歐美圖區\n新体操(仮)～妖精达の轮舞曲～\n吗比\nmm屄\n和熟睡的meimei做爱教你一手\n强效失意药\n连続失禁\n扶正肉棒，瞄準她的肉穴插了進去\n搞她\n淫婦自慰\n〓成人写真〓\n新江湖Online\n毛奸夫淫妇\n口爆\n毛刘　淇\nｓｈｉｔ\n彭珮云\n游戏监督员\n色情网址网上视频\n淫奸少女电影\n手机注册电影\nhai dao wang\nsw2\n公务员\n毛倭国\n疯狂赛车\n小穴里流\n迟浩田\n淫女淫图淫书淫片\nＵＲ\n不卡免费淫电影\n搏客\n亚洲激情淫色贴图网\n毛破坏\n桑拿房的淫乱图片 免费母子淫乱图片\nda hua\n快到高潮时，他哭了。\n賓館女郎\n發@@論\nsvs\n亞洲情色網\n娘餓比\nㄐ巴\n西游\njiejie的穴夹的我好紧小说\n趙 紫 陽\n黄色小电影\n┙\n┘\n┛\n成人午夜激情视频聊天室\n从后面插漂亮小穴妹\n┚\n┝\n├\n┟\n┞\n┑\n┐\n┓\n┒\n┕\n干GM的娘\n└\n妳馬的\n┗\n┖\n┈\n毛海关总署\n┉\n┊\n┋\n┌\n┍\n┎\n┏\n━\n│\n┃\n和两勇男玩44P\n┄\n┅\n┆\n┇\n和两勇男玩43P\n┻\n┺\n┹\nメイド召しませ 第一话\n┸\n┿\n┾\n┽\n┼\n┳\n┲\n東亞病夫\n┱\n9v9e\n┰\n弟弟淫奸姐姐穴\n┷\n┶\n┵\n┴\n罢饭\n┪\n┫\n┨\n┩\n射雕淫女传\n┮\n┯\n┬\nqian nian\n┭\n┢\n┣\nci77\n┠\n┡\n┦\n┧\n┤\n拔屄自拍\n┥\n极品成人激情影院\n5街区\n政治封锁\n和两勇男玩42P\n日本人淫乱操淫荡少妇操淫荡meimei\n司馬義•鉄力瓦爾地\n冯东海\n广东王\n国外美女黄网 美女激情SM处女淫水图台\n╈\n╉\n╊\n╋\n肉棒狠狠插嫩穴\n╄\n╅\n╆\n╇\n毛看牌器\n╀\nYukata Angel Vol。 1 : 矢崎茜\n╁\n╂\n色mm的嫩穴\n╃\n惹火身材\n日韩妹妹嫩穴淫图\n如果你未满30周岁请自觉离开。\n和两勇男玩41P\n色情导航\n02jam\n你的鸡吧好大啊干的我好爽哦\n如果你未满31周岁请自觉离开。\n成熟性感大meinv\n骚穴\n毛尻\n毛黄色电影\n積存的巨乳妻子積存的巨乳妻子\n抽插32岁处女妹妹淫叫不断\n1000y\n淫奸处女穴电影\n茎候佳阴\n毛法仑\nyi wang chuan shuo\nChink Chink\n赵晓微\nbeijingspring\n成人娱乐网\n和两勇男玩40P\n艳尻 北原多香子\n剛比樣子\n毛基督\nｃａｏ你\n抽插39岁处女妹妹淫叫不断\nfuck托管理事会\n操波霸\n本站所有电影完全免费无需手机注册\n他NND\n和meimei做爱\n屄屄\n滚滚球\n毛鸡掰\n賤貨\n人体摄影\n老二在她湿润的阴道口边摩擦\n法*论\n毛轮奸\n淫电影下载论坛\n插我\n欲火\nasgard\njiejie的阴道好嫩啊\n龟头被舔得又麻又痒\n问题论坛\ndick\n47xp。com 电影免费\n你的小穴好紧┅┅夹┅┅夹得我好舒服\n亚洲免费淫色电影下载\n毛看棋器\n骚屄淫水泛滥\n鬼村\nfagtard fagtard\n勃大精深\n亚洲色妹妹\n黑天使\n肉棒插进美穴\n长途汽车上的淫乱乘客\n仆街\n少妇偷情\n賴昌星\n肉棒的抽送 淫乱 用力干\n激情小说\n顶级影片禁书禁图\n客服人員\n制服美妇\n又粗又长的肉棒插入阴道里\n小澤圓(小沢まどか)的退隱作\n血B\n激情打炮\n毛高校群体事件\n吮吸小穴\n民主\nｌｏｖｅｂｏｘ\n操淫荡少妇 操淫荡meimei\n广电局\n淫荡少妇穴\n残废\n动漫激情淫色\nzhuanfalu\n成人午夜电影\nconquer\n毛江八\n家庭主妇 翔田千里 志村玲子\n安眠酮\n免费午夜成人影院\n肉棍狂插嫩穴\nChudi Chudi\n毛干妳妈\n毛舔西\n阳痿\n激情成人视频聊天室\n血b\n我靠\n淫液不断从穴里流出\n黃金聖水\n金庸群侠转\nｅｖｅｒ ｑｕｅｓｔ\n掰开她的小穴\nｇｙ\n日本淫荡电影\n经典免费淫电影下载\nｇｍ\n色妹妹乱伦理小说\nmoxiang\n張 伯 笠\n诱惑视频\n藏青會\n看\n李金华\n騷逼\n错那吗逼\n少妇淫心我要看淫心情迷电影淫心情迷电影\n美女\nｑｉａｎｇｊｉａｎ\n如果你未满34周岁请自觉离开。\n激情meimei聊天室\n激情美穴\n情色俱乐部meinv奸淫。黄色电影裸体电影PP点点激情电影\njiejie被插的好爽\n毛窃听器材\njiejie的阴户好嫩啊\n大鸡吧在她的小嘴抽送\n嘴唇吸小肉缝\ntibet\n毛鸡巴\n操78\nyong heng\n无套自拍\n在线看淫色电影\n毛烟草局\n嘩嘩曼\n 山咲Chiyuri\n屠城\n梦城互动\nmeimei嫩穴被狂插\n操测绘局\nxinguangming鸟你\n我操了美丽的女律师\n毛干妳娘\n人民报\n毛枪决现场\nｈｅｌｌｇａｔｅ:ｌｏｎｄｏｎ\ng。m\n无码电影\n毛纠察员\n教你怎样延迟射精28岁以下禁免费淫电影a片\n姐姐的穴好大啊\n王汉斌\n小騷比\n紅十字会\n耶苏\n姐都快被你搞死了\n毛以茎至洞\n怎样延迟射精23岁以下禁\n好弟弟使劲干姐姐\n美女 淫穴\n刘永川\n老人政治\n亞洲有碼\n鄧 笑 貧\n残廢\n前凸后翘\n点击观看*林心如做爱*色情电影*免费淫 电影\n强奸40岁处女全过程/淫妹阴毛小穴\n电影试看片\nfuck他祖宗\n张 钢\n强奸22岁处女全过程/淫妹阴毛小穴\n戴秉囯\nviprxjh\n罢工\n白肌美女？琴也\n欧美两性淫乱\n政治协商会议\n毛新生网\n血腥冲突\n他母親\n床上夫妻激情自拍图\n皮條客\n布莱德雷\n天鹅绒活动\n91bb成人小电影\n蕩女\n熟女颜射\n罗礼诗\n法@纶\n哥哥插入我的小穴\n你以为你装屄就能骗我\ndyonline\n毛法西斯\n屌妳\n台湾电影网龙游龙洲影院日本娱乐性免费电影在线\n刘　淇\n三个呆婊\n淫术炼金士\nnarod。ru\n日本大型黄色美女做爱\n欧美淫图\n爱抚淫女的阴户\n台湾午夜色情\n省長兒媳婦\norgasums\n圣角色扮演学园~大泽萌\n做愛\n韩 东 方\nfuck天安门\n美女发春激情在线电影\n法^^纶\n香港激情图源成人色情论坛脱衣美女做爱\n口活\nfuck氵去\nｔｉａｎ ｘｉａ ｅｒ\n王剛\n卖B发论公\n蒙独\n蕩妹\nkakajb\n狂插淫洞\nhomosexuel\n黄慈萍\n导航色站\nｊｂ\n倪育贤\n性爱日记\nmeinv穴\n智障\n海盜時代\nbbscity\n奇乐网\n肉棒在阴唇上摩擦传来快感\n色情视频聊天\n色狐狸网址\n大波粉B\n色情导航 免费色情电影\n靠北\n贱种\nwizard-soft\n*法*轮*功*靠\n狂插嫂子穴\n刘伯承\n少妇扒开阴道图\n毛佛教\n第十六次代表\n愛女人\nRed Hot Fetish Collection Vol。41若叶こころ\n性奴会\n高校群体事件\n黑社会大姐大性欲很旺盛\n南蠻子\nｍｅｎｇ ｈｕａｎ ｇｕｏ ｄｕ\n乌云其木格\n激情潮喷\n毛色情电影\n沃尔开西\n大波粉b\n激情小說\n妈浪女骚\n淫欧美淫暴\n在线免费观看性感摸特日本性感奶霸图片骚\n亚洲激情论坛\n激插\n18禁\n龟头顺着肉壁插入她的阴户\n阿姨的阴唇好大啊\n批林批孔\n成人激情午夜\n戴秉國\n6月4日\n第7色大型黄色网站\n蒙獨\n姐姐的穴好紧啊\n共產主義\n小嫩逼\n32283\n测绘局\n小浪穴┅大鸡巴\nchangeu\nｐａｏ ｐａｏ ｔａｎｇ\nfuck杀人犯\n毛董贱华\n盗电\n一边一国\n罗干\n成人激情免费电影院\n董文华\n韩联潮\n本站10006多部激情电影全部免费试看\n毛光明日报\n免费淫奸女Ａ片段试看 淫电影//少女被强奸到高潮时偷拍小A片\n激情妹妹聊天室\n文化部\n………好舒服喔\n操操你妈屄\n魔力宝贝\n軍國主義\n哭么\n法@綸\n蕩婦\n六四參加者回憶錄\n淫女穴影院\n操达赖喇嘛\n南华早报\n台湾成人免费情色电影\n淫色电影\n殘廢\n毛妳妈的\n十八岁美女护士电影网站\n多维新闻\n操Gm\n统治\n偷情\n你的肉棒干\nfuck推油\n毛小鸡鸡\nwhore\n丁关根\n监督\n大鸡巴坚挺插淫穴\n新疆独立\ni n c e s t\n尹庆民\nd7se淫色穴图片和美女发春激情3级片试看\nｎｅｔ\n(ATHENA)奥様欲情日記 エプロンをめくって犯して うずく人妻美巨乳スペシャル\n网址之家\n野性姐妹花\n法^^綸\n毛洗脑班\n殘废\n外**掛\n夫妇乐园\n亚洲成人激情淫影院\n毛联易互动\n日本激情动漫\n肏穴\n驶你娘\n虎虎虎~淫秽的身体 Tora-Tora Gold Vol。25 : Shizuku Natsukawa\n口淫\n东亚病夫\n狂操穴喷出阴精\nzhicheng163\n所盗摄护士jiejie性感美腿图 肉丝美腿艳情淫欲\n来插我\n激情淫洞小说\n江油餘震\n溫B\n处女阴道特写爽图\n本站10006多部激情电影全部免费试看三天\n徐才厚\n它NND\nㄐ八\n在线淫穴\nｓｏｎ ｏｆ ｂｉｔｃｈ\n午夜激情电影网\n自由亚洲电台\nkele8\nｎａｂｉ\n操机掰\n毛计生委\n挨球\n贯通两极法\n操GM\n狗比\n干妳马\n發$侖\n日本暴淫网\n魔獸幣\n情色成人\n濕了還說不要\ngame17\n臭作\n锤锤\n三個代婊\n肉穴\n舔雞巴\nf r e e d o m\n温柔女孩操屄竟如此陶醉\n李小朋\ncjsh\n色色五月天\n大航海时代\n我扒开了她的阴道\n肉棒插入小姨的肉縫\n淫乱工作\nH動漫\n美女做爱激情电影\n9ｙｏｕ\n公审李鹏\nｓｂ\n美国\n免费淫色强奸电影\n踢踢球\n成人午夜场，吸精痴女护士\n露陰照\n超级色情网站 乱伦色情小说 香港娱乐网黄站 性bt论坛\nｆｅｎｇ ｋｕａｎｇ ｓａｉ ｃｈｅ\n奴畜抄\n自拍写真\nBitch\n伊斯蘭\n冰風傳奇\n戳逼\n外**挂\n和两勇男玩50P\n抽插29岁处女meimei淫叫不断\n淫之插穴虐待破处流血\n众明星乱淫禁书\n现役看护妇电影下载\n江湖淫娘\npp244淫色导航\n歇b\n赌球\n小泉彩~黑人体内射精连续20发\n它妈的\n钱其琛\n發**掄\n母奸\n內射美婦\n外—挂\n淫女信息\nfuck死gd\nnacb\n毛抑制剂\n是鸡\n大陆性美女淫穴\n狂操操小小姨穴\nfuck死gm\n幼交图嫩女兽交\n混沌诀靠\n情色文學\n鮮族\n中文情色网址导航\n奧美電子\n多党执政\n少妇被鸡吧操\n和两勇男玩52P\n情色文学\n含住我的小鸡鸡\n臺灣帝國\n免费淫电影网址大全\n操你祖宗\n龜投\nnabi\nmeinv护士被奸\n毛塞你老母\n紅火女郎~真央~壓倒性的震撼力~汙染女人的快感\n嫂子的阴道好大啊\nhai dao shi dai\n色迷城\n宁王府淫色\n爆草\n受灾\n和两勇男玩51P\n受災\n操表妹操\n王冶坪\n梅花屄\n新疆打砸抢\n豬容基\nwhoyo\n淫荡少女穴\n激情淫色\n穴淫\n贱B\n和阿姨淫穴\n香港成人激情影院\n外—掛\n客服人员\n作ai\n亞洲淫图\n大膽少女\n黨中央\n干妹妹小穴\n我和小啊姨在车上做\n又肥又嫩的小骚屄儿\n华西列夫斯基\n小平\n和两勇男玩53P\n淫穴 大鸡巴干 好爽\nnage\n国际足坛五大\n成人激情图库\n毛李弘旨\n嫩B弓虽女干\n客户服务人员\nyuckfou\n抽你丫的\n网龙\n龟头碰到子宫口\n988wow\n制服美婦\n发**纶\n又大又肥的阴道\n卖软件\n刀online\n毛官逼民反\n狂插小穴\n穴海\n秦 晉\n妹妹社区淫色\n秦 晋\n征服小姐\n贱b\n非典\n龟投\n战牌靠\n古柯\n羅幹\nBiaozi\n淫荡教师穴\n吹喇叭\n大鸡巴顶进meimei的小穴\n淫妇自慰\n套弄鸡巴\n笨屄\n獸奸\n學生愛國運動正名\n激情小浪穴\n法轮工\n淫城乱伦屋\n白嫩meinv穴\n新斷線\nWhitey Whitey\naika\n淫水横流\nqqpp\nqqr2\n摇头丸\n滚那吗瘟比\nAsian Hot 018 岸田香織 女陰破壊\nKOKESHI COWGIRL Vol。29 石川优季奈\n女人被迷奸的时候也很爽\n新手辅导员\n大鸡巴在她的小嘴抽送\n美女与猪性交电影\n经插进我的穴了\ndfdz\n卡通美女淫乱图\n杂种\nrepent\n買賣\n贝肉\n百家性大型黄站\n穴爽\n激情最新网址\n肉壁包容着鸡吧很舒服啊\nfils de pute\n毛分裂祖国\n毛龟孙子\n性無能\n黄色小说\n妹妹淫图\nｂａｓｔｅｒｄ\n毛麻醉乙醚\n花瓣正在潺潺地渗出蜜汁\n我和jiejie的淫乱史\nClayton Dodge Clayton Dodge\n统独\n插妻子小穴\n旅館自拍\n雞巴\n他媽ㄉ王八蛋\n法~~綸\n本站10001多部激情电影全部免费试看\n激情\n李继耐\n毛摩门教\n操插妳\n顾顺章\n牛比靠\n六四参加者\n宏法\n日本激情淫色动画片\nmeng huan guo du\n基督教\n亀頭\n淫奸表姐论坛\n森田めぐみ 凌辱饲育\n管裏員\n赣江学院\n免费性爱电影\n阴道夹的我好紧\n靠么\nvgbh\n李老师\n淫奸表妹\n在线淫影院\n嫩穴 插干 …快插我吧\nQzone精彩由你而来\n阳物\n香港成人电影网五月成人情色论坛\n美女极品嫩穴\n多黨執政\n毛卖国求荣\n在线免费观看黄片Ａ片\n色情网址网上做爱\n┅┅别再磨了┅┅痒死啦！┅┅快\nrenminbao\n粗大的肉棒………我受不了\n毒龍舔腳\n爱妻淫穴\n洪 吟\n熟女亂倫\n奸淫……好舒服\nRMB\n少妇房中自淫，和两勇男玩17P，操得直叫爽 操日本淫女穴\n淫奸护士姐姐穴\ngame\n情色电影网站\n錘子\n民主墙\n欧美淫图 成人色情网\nonair\n油田总部\niuiuu\n抵制日货\n无网界\nｄａ ｈｕａ ｚｈａｎ ｇｕｏ\n操你老母\n乳頭\n肉簫\n免费成人激情电影\n104bb成人小电影\nJB靠\n姐姐的肥穴\n曹剛川\n淫奸美女\n爽死你淫图\n插入小姨的子宫\n插　护士\n藤人\n猛插\nｔｏｎｇ ｓｈｉ ｔｉａｎ ｄｉ\n荣誉之战I--钢甲洪流\n你老妹\nｊｉａｏｃｈｕａｎｇ\n藤仁\n罗富和\n榮譽之戰I--鋼甲洪流\n馒头屄\n氣性坏疽病\n妖艷醫生\n陰毛\nGameMaster\n导弹\n春色AV激情片\n18DY性爱电影\n賽你老母\n让你操\nｓａｎ ｇｕｏ ｈａｏ ｘｉａ ｚｈｕａｎ\n淫女淫洞\n刘澜涛\n老二\nhun luan mao xian\n抽插26岁处女妹妹淫叫不断\n欧美淫女在线看电影\n碧雪情天\n亚洲淫色网站\n极品成人A片无码\n肉棒插到花心深处\n毛麻醉枪\n路易\nfuck台独\n莎木online\n狂插日本学生妹\n耶蘇\n怪獸總動員\n淫女老师\n脑残\nｔｅｎｇｗｕ\n毛鸡奸\n全免费淫电影\n干你良\n街头篮球\n政治打击\n瀨名\n學潮\n露出了娇小鲜嫩的阴道\n吳德\n李禄\nfuck世界日报\n抽插55岁处女妹妹淫叫不断\n毛王昊\n陰水\nfofg媽的\n幹的你\n猫扑\n肉箫\n發*侖\n毛税务总局\n劉奇葆\n騷貨\n山口组\nfuck受虐狂\n易丹轩\n免费淫女看电影网站\n特码\nX妈牛比\n公子开\n日朱骏\n蓬浪\n王子傑\n淫护士\n插妹妹小穴\nminghuinews\n手记注册日韩5010部激情电影\n毛李瑞环\n李鹏*\n狗B\n哥伦布\n许世友\n吴弘达\n好肥的阴户\n97sese\n六四事件\n瞒报\n藤任\n群奸\n手指扒开红嫩的阴唇\ntum tum\n張朝陽\n毛轮功\n狗b\n鹿城最新娱乐小说\naids\n性饥渴\n捏你奶子\n毛性爱\n陳毅\n妹妹黄站\n监听王\n艷舞淫業\n華國鋒\n死亡笔记\nwg武功\n最强美乳おたま初解禁\n成人之家\n免费A片日本淫电影\n無碼精選\n阴茎把子宫顶烂\n最新99bb)有名女优~及川奈央-痴女教师\n魔法奇兵\n你妈了妹\n无界浏览\n錢運錄\n欲望\n捅比\n中青旅创格\n免费试看激情成人电影\n操 gm\n比卵\n鸡吧插进肥嫩的小骚屄\nBT激情\n艳星诱惑\n操嫩穴\n日本熟母\n操你大爷\n强奸23岁处女全过程/淫妹阴毛小穴\n中原油田\n监管\n大纪元新闻网\n免费成人情色激情视频聊天\n肉缝\nQQ幻想\n黄祸\n神经病\n干她妈\n插穴图\nGOQO\n整根阴茎没入她空虚已久的阴户\n法$$淪\n日本美女吃精图\n发沦\n强奸28岁处女全过程/淫妹阴毛小穴\n法@@綸\n世界都\n毛欢城互动\n95bb成人小电影\n亚洲激情网\n我的小穴好爽\n金瓶梅\n免费在线淫奸mm电影\n月经\n換妻\n毛梅花网\n紫彩乃潮吹赛红音\n马大维\n龟头挤进了jiejie的小穴\n成人色情网站\n毛十八摸\n你它妈的\n私處\n手指对她阴蒂的刺激\n中条英机\n尉健行\n免费电影未亡人柔肌\n国际法院\n王军涛\n賣號\n肛门拳交\n毛受虐狂\n吴德\n銀民吧\n2。23會議\n淫荡少妇穴少妇嫩穴 美女的嫩穴\n教你如何鉴别处男处女的方法(组图)18岁禁看\n操插你妈\n指导员\n少妇房中自淫，和两勇男玩8P，操得直叫爽 操日本淫女穴\n星戰前夜\n干洋妞穴\n遊戲監督員\n毛鸠屎\ntravestido\n出售此号\n裤袜\n美麗世界\n粗鸡巴 嫩穴\n和妹妹肏屄\n淫乱小说\n毛卵\n台湾18岁成人\n張宏堡\n日本精選無碼DVD\n毛杀人犯\n我将肉棒狠狠插入漂亮的老师小淫嘴抽插数十次\n强奸34岁处女全过程/淫妹阴毛小穴\n少妇房中自淫，和两勇男玩19P，操得直叫爽 操日本淫女穴\n淫水横溢\n虎虎虎~ Tora-Tora Platinum Vol。2 : 魔性的meinv 鈴木麻奈美\n操淫荡少妇 操淫荡护士 激情穴贴图\nｈｕｏ ｘｉａｎ ｒｅｎ ｗｕ\n淫女吃尿\n嫩缝\n外••挂\nfuck舔西\n亞洲淫娃\n人体炸弹\n法^纶\n亲亲小芳 色咪咪论坛 新亲密爱 六月龙盟 人间仙境情色网\n毒品\n发浪\n插妳\n教你怎样延迟射精22岁以下禁免费淫电影a片\nfuck他妈的\n天安门大屠杀\n痛批政法委\nda tang\n毛南联盟\n肉縫\n傻卵\n蒋中正\n毛独立\n免费成人A片\n黑省副省長\n封从德\n淫欲日本\n山口組\n黑省副省长\n淫贱妹妹贴图论坛\n發^^論\n淫女浪穴\n内衣模特露点走光\nｚｈｅｎ ｆｅｎｇ ｓｈｅｎ\n成人激情聊天室\n發@侖\n韓國自拍無碼系列之二)\n色情电影 色情排行榜\n毛台独\n妈……你的小穴好插\n原子能机构\n江绵恒\n东西南北论坛\n曼施坦因\n成人激情淫洞网站\n孤儿院\n淫色贴图\n胡緊套\n淫魔\n姐姐的屄\n长身丝袜meinv~赤く烂れたマンコが痛\n李老師\n狂插淫荡女护士\n毛玉杵\n亚洲艳情男女图吧\n射屏\n法@@纶\nfuck死GM\n学潮\nfuck死GD\n瑪雅網\n求你插插我吧好痒\n菜逼\n社科院\n台湾民國\n曾 慶 紅\n法耣功\n外交部\n耶和华\nｊｉｎ ｙｏｎｇ ｑｕｎ ｘｉａ ｚｈｕａｎ\n傻吊\nｔａｎ ｋｅ ｂａｏ ｂｅｉ\n勃起\n步飞烟\n骚逼大型成人色站\nTora Tora Gold Vol。16 香山圣\n教你怎样延迟射精26岁以下禁免费淫电影a片\n淫荡 穴 淫洞\n我淫我色淫色导航\n淫乱小姨子小说\n龜兒子\n令人喷血的春宫108姿势\n法~~纶\n台湾民国\n平成女忍者 奥义中出し陵辱\n法磮功\n毛叶剑英\n溫 元 凱\n猪操\n胡紧涛\nnigga nigga\n审查\n龟头压挤阴道\n用劲吸吮咬舐着湿润的穴肉\nｋ粉\n手记注册日韩5022部激情电影\n少女床上自拍\n狂插日本美女护士\n色mm激情论坛\n嫩縫\n育碧\n李少民\nwowassist\n熱 站 政 論 網\n陳曉寧\n毛小泉\n法^綸\n大波波\n江核心\n林长盛\n免费电影 美女电影\nkefu9you\n激战\nf_u_c_k\n89年的斗争\n廁所偷拍\n完全免费淫电影\n肉棒伸入她的口中\n幼女玩性\n法倫\nImage)ヌーディストGALビーチ\n天天干贴图\n小電影\n晚年周恩来\n淫妻的穴\n大鸡巴好趐┅┅趐┅┅快┅┅别揉了\n妈个b\n览叫\n咬着龟头\nfeng kuang sai che\n操操你八辈祖宗\n唐人街情色导航\n成人两性电影\n卖ID\n迪里夏提\n毛游行\n午夜电影\n熟女乱伦\n解放軍306醫院\nPP电影\n八九学潮\n┅干的妈好爽\n色情网站\n教你怎样延迟射精18岁以下禁\nmeinv口含鸡吧玩内射\n表舅妈与我\n9you\n操操gM\n走光偷拍\n万里\n色情导航 导航\nｓｐｅｃｉａｌｆｏｒｃｅ\n义解\nsheng da fu weng\n無碼淫女\n淫民欧美激情贴图\n好弟弟使劲干jiejie\n目标软件\n侬着冈峦\n淫女疯狂偷情\n淫 电影\n老逼\n海洋局\n汪岷\n鹿城娱乐\n毛卖ID\n狂插日本少妇\nliu xing hu die jian\n露点\n屎妳娘\nfuck傻子\n粉嫩美少女穴让你插\n毛淫\n肉棒在姐姐的阴道里\n操mm嫩穴\n搞他\n甜嫩穴\n民主党派\n大花B\n法侖\n插小穴的快感\n狗b新光明\n操操gm\n香港色情影星meinv裸体\n瘋狂賽車\n嫩鲍\n狗杂种\n黄片\nzhengjianwang\n小姨的穴\n白梦\n黑点\n幹妳馬\n亚洲激情片\n亚洲情色\n日领馆\n毛新闻办\n一本道\n赵海青\n殘疾\n猛干 添\n台湾18dy电影\nｙｉ ｗａｎｇ ｃｈｕａｎ ｓｈｕｏ\n陈至立\n含屌\n操吃屎\n浪女叫春成人招妓网色性网电影性暴力电影\nfuck吴仪\n人民银行\n午夜激情\n骚洞\n反封锁\n成人激情论坛\n相奸\n國賊\n春光外泻\n插她\nslanglist\n法伦\nRed Hot Jam Vol。 藤崎夕凪\n免费电影妖艷醫生\n龟奴\n后庭\n女教師 レイプ 輪姦\n趙 海 青\n民政局\n爆乳家庭教师\n毛傻卵\n绝品meinvが恍惚颜の绝顶快感\n毛邮政局\n外*掛\n卖QQ\n免费激情论坛\n艾滋病\n新石器時代\n外/挂\n美女护士被奸\n预审查\n胡乔木\nfuck小泉\n插的jiejie好爽\nmforest\npao pao\n熟女顏射\n骚比\nｓｔｏｎｅａｇｅ\n亚洲情色淫穴\n狂插日本meinv护士\n骚母\n用手指分开两片肥嫩的阴唇，露出了那娇小鲜嫩的小屄洞\n水扁\n司马璐\n龟头对准菊门\n袁纯清\n插你丫的\n法仑\n雜種\n毛三民主义\n外-挂\n粉红色的大阴唇正紧紧的闭合着\n奸淫空姐\n漂亮。女記者採訪時被逼姦\n星戰情緣\n断電\n屄儿好┅┅好爽!┅┅好舒服!┅┅\n毛性无能\n日逼\n18岁淫乱\n外/掛\n激情淫穴导航\n足腳交\n儿子淫奸jiejie\n对著暴露的美穴狂插\n法$$论\n聶榮臻\n草你妈靠\nBao皮\n三反五反\n龜头\n狂插\n强制浣肠\n淫兽学园\n韩日成人激情电影\n把肉茎舔乾净\nmwo\n法沦功\n法婨功\n龟头\n毛恐怖主义\nmxd\n天安門母親\n猖妓\n外-掛\n毛傻吊\n朱林\n韩启德\nHeterosexual\n色站导航\n大鸡巴顶进妹妹的小穴\n搞你\n你馬的\n妈个B\nKAO\n法@@掄\n香港激情性爱影院\n練完瑜伽功做？才爽呢(國產3p)\n老卵\n变态心魔女仲村 生中出\n骚水\n龜奴\n色情聊天室\n麻痹\n騷穴\n綿阳\n成人圖片\n拉登\n蔣彥永上書\n国贼\n綜聯數碼\n色情五月天\n肉棒插入meimei小穴\n魔域\n羞耻母\n免费成人两性影院\n吴仁华\n老师被干小穴\n抽插56岁处女meimei淫叫不断\n蒋彦永上书\n国外meinv黄网 meinv激情SM处女淫水图台\n性愛圖片\n色情导航 av电影\n牙齿轻咬着阴核\n淫荡姐姐勾引小弟玩操屄\n朱溶剂\n插淫妇骚穴\n李宏志\n邓笑贫\nfuck文化部\n肉棒抽插…不要\nMUGEN EX Vol。4\n她的阴道乳房真美\n毛彭真\n發論功\n煞逼\n台湾成人网站\n阳具插入小穴中\npartita\n插入内射\n世界日报\nCCTV\n大鸡吧塞进逼里使劲插我\n日本淫妞\n哥哥我爽晕了\n谭绍文\n邻居少妇 干邻居少妇 邻居的少妇 邻居美少妇 操淫荡少妇\n阳具对准她湿润的小穴口\nMUGEN EX Vol。 7~滨崎(MX-07)\n烂人\nㄖ\n日本色诱女郎图库激情成人视频聊天室\nBastard\npuSSy puSSy\nphuck\n去死\n盖世太保\nf a 轮\nadmin\n三去车仑\nsealonline\n鲍岳桥\n毛美国\n情色 激情 美女 穴\npao pao dao\n法~淪\n两性淫乱小说阅读\n完美國際\n发@伦\n天上碑\n胡XX\n肉棒 插得太深了\n强奸护士\n连胜德\n免费淫奸女Ａ片段试看 强*十八岁美女护士看完保证你打飞机\n淫穴把整个龟头吸住\n狂操\n青樓\n射入阴道里\n抢 粮 记\nMASTER\n女教师家访遭遇体剃毛内射肛交口爆喷尿虐待\n毛人大代表\n蜜汁 舔穴 塞入\n成人论坛导航\n极品金牌素人口交系列\n溫逼\n陽萎\n操操XX\n法$$論\nhkhk68\n胸涛乳浪\n亚洲淫色俱乐部\n奴隶调教\n盤古軟體\n网星\nｔｎｎｄ\n毛一夜情\n好爽┅好舒服小骚穴\n网易\nf a 輪\n狂插林心如穴\n綿陽\n西洋美女\n狗成\n368成人网站\n乱伦父女\n國產偷拍\nGold Angel Vol。7 纹舞らん 可爱\n发@仑\n倒台\n日軍\n性愛插穴\n15年前的“共和国卫士”\n又硬又大的肉棒\n插入……大肉棒\n叫春\n台湾小穴自拍护士做爱\n潮吹喷射地狱 不行触摸的喘气 4P肉奴隶中出\n毛卖B\n吗的靠\n将手指插入她的穴里\n法@@抡\n抽插20岁处女meimei淫叫不断\n北京之春\n不要！嗯──啊啊、啊啊！”\n亚洲激情帖图\n法（轮）功\n轩辕二\n獸交\n毛吴仪\n无码炮图\n大论坛\n同房\n美幼\n毛穆斯林\n肉棒就顺着湿滑的阴道口插进去了一半\ncdjp\n台灣狗\n哦……再快……小屄要美死丁……\n性色俱乐部\n发$$轮\nzl-fm。com\n倫功\n女学生嫩B\n外@挂\n风尘劫\nfuck他马的\n自民党\n毛一中一台\n毛吕秀莲\n青天白日旗\nt2dk\n骚浪\n封神\n58xp。com 电影免费\n大鸡巴用力的往小穴里面狠插\n陵辱面接4人淫乱娘\nｅａｒｔｈｃｉｖ\n春藥\n毛罗干\n庆 红\n她的阴道好深啊\n本站10020多部激情电影全部免费试看\n鸡8\n和阿姨作爱\nnnd\n地藏\n九評\n毛卖QQ\n操藏独\n淫语连连\n賤種\n如果你未满19周岁请自觉离开。\n戳那吗逼\n18摸\n插的meimei流淫水\n三去车侖\n臭机八\n膏药旗\nGAME MASTER\njiang wang\n外？挂\n日本激情电影\nqqtang\n柳下惠\n。gm\n莖候佳陰\n令人喷血的春宫108姿势(组图)22岁禁看\n猪头\n让我操高潮淫水一直流溅\n新闻管制\n無界流覽\n外？掛\n好大的阴户啊\n性感动感美女日本幼少女性感写真\n援助交际\n亚洲激情影院\n陰蒂\n强奸38岁处女全过程/淫妹阴毛小穴\n成人电影 淫穴嫩穴骚穴\n海之乐章\n小骚娘们的小嫩骚屄\n89风波\n蔣彥勇\nc a o靠\n我日靠\n玫瑰园\n鸡巴对准穴口猛地插进去\n嫩鮑\n毛滚\n毛管理员\n亞洲瘋情\n4-Jun\n淫奸表姐\n在线淫电影 看完保证你打飞机\n淫水狂喷\n万钢\n肉茎\n外@掛\n好……小……小骚穴\n發倫功\n……好爽喲 插穴\nfuck傻屄\n操插你\nｒｉ ｎｉ ｍａ\n亲美\n日本激情淫影院\nayawawa\n日你妈啦逼\n免费黄色淫电影一社会男子在宾馆里强奸一\n如果你未满27周岁请自觉离开。\n台湾成人黄色电影\n老兵\n大鸡巴插小穴\n八九年那个春夏之交\n卖号靠\n小穴疯狂地吮吸着我的阳精\n援助交際\n青楼\n三去车仑工力\n18dy村妓\n本站10008多部激情电影全部免费试看\ntui tui xia\n胸濤乳浪\n毛国土资源部\n骚棍\n刘华清\n盗窃犯\n手指抠肉洞 肉缝\n骚棒\n强奸美女狂插处女阴道\n大跃进\n插淫屄\n毛贱逼\n陰阜高聳\n月光宝盒\n周六性吧\n儿子的肉棒好长啊\n红色的小穴\n下贱\n梦幻之星\n本站10011多部激情电影全部免费试看三天\ntianxungame\n抽插51岁处女妹妹淫叫不断\nVIP激情成人电影\n89暴乱\n三级电影淫电影艳情电影\n毛穆罕默德\n杀手\n處女\n坐脸\n淫水爱液\n辅助程序\n純情尤物由美（中文字幕）\n鸡巴抽插好……爽\n25dy淫色贴图\n民警当副院长\n肉莖\n杨巍\n運营者\n鸡芭\n婊子养的\n我湿淋淋的阴道\n陷阱超人哗哗曼\n欧美成人漫画贴图区\n壓制\n如果你未满35周岁请自觉离开。\n和平演变\nji zhan\n爱神之传奇\n狗操\nｌａｓｔｃｈａｏｓ\n她的阴户好嫩啊\nmeimei的穴好紧\n鄒德威\n法$纶\n教你怎样延迟射精32岁以下禁免费淫电影a片\n妓女靠\n肉棒狠狠地插进小淫穴中\n刷钱\n淫穴电影\n毛氵去\n龟头插小穴\nMAKELOVE\n机掰\n毛舆论钳制\n啊┅┅用力┅┅用力插烂我的淫穴\n下賤\nmeimei的嫩阴户\n来干\n明星捐款\n毛卖比\n粗制吗啡\n插进 小穴 嫩穴\n新手輔導員\nTora Tora Gold Vol。15 香山圣\n海洛因\n毛他母亲\nmeinv极品嫩穴\n龟头顶破了处女膜\n口內爆射\n龟头插进了小穴\n悲惨-双胞胎一起被奸\n红十字会\n365情色网\n靠你妈\nmeimei的阴唇好嫩啊\n玉乳\n法*綸\n小屄又湿又滑\n王丹\n少女被强奸到高潮时偷拍小A片免费淫电影a片\n淫乱插穴好爽\n懶趴\n淫浪少妇和医生疯狂做爱\n操操Gm\n淫电影//教你怎样延迟射精\n呂秀蓮\n风骚欲女爽爽网\n情色图片成人小说\n虐奴\n陰莖\n教你怎样延迟射精41岁以下禁免费淫电影a片\n插 干姐姐 好爽\n淫色贴图小说\n狂插老师穴\n大*FA 弟*子\n8 仙\n大 中 國 論 壇\n操操GM\n屙民\n完美国际\n猛將\n猛将\n色情小说 BT限制级电影\n激情淫穴小站\n美女淫图\n無碼體驗\n不像我讓你打~\ncctv\n露毛\n项怀诚\n刘青\n成人午夜剧场\n老土\n50zhou\n把我的穴插烂了\n睪丸\n一元激情电影\n46xp。com 电影免费\n鹿城娱乐情色\nｗａｎ ｗａｎｇ ｚｈｉ ｗａｎｇ\n抽插27岁处女meimei淫叫不断\n中天萬方\n代理\n亮穴\n机战\n成人 激情 淫 另类\n狗日\n欧美做爱\n香港激情淫色贴图网\n发^仑\n台幣\n狂插处女穴\n巡府媳\n国家主要部委\n台湾独立\n色网阴道特写强奸24岁处女全过程\n幸运抽奖管理小组\njue dui nv shen\n东方红时空\n迷姦雙胞妹妹\n龟头挤进了她的阴道\n毛东亚病夫\n成人免费\n免费成人图片 色情美女做爱美女小穴写真\nGroove Party\n我跟妹妹肏穴\n性之站\n去你媽的\n台湾十八两性电影\n獨占動畫~肛門 1~長谷川\n好紧的阴道啊\n淫色全免费\n抠穴\n陰茎\n痛经\n杨元元\n操XX\n日本黄色电影\n餘震\n本站10007多部激情电影全部免费试看三天\n成人激情性爱电影\n大鸡吧插人阴户\n毛鸦片\n色mm激情图片\n吃屎\n睡着的武神\n發$$綸\n用肥嫩的小穴套弄着我的阳具\n毛吗啡\n包pi\n拒绝食堂\n谢非\n毛淫靡\n討伐\n疯狂jiejie教弟弟小说\n妹妹乱伦理淫穴\n情色视频聊天\nse淫乱姐姐小说\n法*纶\n鹿城黄站\n無網界\n李 錄\n三角裤\nKao\n吊子\n六月四日\n情色天崖\n丝诱\n成人 激情 淫乱 另类 高潮\n发抡\n十五周年\n都市2046\n抽插37岁处女妹妹淫叫不断\n林保华\n狗日的\n乳腺\n讨伐中宣部\n發~~綸\n89暴亂\n法崘功\n佛教\n淫奸少妇论坛\n文革\n日本免费激情学生美\n圣泉学淫\n成人情色论坛导航\n黄色网站\n日本天皇\n基督基督教\nPajawa Pajawa\n色mm穴激情图片\n残疾\n抽插26岁处女meimei淫叫不断\n张春桥\n反社會\n林樵清\n反攻\n少女激情自拍\n屄毛\nFLG妈B发抡\n亚洲淫穴电影\n舔屁眼\n18淫书\n剑网2\n日本激情淫色下载\n剑网3\n插穴淫图\n猛干\n劉延東\n精液射在妹妹的子宮\n我日你妈\nｍｏ ｌｉ ｂａｏ ｂｅｉ\nTibet国\nｗａｎ ｍｅｉ ｓｈｉ ｊｉｅ\n你老味\n『第一爽图区』\n用舌头舔着他的马 眼\n民族\n災区\n激情美女贴图九九情色\n青 天 白 日 旗\n強制飲尿便器小野悠美\n毛明慧网\n江山\n18dy电影\n操曹刚川\n未满20岁的请自行离开 ！\n中機電\n大鸡巴插进了的小穴\n灾区\nraycity\n日本激情淫色影院\n很黄很暴力\n災區\n小犬蠢一狼\n王勝俊\n纳粹\n17岁处女小阴唇张开\n老味\n藏独靠\n宋书元\n外$掛\n万维读者论坛\n下身\n瘟B\n銷魂洞\nAnal Asspirations 7\n親 美\n毛卖淫\n小穴被插\n灾區\n寫真\n高级应招女\n肉棍子\n发^伦\n毛唐家璇\n抽插53岁处女妹妹淫叫不断\n情色成人基地\n連続失禁\n外*挂\n空姐护士淫荡图片 淫荡女护士 淫荡少妇穴\nｇｕｉｌｄ ｗａｒｓ\n在线免费观看性感mo特日本性感奶霸图片骚\n瘟b\n反日\n教你如何鉴别处男处女的方法(组图)18岁禁看免费淫电影a片\n皮條\n臺灣黨\n百家乐\n又长又大的肉棒\n潮吹喷射地狱 不行触mo的喘气 4P肉奴隶中出\n61xp。com 电影免费\n风雨神州论坛\n麥角酸二乙基醯胺\n骚b\n钓鱼台\n虎虎虎~ Tora-Tora Platinum Vol。2 : 魔性的美女 鈴木麻奈美\n山本\n男人阁-套图站\n陸肆\n我和姐姐\n李录\n裸體少婦\n毛密洞\n淫乱熟女\n幹你媽逼\n抽插23岁处女meimei淫叫不断\n毛meimei贴图区\n毛东突组织\n美女激情视频聊天室\n春药\n鸡吧阴茎插嫩穴小穴\n同床\n賽維創世\n奸杀meinv大学生\n放荡少妇宾馆\n轩辕剑\n比毛\n游戏指导员\nfuck驶你公\n回民暴動\n一面做愛\n娇小鲜嫩的小屄\n温家保\nｂｉａｏｚｉ\n毛体育总局\n代开发票\n18成人网\n屌毛\n手记注册日韩5007部激情电影\n教你怎样延迟射精29岁以下禁免费淫电影a片\n毛手机复制\n黃片\n龟头缓慢地从我的阴道中戳入拉出\n少妇房中自淫，和两勇男玩12P，操得直叫爽 操日本淫女穴\nmeinv主持27小时全天激情热舞\n骚B\n手机注册激情网站\n手记注册日韩5016部激情电影\n信不信我撞死你\n十年动乱\n毛宋祖英\n沦功\n激情潮噴\n大鸡巴紧紧的插穴\n……插的……好舒服\n法$綸\n浪女淫水\n车牌反光\n两岸关系\n操gm\nmeimei的小穴好紧啊\n神甲奇兵\n淫乱jiejie小说\n掰开嫩穴 淫液\nｇｕｎ ｇｕｎ ｑｉｕ\n贱bi\n肏姐姐屄\n纪念松岛MM引退大作-松岛枫合集4\n爽穴\n淫护士小说\n比水\n她的阴道真紧\n王刚\n毛骚B\n發浪\n東熱空姐\n免费在线成人电影\n免费淫色电影网站\n超恶い状态10人の俗人たち\n睾丸\n免费淫女骚穴在线看电影\n操gM\ncom流氓\n皮条\n回民暴动\n流出大量淫水\n淫乱　爽电影\n淫奸女教师\n台湾帝国\n银行账号\n东土耳其斯坦\non9\n毛胡锦涛\n飞扬论坛\n激情动漫成人\nMarc Dorcel)Jesse & Jenna expertes du vice\n王克\n如果你未满24周岁请自觉离开。\n亚洲淫色\n琼浆玉液\n司马义•铁力瓦尔地\n靠你媽\n干的爽\n援交\n美女网站脱衣美女百分百作爱\n赛维创世\n百海\n抽插到高潮\n两腿之间\n王八\n哥哥的大肉棒\n扒开她的嫩阴道\n免费桃色电影不需注册\n六四運動\n点击观看*林心如做爱*色情电影\n熱血英豪\n三级淫水写真\n掩埋\n麦叫酸\n于浩成\n日本情色电影\n寇晓伟\n外$挂\n毛胡锦淘\nwg99\n先奸后杀\n惊天动地\n毛圣女峰\n游戏发奖员\n性感誘惑\n發淪\n一码中特\n坦克寶貝\n臺灣建國\nｍｏｘｉａｎｇ\n要色色\n气象局\n女儿的阴道好大啊\nfuck賣．國\nTENGWU\n18Chinese Japanese AV Girls\n倭国\n毛三个代婊\n王兆国\n阿姨的阴户好肥啊\n白烂\n和两勇男玩18P\n張 昭 富\n屠杀\n社會主義\n免费淫色情电影 20岁女大学生被强#(组图)全过程\n姐姐的阴户\n成人淫图\n王兆國\n醬豬媳\n狼友\n情色导航\n台湾盲人meinv在家疯狂做爱\n錯比\n外~挂\n中宣部是中國的愛滋病\n18岁少女处夜激情性爱电影\n毛六？四\n和两勇男玩17P\ntaip\n择民\n淫色贴图导航\n魔幣\n淫电影//教你如何鉴别处男处女的方法(组图)18岁禁看\ncnouc\n鳳舞天驕\nＧＡＭＥ　ｍａｓｔｅｒ\n梦~ 总辑篇-GONZO Vol。 4 Rio\n代练\n倚天\n手记注册日韩5025部激情电影\n操插深些\n激情淫穴影院\n抽插27岁处女妹妹淫叫不断\n娘饿比\n威而钢\n150m。com\n法@@沦\n射爽\n巨灵神\n成人网我该死免费视频\nfuck税务总局\n娘個比\n入穴一游\n猥亵\n89風波\n肛門噴水\nｗｅｎ ｄａｏ\n劲舞\n和两勇男玩19P\n张大权\njhwg\nbt淫色电影\n绿色环保手机\n毛阴蒂\n共产党\n在线免费淫电影\n陈蒙\nｓｅｘ\n野雞\n好粗的大肉棒\n插那吗逼\nda hai zhan\n张 志 清\n射雕淫女传9\n李洪\n王兆囯\n游戏管理员\n私立淫乱学园 完全版(中文字幕)\n好硬的大肉棒\n胡緊濤\n我和女大学生在公共汽车上做爱\n教你怎样延迟射精20岁以下禁免费淫电影a片\njiejie的肥穴\nｊｉｅ ｔｏｕ ｌａｎ ｑｉｕ\n小穴套着鸡巴\nma chang da heng\n免费淫电影吴宗宪搞笑语录(19禁看)\n毛温家宝\n胡喬木\n卖淫\n又嫩又紧的穴\nｊ8\n告全體線民書\n毛妳娘的\n日本激情漫画贴图\n武林外轉\n狂插小浪妹\n盜撮\n大B私服\n抽插36岁处女meimei淫叫不断\n遲浩田\n快乐到死\n乳丰穴嫩激情性电影\njhsz\n浪穴被你插得爽死了\n法**淪\n老师含住我的小弟弟\n運營組\n毛倭寇\n淫絲蕩襪\n武林外转\n毛工商局\n泡沫经济\n傅作義\n大史\n信息\n新黨\n新幹線\n宫泽Keito无修正第二弹\nu   r靠\nｒａｐｅ\n鸡鸡插入穴嫩\n精液浴\n发廊\n彈 劾\n极景\n干骚女\n強姦犯\nmeimei的大阴唇好嫩啊\n綠色數码\n毛硬挺\n狂插美女护士小说\n凸肉优\n龟头插入肉洞里\n和两勇男玩10P\n滥逼\n少妇小穴被操的很爽\n谋略高手\n用我的舌头刮着她的阴蒂\nlong yu di xia cheng\n一对熟女间的性虐游戏\n极品嫩穴 淫穴嫩穴 扒开嫩穴 抚摩她的嫩穴\n妹妹的穴好紧\n恐怖份子\n放蕩熟女\n双龙入洞\n弟弟的肉棒好大啊\n游龙在线\n法**沦\n腾武\n长长的肉棒插进我的阴道里\n毛叫床\n捅我\n品色堂永远免费开放注册中\n本站10020多部激情电影全部免费试看三天\n装B\n淫水直流\nｘｚｈｏｎｌｉｎｅ\n淫奸meimei的屄\nfuck温家保\n阿姨的肥阴唇\nGold Angel Vol。8 仲村りお\n免费黄色淫奸电影3级片a片手握着我的大鸡巴\n湿了还说不要\n吸精痴女护士\n香港激情图源成人色情论坛脱衣美女做愛\n杀你全家\n插妹妹淫屄\n自制手枪\njiba\n操b指南\n手机激情淫色网址\n盗撮\nav电影 色情导航\n毛托管理事会\n美女野兽做爱淫图\n赖昌星\nhong bao shi\n无需手机注册激情电影\n又顶到我的子宫\n和两勇男玩12P\n就去诱惑\n舔奶\n戳那吗B\n汕頭頻傳擄童割器官\n口射\n民族分裂\n裸体小姐作爱视频彩福联盟社区 大奶妹脱\n龜公\n放荡少女少妇\n台湾免费淫电影\n11月更新成人导航\nmaster\n亚洲激情meimei\nmixmaster\n钟山风雨论坛\nalod\n全国人大\n和两勇男玩11P\nora-Tora Gold Vol。4~~ささきふう香~~92cm G罩杯美爆乳\n法^沦\n我与网吧老板娘\nｍａｋｅｌｏｖｅ\n毛新闻出版总署\n密穴\ndragon\n27dy淫色贴图\n十八岁禁入电影\nWEB牌戰\nThe Best by Private 71: The Best Little Horny Asians\n和两勇男玩14P\n龟公\n毛你爷爷的\n奸淫电车\n包皮\n厕所盗摄护士jiejie\n舌头穴\n毛贱货\n淫奸阿姨论坛\n毛僵贼民\n淫奸小姨子\n滾那嗎B\nitembay\n和两勇男玩13P\n台湾成人19贴图\n肉棒好粗 插\n韩光\n熟女護士亂舞\n痴美女醫生松島\n豐唇豔姬\n吗啡\n基督\n经典炮图\n日片罕见3P加肛交\n爱液\n全家死光光\n和两勇男玩16P\n屄……爽死……了……嗯……嗯……快……快\n精选明星扒b图\n灵游记\n運营組\n淫液沾满阴道\n做爱大全\n田纪雲\n毛仆街\ncdjp李鹏*\n大鸡巴干妹妹\nｔｉ ｔｉ ｑｉｕ\n色情 导航\n三民主义\n毛阴茎\n私￥服\n毛一国两制\n和两勇男玩15P\nｊｉａｏ ｃｈｕｎ\n贩毒\nigxe\n情色导航 激情爽电影\n插入蜜穴\n毛嫩b\n作爱\n阴茎插小穴\n高級逼\n美少婦\n射精女皇系列Ladies In Lust\n高潮\nfuck外　挂\n免费激情网\n淫液从密穴 骚穴 嫩逼里流出来\n氢弹\n俄罗斯美少女惨遭蹂躏-嫩的不能再嫩\n漂亮女站着撒尿躺下被操\n發$$論\n朝天穴\n创世之子猎艳之旅\n阴茎被火热而湿润的阴道所包含着\n毛嫩B\n毛六-四\njinku\n劍俠情緣OnlineII\n蒋彦永\nav貼圖\n激情　电影\n陈良\nMM视频聊天室\n叫床\n沙僧\n萬里\n激情淫色贴图\n12岁处女开包现场\ngang jia hong liu\n髙潮\n中俄边界\n欲望成人3GP视频\n虎虎虎~ Tora-Tora Platinum Vol。1 金沢文子\n挂机\n毛发抡\n呆卵\n毛六。四\n外卦\n肉棒插入风骚少妇穴\nMM嫩穴\n少妇房中自淫，和两勇男玩3P，操得直叫爽 操日本淫女穴\n正清網\n好紧的阴户\n被10个猛男轮奸草得血直流\n情欲奸岳母\n林文漪\n狂插处女穴 淫水流\n情色導航\n激情套图\n倖存\nｐｕｓｓｙ\n發掄功\n民意\n吳 官 正\n一面打電話給現任男友\n運營组\n操女人穴 色情贴图 性交贴图\n白石ひより-完全攻略\n狗娘\n新疆獨立\n邓 笑 贫\n陰締\nmland\n下體\n插进鲜嫩的小穴\n你爺\nfuck尉健行\n迷魂药\nｍａｉ骚\n科洛斯\n淫色俱乐部\n小嫩鸡\n红 色 恐 怖\n张昭富\n你爷\n奇淫宝鉴\n韩桂芝\n18dy淫间道\n勁舞\n午夜免费爽电影\n发愣\n大陆激情淫穴小说\n汕尾事件\n严家其\n粗大肉棒插入蜜穴\n103bb成人小电影\n最牛钉子户\n强奸21岁处女全过程/淫妹阴毛小穴\n干少妇小穴\n信号拦截器\n舔吮她的阴唇\n嫩穴狂插还流水\nｚｈａｎ ｇｕｏ ｙｉｎｇ ｘｉｏｎｇ\n圣角色扮演学园\n色图乡\na$shole\n暴力虐待\n轮盘赌\n網龍\n艳乳\n淫奸熟睡的妈妈亚洲床上色情图库少女做爱\n杨林\n弟弟淫奸熟睡姐姐\n蝴蝶谷黄站\n又硬又粗的肉棒\n陽精\nDick\n法~~論\n变态\n毛二乙基酰胺\n爱滋\n日本成人激情\nfuck他母亲\nmakelove\n法@淪\n操淫荡 极品嫩穴操淫荡少妇\n抽插43岁处女meimei淫叫不断\n迅雷下载免费淫乱电影\n絲襪高跟\n春夏自由论坛\n20岁女大学生被强#(组图)全过程\n插他\n骚逼小姨\n抵制奥运\n全 國 兩 會\n观看淫电影 看完保证你打飞机\n汉奸\n美女嫩穴\n未满18岁的请自行离开 ！\n日本成人激情淫穴\n封神榜\n操meimei\n裙下風光\n两岸三地论坛\n牛B，牛比\n阳萎\n激情成人影院\n肉棒在阴道中疯狂抽射\n毛狗b\n首長\n臺幣\n大会堂\n喔…快…爽死\n暴操淫荡小妹妹\n免费性爱电影网\n免費偷窺網\n窩窩客\n淫B浪穴\n强奸犯\n首长\n哪有a片 百万美女贴图夫妻激情免费视频\n又肥又紧的屄\n李宏旨\nxi you fu mo lu\n避孕套\n黄色免费电影美女\n孫逸仙\n张伟国\n教你怎样延迟射精18岁以下禁 免费淫 电影a片\n新石器时代\n毛田纪云\n激情视频聊天论坛\n藏青社\n陰精\nRED HOT IDOL Vol。3 Concentrated Sex : 山咲舞 Mai Yamasaki 4\n成人激情asp电影通道\n乳此絲襪\n大鸡吧插meimei淫屄\n干您娘\nOn Air\n你他媽的\n淫水从穴门流出\n弟弟摸姐姐胸部小说\n密碼\n阿旺晋美\n嫩bb\n日本無碼o ~幼齒女學生\n法^^淪\nG。M\n吸毒犯\n三个代表\n綠色數碼\n温家宝\n肉棒插入子宫\n快干我!快干我┅┅我要被干\n我含着阴茎\n插你\n龔學平\njinv\n联众\n朱容基\n全球公審江澤民\n岡巒\n雞巴暴脹\n激情淫色网站导航\n抽插36岁处女妹妹淫叫不断\nMUGEN EX Vol。3 魅惑美少女 : 小日向葵 有码女优首度无码隆重登场\n复员\n激情裸聊\n亚洲淫影院\n欧美两性淫乱图片\n小雞巴\n穴口挤出淫水\n她的小穴插了进去\nporn\n毛信访局\n外~掛\n比样\n運營者\n法~~论\n聚丰\n新绝代双骄\n手指插的进小淫穴\n肥嫩的阴穴\n捏弄\n联合国\n黃色影視\n倚天屠龙别记 殷素素\n蒙古達子\n阴蒂\nmythos\n狂插 鸡巴狂插\n完全免费淫色电影\n美騷婦\n台湾 18 成人网\n弟弟淫奸jiejie论坛\n淫奸熟睡的妈妈\n窃听器\n独夫\n免费色情激情电影网站\n三級片\neqsf\n温家寶\n艳情小说\nfuck王乐泉\n外\\挂\n鎮壓\nfuck温馨\n女友下班后被别人献花接走给操了\n抽插46岁处女meimei淫叫不断\n操你八辈祖宗\n中机电\n世界日報\n插你娘\n情色六月天mm淫乱\n39CK\n王八蛋\nxinsheng\n血逼\n裙下风光\n自慰\n下流地帶\n鸡巴插进了的小穴\n小泉\n亚洲/无码\nfuck小穴\n熟女乱伦联盟\n下流地带\n阴茎\n爱国者\n贼民\n哥白尼\n賤B\n校园激情淫色\n爽女美图\n大便\n12次下跪\n王太華\n魏 新 生\n乳此动人\n淫亂軍團\n冈村宁次\n臺灣民國\n和条友做爱\n扒穴\nｗｗｗ。ｇｐｃｈｉｎａ。ｃｏｍ。ｃｎ\ncao靠\n田紀云\n舔屄\n誘色uu\n肏又嫩又紧的穴\n嫩穴肉縫\n賣B\n985985\nｔｘｓａｎｇｕｏ\n美女主持27小时全天激情热舞\n干你媽b\n流星學院\n少女嫩穴被插出血\n昏迷\n和两勇男玩29P\n毛伊斯兰\nse淫乱jiejie小说\n舔舔私处meimei嗷嗷叫\n陆肆\n狗屁\n马的\n狂操搔逼妈妈\n狗屎\n炮友\n骚妹爽图\n阿芙蓉\nmbs\n鸡巴 插 嫩穴\n刘少奇\n週六性吧\n做ai\nAV女优\n阴莖\n日本激情淫穴电影\n吴仪\n毛反日\n挖挂\n愛妻淫穴\n淫荡老师穴\n无网界浏览\n18男人摸摸B\n生命奇迹\n毛佛祖\n┅舒服死大鸡巴插\n舐她的小穴\n激情淫色视屏\n发~~仑\n淫奸处女穴论坛\n色空寺\n臺灣民国\n靖国神社\n毛学潮\n廖承志\n插你媽\n换妻\nmeimei乱伦理淫穴\n崔英杰\n桃園蜜洞\n嫩逼\n他ㄇㄉ\n大龟头在花心深处\n爆乳美脚RQ中出し鬼輪姦-- 森高レナ\n爱的奉獻\n捅进了她的阴道\n台湾女人裸体贴图\n戴红\n毛观世音\n喷雾型迷药\n抽插31岁处女meimei淫叫不断\n王辅臣\nsm女王\n春光外瀉\n大学骚乱\n苯甲醯甲荃芽子堿\n啊雅娃娃\n二乙基酰胺\n牛比\n亚洲激情 贴图区\n黑白无常\nfuck性爱\n本站10003多部激情电影全部免费试看三天\n发~~伦\n性飢渴\n惹火自拍\n黄色免费超级淫图\n大乳\n美女插穴\n毛厕洞\n林彪\n极品成人电影\n和两勇男玩24P\n傅铁山\n粗长的肉棒粉嫩的玉门里进出\n日本淫图\n龔学平\n幹ＧＭ\nㄐ掰\n军民冲突\n法~沦\n人肉\n萬鋼\n淫水流出图强*免费淫电影\n刘千石\n免费淫电影吴宗宪搞笑语录(18禁看)\n哗哗曼\n性爱 图库\n共产主义\n抽插18岁处女meimei淫叫不断\n九评\n乱伦淫书丝袜淫图\n和两勇男玩23P\n毛后庭\n免费淫奸女Ａ片段试看 令人喷血的春宫108姿势(组图)18岁禁看\n小姐打飞机\n华通时事论坛\n迷姦雙胞meimei\n免费淫奸黄色电影\n美少妇\n和两勇男玩22P\n計牌軟體\n激情XXX电影(\n徐匡迪\n生孩子没屁眼\n全球公审江泽民\n我草\n色弟弟\n无码片\n国民党万岁\n伸入姐姐裙内\n(盗摄)女子公共澡堂!\navi免费淫电影下载\nonewg\n她妈的\n和两勇男玩21P\n成人色网站\n少女一丝不挂做爱淫水直流！\n爱的奉献\n朱毛\n亚洲黄站\n近親相姦\n点卡\n刘正荣\n开放杂志\n嫩穴肉缝\n妹妹等你大鸡吧来插\n毛客服\n肥西\nmeimei┅┅爽快┅┅死了\n护士穴\nluan shi san guo\npp点点激情播放\n斧头镰刀\n胸罩\n公车淫奸meimei\n裸聊日本成人电影\n毛狂操你全家\n日本淫色贴图区\nmeinv做爱激情电影\n毛拉姆斯菲尔德\n淫奸嫂子穴\n和两勇男玩28P\n胡主席\n三陪\n烂鸟\n(SOD)芸能人 範田紗の超高級ソープ嬢\n小骚比\n下阴\n桑國衛\n蔵獨\n遊戲管理者\nying xiong wang zuo\n弟弟mojiejie胸部小说\n盛大富翁\n台湾成人18贴图\n手记注册日韩5013部激情电影\n圣者无敌\n和两勇男玩27P\n天主教\n龟头 磨蹭 插入 小嫩穴\n淫穴 夹 好爽\n弟弟淫奸jiejie\n炼功\n免费淫奸女A片\n江八\n俏臀摄魄\n操薄一波\n作爱聚会\n和两勇男玩26P\nｃｈｅｎｇ ｊｉ ｓｉ ｈａｎ\n品香堂\n李雪峰\n下陰\nkjking\n近親相奸\n如果你未满32周岁请自觉离开。\n复制\n赣您娘\n驶你母\n貝肉\n國產AV\n操操你妈\n365情色\n台湾身份证\n毛新义安\n肉棒在小穴里好爽\n濫比\n和两勇男玩25P\n白癡\n吳邦國\n成人黄色电影\n毛台湾党\n我考\n於天瑞\nzlywy\n漏B\n插入內射\n國家領導人\nNND\n肉棒刺入我的小穴\n免费淫奸女Ａ片段试看 令人喷血的春宫108姿势\n册那娘饿比\nPoon Poon\n法~~轮\n流星学院\n毛妈卖妈屁\n值勤\n打砲\n无关键词(导航搜索)\n國軍\n鸡巴 嫩穴 淫荡\nSUCK\n依星\n淫蕩 大雞巴 嫩穴\nsimple\nfalundafa\n亚洲激情妹妹\n大肉茎插进小肉洞\n毛亲民党\n國 賊\n教你怎样延迟射精33岁以下禁免费淫电影a片\ncabal\n小姐打飛機\n黄色免费电影\n政治犯\n温家堡\n法$$纶\nwgpj\n歡迎G12情色電影院\n倒塌\nNPC\n手记注册日韩5015部激情电影\n唐人電視臺\n淫民俱乐部和meinv发春激情3级片试看\n滥货\n廖锡龙\n多人轮\ndse淫乱小姨小说\n13点\n海盗的悠闲生活\n公子冲\n偷情 乱伦\n狗干\n野外操B更刺激\n法^淪\nｃａｏ ｂ\n狗幹\n贱比\n老鸨\n凸肉優\nT。M。D\n狂插meinv穴\n吞精騷妹\n发$仑\n新闻出版署\n白痴\n插 干jiejie 好爽\n妈的b\n保监会\n美乳鬥豔\n滚那吗B\n多人輪\nfuck王兆国\n笨比\n卖比\n操淫荡少妇 操淫荡妹妹 操淫荡姐姐 操淫荡护士 操淫荡 空姐操 老师操女老师 学生操老师 操老师小说\n妹妹美图网\n斐得勒\n娇妻偷欢超爽日本电影\nRed Hot Fetish Collection Vol。30 森田めぐみ\n久遊\n和两勇男玩20P\n杨月清\n成人午夜\n高文谦\nRI NI MA\n激情爽电影，夜夜最消魂！\n色网联盟\n吳儀\nzhengjian皮条\n超淫荡丝袜妹妹\n毛二逼\nYOUXIKA\n妈的B\n佳静安定片\n14se淫色图片 免费vagaa电影 免费电影\nNMD\n毛恩格斯\n寂寞自摸\n生命奇跡\n欧美激情淫色贴图区\n新任教师の淫行物语\n６１０辦公室\nRed Hot Fetish 纹舞らん--スケパン刑事\n基地組織\n学生妹meinv在野外偷情\n援助交易\n23dy淫色贴图\n吳邦囯\n自拍\n法~~輪\nBlowJobs\n学生暴动\n淫荡少妇露逼自拍\n梦幻西游\n郭 羅 基\n齐墨\n吾爾開希\ncao ni ma\n幻靈遊俠\nfuck投毒杀人\n何候華\n雞歪\n手记注册日韩5000部激情电影\n激情玩穴网\n妹妹的嫩阴唇\nbutthole\n毛妳老母的\n管理员\n金酷\nmeimei的屄\n马场大亨\n肉棒插入穴中\n学联\n淫荡妈妈之办公室助理\n发$伦\n计生风暴\n外_掛\n毛买财富\n肉蒲团\n发*仑\n做爱爽图\n法$$綸\nｔｉａｎ ｚｈｉ ｙｏｕ ｘｉａ\nｔｏｍ在线\nｔ2ｄｋ\ntaobao靠\n插你妈\n母子乱伦\n东条英机\n杨振\n周百刚\n毛主席复活\n亞洲互動\n站前留學 外國人英語會話教師的癡態紀錄 (3月作品)\n浓浓的精液喷入了子宫\nmir\n極品奶妹\n肉蒲團\n发$$论\n吞食天地\n中华养生益智功\n用手指玩弄着阴户\n法@@淪\nBiao子\n堕淫\n代練\nｆ_ｕ_ｃ_ｋ\n亲吻小屄\nHimiko 010 Riko Kurusu 来栖! 中出\n日本第一av女优武藤兰被10男糟蹋全记录1小时\n操濫妳那騷穴\n制服诱惑\n精液榨取\n毛澤東\nALERT\nｃｈａ你\n外_挂\n2。23会议\nboxun\n產黨\n网爱\n陈破空\n管理員\nPink Puncher~生中出SEX Ais Cumshot Inside Sex !! : 姬野爱\n色网阴道特写强奸31岁处女全过程\n操淫荡护士 操淫荡 空姐操\n壮汉给纯情处女开苞\nfa轮\n政府無能\n去你的\n黄色影视\n熟妇骚器\n斷電\n天安門事件\n毛吴官正\n毛食捻屎\n红楼绮梦\nasktao\n毛发正念\n香港顶极淫片\n臺灣共和國\n色區\n色区\n网特\n外\\掛\n小澤瑪利亞~~誘惑色情\n盐酸二氢埃托啡\n免费淫电影网址\n劍俠世界\nbt成人\n帝国主义\n大龟头碰到阴户底部最敏感的地方\n66xp。com 电影免费\n强*十八岁美女护士 看完保证你打飞机\n三唑仑\n成人图片\nzhengjian皮條\n李昌\n大肉棒干我的小穴和屁眼\n钓鱼岛\n吳 宏 達\n前凸後翹\n306醫院\n气性壞疽病\n戴相龙\n发*伦\n那嗎B\n那娘错比\n21世纪基金会\nｌｏｎｇ ｈｕｎ\n毛干七八\n插穴\nrxjhhvip\n強姦物語 逢乃\n黑鮑娇\n老虎机\nfuck\n蒋介石\n乱伦熟睡的jiejie\n就去色色\n爆炸\n毛狗日\n六四死難者\nmeinv\n肉棒顶到花心\nTora-Tora Platinum Vol。11 持田茜\n赵南\n毛社科院\n亚洲淫色图\n来爽我\n肉棒插进阴户内\n沈彤\n大鸡巴操得我好爽啊\n干阿姨的骚穴\n韓啟德\n推推俠\n在线清晰日本超爽mm毛\n好紧的小穴\nmeinv视频短片\n苏晓康\n滕文生\n操射\n淫荡女mm的嫩穴 老师嫩穴\nsm调教\n郭 岩 華\n美乳看護學生\n毛胡耀邦\nnetdream\n幹死你\n何 德 普\n陰部\n柴玲\n毛死gd\n免费淫奸女Ａ片段\n插烂妹妹小屄\n外陰\n銀行帳号\n操草你妈\n两性午夜激情电影\nfuck售软件\n跳大神\n毛死gm\n毛生孩子没屁眼\n汤光中\n日你妈啦屄\n推推侠\n小穴好紧\n女做爱光盘亚洲\n色魔\n天鹅绒行动\n发*沦\n乱伦熟睡的姐姐\n淫奸妹妹论坛\n香港GHB水\n高潮白漿\n淫色成人网址导航\nｊｉｎｇ ｔｉａｎ ｄｏｎｇ ｄｉ\n干老师的穴好爽\n色鬼\n和少妇做爱好爽\n法^倫\n震级\n万岁\n澳洲光明网\n淫洞 贴图\n一夜情交友\n咪咪圖片\nstreet of EROS 07\nstreet of EROS 08\n发~抡\n国产幼女屄屄套图\n淫女床上的自慰\n圓滿\n毛江泽民\n姐弟激情淫色\n狗城\n非常三国\nRed Hot Jam Vol。6 Chiaの露出初体験\n黄色图片\n20岁女大学生被强#(组图)全过程免费淫 电影\n好色妹妹电影做爱论坛\nbukake\n平反六四\n非常三國\n姐姐的阴唇好大啊\n最淫成人贴图区\nｄａ ｑｉｎｇ ｄｉ ｇｕｏ\nfuck吴　仪\n小鸡鸡\n成人网站\nfuck社会主义\n肉棒 ……好舒服喔\n圓满\n邪恶的党\n阴部翻过来看超拽\n千年\n张钢\nmeinv插穴\n掰穴皮卡丘\n免注册台湾18dy\n弟弟的大肉棒\n三国策\nWANZ)强制猥亵 非合法ドラッグ\n妹妹的阴道真紧\n毛妈批\n东突暴动和独立\n精液射进小姨的子宫\n舔舐着阴道内壁\n少女免费激情电影\n毛皮条\n鹿城激情淫色贴图\n強制浣腸\n操打炮\n毛身份生成器\n毛睾丸\n法^侖\n红卫兵\n操抽插\n美仙子 美乳+美腳 天中梨子\no2jam\n免费淫奸女Ａ片段试看 20岁女大学生被强#(组图)全过程\n生殖器\n小穴任由大鸡吧来回抽插\n美脚残酷物语膣壁浸透汁\n毛强效失意药\n處女小穴 阴毛\n淫奸处女\nyutong\n亂武天下\n乱伦\n幹砲\n西游伏魔录\n和妈妈玩插穴\n萨斯\n大胆出位\nmeretriz\n毛肉洞\n大衛教\n黃色網站\n发$沦\n秘密調教凌辱\nfuck西藏独立\n三句硬话\n澳洲光明網\n快活林黄站\nfuck煞逼\n免费黄色网站电影\n幹你良\nhotsex\n買月卡\nLineageⅡ\n熱血江湖\n去妳妈\n日韩meimei嫩穴淫图\n乳交\n在线激情电影 - 会员注册\nfuck台湾共产党\n美體豔姿\n毛精子\n蠢猪\nmm美图\n女友坊\n肉棒快速抽插\n林心如做爱*色情电影*免费淫电影a片\n林肯\n淫乱军团\n法^伦\n毛小乳头\n中國\n免费淫 电影\n好色cc\n台湾色色导航\n毛澤东\n抚弄后插入小穴\n淫奸电影\n推翻\nvaameline\n日本美女图库\n亚洲成人激情影院\n超级樂者\n毛阳具\n抽插50岁处女meimei淫叫不断\n连邦\n露穴\n激情文学区 暴乳母娘 国外淫色贴图网址\nDICK\n卖。国\n淫乱秘史\n台湾裸体贴图成人免费电影\n男人最爱的淫图\n裸体少妇\nmm美圖\nｄｉ ｇｕｏ ｃｈｕａｎ ｑｉ\n乱交\n如果你未满22周岁请自觉离开。\n不用注册淫电影网站\n肉棒好粗\n天安門屠殺\n黄色免费电影meinv\n内射魅惑の美少女精液滋滋往外冒\n肉棒插入嘴\n絲路傳說\n天閹\n毛弓虽女干\n蒸汽幻想\n林业局\n新唐人電視臺\n毛孙逸仙\n李 旺 阳\n操屄\n毛丫头养的\n妹妹的屄好紧啊\nGold Angel Vol。8 Super Erotic Girl : Rio Nakamura 仲村りお\n操陈水扁\n我和小姨淫\n姚依林\n你的小穴好温暖…\n群阴会\n遊戲指導員\n毛国务院\n两个\n毛工力\n妹妞露穴\n毛枪决女犯\n在线淫洞影院\n操尼\nthsale\n最新娱乐黄站\n处女性开苞\n孤儿\nｐｋ1937\n毛片\n発射\n成人午夜激情电影\n发@沦\n装屄\n金发表姨子的鲜嫩蜜穴\n恋爱盒子\n台湾成人23贴图\n凯图科技\n九評共產黨\nRED HOT JAM 7 Eeos Note 姬崎水希\nａｉｏｎ\n江折民\n肉棒插入肥肥的淫道\n孤兒\n粗大的龟头\n黄色漫画\n王涵万\n台湾党\n插婷婷小穴\n奸淫少妇穴\n天安门惨案\n疯狂抽插嫩穴\n日本淫色贴图\n肉棒插进蜜穴\n天阉\nmeinv激情性交射精电影\n2逼靠\n那吗B\n李 旺 陽\n香港成人色网址大全\n爛比\n法^仑\n总理\n妳媽的\n圆滿\nunixbox\n肉棒的抽插淫荡少妇穴\n军转\n欧美成人激情网站\n毛日你娘\n抽插25岁处女妹妹淫叫不断\n毛新党\n被老师强奸的小初中生\n手记注册日韩5018部激情电影\n黄色免费电影淫电影在快要射精时迅速地将\n慰安婦\n高麗棒子\n姐的嫩阴道\n顶到花蕊\nTtmd\nGate To Heavens\n压/无码\n棒阴道好痒痒\n弟弟的肉棒好粗\n用力刺激小穴\n干小姐小穴\ntaobao\n我的小屄儿好痒\n阴道图片\n私**服\nｓａｒｓ\n张震\n性交課\n免费淫虫在线看电影\n麻醉钢枪\n强奸29岁处女全过程/淫妹阴毛小穴\n性爱擂台\n激情 少女 成人论坛 乱伦\n星際online\n柯慶施\n法^^沦\n真理教\n美女淫色贴图\ngc365\n21ｃｎ\n高潮白浆\n人民報\n成人午夜激情聊天室\n毛日你妈\n幹穴\n精子射到妈妈的穴里\n毛狗操\n中宣部\n毛佛展千手法\n王希哲\n妈妈你的穴好紧\n摩门教\n龙虎\n操淫荡妹妹穴\n妈个比\nGM美国\n血洒\n美利坚\n淫meimei电影论坛\nｗａｉ ｇｕａ\n血洗\n中天万方\n┅┅妈舒服死了\nGM美國\n毛金日成\n龟头抵制花心顶住子宫口\n李 少 民\n亚洲贴图区月宫\n狂插 嫩穴\n搜狐\nmeinv主持24小时全天激情热舞\n勾魂少妇\ncaoB\n勾魂少婦\nKISS\n免费在线淫电影欣赏\nRed Hot Idol Vol 1\n毛六四\n蒙古达子\n干炮插穴\n冊那\n解放军306医院\n共产党靠\n肉棒插入浪穴\nto173\n外@@掛\n性病\n女儿的阴道好嫩啊\n免费淫电影a片。母子淫*乱轮现场看扶正肉棒\n香港免费淫电影\n上妳\n毛苹果日报\n范田紗~120％!H喜不自禁住在一起日記\n阴茎把淫穴塞满\n教你怎样延迟射精18岁以下禁免费淫电影a片\nPriston Tale\n先审后贴\n卓奥\nsoma\nK他命\n李文斌\n钦 本 立\n卓奧\n奥运\n新诗年鉴\nglobalrescue\n免费淫色电影下载\n压制\n淫荡家族群体性交\nmeinv激情视频聊天室\n干全家\n互联经纬\n脫衣舞\n吴 百 益\n藏妇会\n分家在\n毛美国之音\n933cn\n五星红旗\n插你老母\n淫奸护士穴\n鸡吧刺入她肛门\n娱乐黄站\n骚浪淫水的小屄儿\n麥角酸二乙基酰胺\nｑｉ ｊｉ ｓｈｉ ｊｉｅ\n色色连\n龍與地下城\n社。會。正。義。力。量\n弓雖\n慰安妇\n嗯…… 啊… 不要停…… 啊…… 插的再深一点…\nyxzbw\n奧運\n干穴\n指点江山论坛\n幼男做爱妹妹做爱\n淫奸阿姨穴\n八九风波\n白小姐\n毛董建华\n毛外\\挂\n错那吗比\npp241淫色导航\n毛外汇局\n假阳具插穴\n艳妇淫女\n本站10002多部激情电影全部免费试看三天\n哥哥淫奸meimei穴\n交换夫妻\n兴奋剂\n风月大陆\n东正教\n扎卡维是英雄\n成人电影\n無碼炮圖\n法功\n淫水嫩穴\n國 家 安 全\n靈遊記\n毛迷歼药\n弓虽女干\n亚洲成人激情视频\n94bb成人小电影\n家庭乱伦小说\nｍｆｏｒｅｓｔ\n圣母\n马巧珍\n毛外_挂\n情报\n创网\n册那\n可爱双胞胎美幼少女的性趣［亚无］［402mb］\n毛铁道部\ngame588\n大鸡巴狠狠插进了她那又肥又嫩的小骚屄儿\n高治联\n二穴中出\n无码体验\n汶川\n实现你的幻想\n塞你公\n外@@挂\n日本18淫电影网\n咪咪图片\n情色谷\n奸淫护士穴\n卡通美女淫色贴图\n剑网\n发^抡\n口交\n自由民主论坛\n扒开她两扇阴唇\n免注册在线淫电影\n反党\n毛死GD\n大血比\nkqking\n泄密\n骚鸡\n淫荡痴女贴图\n幹你老母\n一邊一國\n成人色情网站初中女生偷拍\n毛死GM\ndick呂秀蓮\n発妻\n外教上课迟到就挨操\nfuck射精\n幹你老比\nmm乱淫自拍图片\n激情淫色mp3\n儿子的肉棒顶到妈妈的花心\n毛狂操\n水浒\n爱女人\n新华社\n71776\n法@倫\n垃圾游戏\n……浪穴……大肉棍\n巴赫\n分裂\n毛李登辉\npp点点激情\n淫色搔女网\n鸡巴对准淫穴\n毛李红志\n淫女穴\n美乳斗艳\n毛天鹅之旅\n凌辱\n勁爆\n狗卵\nnana无修正AV女优ハメ撮り\n弟大物勃\n北京当局\n天天干貼圖\n戴 相 龍\n88jb\nroi world\n共和国卫士\nfuck审计署\n汇款\n反共\n领取\n游戏橘子\n性交课\n煙膏\n大鸡巴干meimei\n六合采\nanal\n分裂国家\n姐弟淫乱还玩3P\n毛系统公告\n法*功\n免费妹妹黄色淫乱电影\n淫賤少女\n光之國度\n火麻\n戴 相 龙\n騷妹妹\n金鱗豈是池中物\n男女激情淫乱图片穴\n肉把鸡巴包得紧紧\n東北xx網\ncloseupsof。us\n洪传\n受虐狂\n毛警匪一家\n毛龟公\n操操蛋\n毛睾\n毛五星红旗\n拉姆斯菲爾德\nfuck西藏天葬\n狗狼養的\n買春\nfuck王岐山\n免费淫色情电影 教你怎样延迟射精18岁以下禁\n阴道图打手枪专用\n媽媽的\n又肥又紧的阴道\n高清性愛\n淫奸处女电影\n克林顿\n办理证件\n毛发抡功\n毛傻比\n成人图片导航\n激爽电影VIP会员注册区\n刀剑•英雄\n色情工厂\nｊｉ女\n不要注册激情免费电影\n系统\n午夜成人场\n陳水扁\n你媽的\n毛煞逼\n納粹\n毛游戏管理员\n毛性虐待\n康生\n超級樂者\n雞8\n亚洲成人午夜场\nfuck王刚\n注册通道看情色电影\n色网阴道特写强奸26岁处女全过程\n布雷尔\n受傷\ngamemaster\n人妻交换\n抽插29岁处女妹妹淫叫不断\n乱伦都市\nfuck系统讯息\nolgad\n淫妻浪女\n男人若妻夢交\n秦晋\nTokyo Hot n0156~160合集收藏版\n操淫荡少妇\n戈万钧\nwg886\n捅逼\n走光\nSuck\n父女激情淫色\n操你八輩祖宗\n毛何候华\n保卫台湾\n肉棒插蜜穴\n抽插53岁处女meimei淫叫不断\n白虎嫩B\n达尔富尔\n毛阴唇\n法@侖\n政协\n激情自拍小电影\n趙 曉 微\n政協\n抽插55岁处女meimei淫叫不断\n高橋英4套片合共\n少妇自慰\n狗剩\n操淫荡少妇 操淫荡妹妹\n白虎嫩b\n儿子淫奸姐姐\n八九民运\n毛升达毕业证\n被幹\n王炳章\n助理\n卵子\n本拉登\n翘臀嫩穴\n被干\n毛肉棒\n哥哥干的人家好舒服啊\n沧澜曲\n月宫套图社\n法lun功\n教你怎样延迟射精36岁以下禁免费淫电影a片\n操mm穴交\n陰門\n催情药\n中华\n激情淫影院\n日本灌肠\nsuck你媽的\n在线淫电影，免费淫色情电影\n激情四川方言版\n高　潮　喷精\n姦淫電車\n姬胜德\n蔣經國\n我你老板\n韓正\n手记注册日韩5012部激情电影\n李 月 月 鳥\n人妻交換\n前网\ntransexual\n低價金幣\n台湾成人24贴图\nhardcore\n女儿淫奸妈妈穴\n日本顶级淫片\n摸擦小肥穴\n卡卡金币\n14色se成人站\n性学教授\n小阴唇被鸡巴猛的塞进去\n法@伦\n龙腾工作室\n妳老母的\n李紅志\n現役看護婦\n毛肉棍\n大鸡巴插入肉穴\n8。9\n軍民衝突\n中功\n精品性交图\n瑞士金融大学\n邻居美少妇 操淫荡少妇\n成人色情导航\n完全免费在线淫电影 看完保证你打飞机\n色狼俱乐部\n滾那嗎老比\n孫中山\n陰阜\n淫电影//居然花钱请人教性爱姿势(组图)\n可卡因\n色情电影\n张林\n税务总局\nmeinv淫图\nｑｑｙｉｎｓｕ\nmystina\n淫图新社区\n鲍戈\n妈妈嫩穴…痒\n血比\npiao liu huan jing\n天之煉獄\n顏射\n午夜激情妹妹\n馬巧珍\n姑妈的阴道好紧啊\n处女开包\n现役看护妇\n亞洲性虐\n法@仑\n日你全家\n她媽的\n發~~掄\n去她妈\n姐姐的阴道好紧啊\n分裂國家\n毛淫荡\n365 sex\n毛联合国\n…你的穴太紧了\nｔｉｅ ｊｉａ ｚｈｅｎｇ ｘｉｏｎｇ\n招鸡\n久遊網\nfuck倭寇\n本站10022多部激情电影全部免费试看\n大 史 记\n若叶~Princess Collection(1163)\n极品波霸\n丁香社区\n发仑\n张志清\n毛钱其琛\n特种部队\n狠狠地肏\n丁香社區\n李 月 月 鸟\n┅我要泄了┅┅喔\n无毛穴\ncamon\n王 冶 坪\n操我\n金鳞岂是池中物\n发伦\nｈａｉ ｄａｏ ｓｈｉ ｄａｉ\nｌｏｎｇ ｙｕ ｄｉ ｘｉａ ｃｈｅｎｇ\n干七八\n毛高丽棒子\n天骄\nezgaming\n日本灌腸\nX媽牛比\n少妇的骚穴淫液\n你的大鸡巴好粗\n成人激情论坛导航\n李洪智\n狗养\n赤匪\nｘｉｎ ｃｈａｎｇ ｚｈｉ ｙｅ ｗａｎｇ\n处女淫荡色情网址大全\n混 沌决靠\n陳雲\n美女小穴贴图偷拍少女乳房\n江独裁\nｌｉｈｏｎｇｚｈｉ\n小乳頭\n大话战国\n性色社区\n淫荡俱乐部\n街头对抗\n母子乱伦之淫穴\n兆鸿\n敬國神社\n杜鲁门\n毛回民暴动\n扎卡维\n国家总理\n那吗老比\n賽妳阿母\n天龍八部\n法腀功\n台獨分子\n鲍彤\n六四平暴\n野合\n酥胸誘惑\n大法弟子\n公审江泽民\n可卡叶\n韓啓德\n毛江主席\n光之国度\n在线免费激情裸聊\n搞B\n拿破仑\n法西斯\n赤化\n政变\nAV足球賽11男VS11女大對決\n毛环保总局\n洞洞淫水清晰大图\n针对台湾\n独立台湾会\n儿子顶到姐姐的花心\n傭兵傳說\n懆您娘\n四海龙女\n发^^仑\n帳號\n领土\n穴图\nｘｉｎｇ ｋｏｎｇ ｈｕａｎ ｘｉａｎｇ\nwebgame。com。cn\n色链\n杨尚昆\n阴唇 黏液流下\n粗大的鸡巴插入穴洞\n強姦處女\n法*倫\n現役看護婦下载\n圣火\n办理文凭\n公交车上操女大学生\n强暴\n淫荡女护士\n穴圖\n少妇房中自淫，和两勇男玩1P，操得直叫爽 操日本淫女穴\n亚洲午夜成人影院中心\n快克\n三K党\n克林頓\n护士脱衣图台湾激情电影\n免费淫电影 看完保证你打飞机\nfuck网管\n少妇嫩穴扒开嫩穴扒开嫩穴\npcik\n薩達姆\n色情小说\n手记注册日韩5023部激情电影\n成人电影免费成人电影\n法国\n发^^伦\n激情成人淫片\n妳她妈的\n人大代表大會\n成人论坛\n張震\n逼你老母\n李 文 斌\n操日本淫女穴\n精蟲\n中共\n毛三陪女\n啊……快插啊……小屄美死了\n陳奎元\n大波騷婦\n小骚洞 蜜穴\n本。拉登\n成人导航\n黑星女侠\n纪登奎\n毛管理人员\n牛逼靠\n刘亦菲淫阴道流血色片\n懆您妈\n发**轮\n黄色免费淫乱电影强奸片\n毛共匪\n扒开嫩穴 抚摩她的嫩穴 下身也不停的抽插着\nrxjhvip\nハイパー×潮吹きハイパ 麻美ゆま\nU/R靠\n媽B\nhabbo\njapansweet\ndekaron\n淫色小说网短片色情\n阿扁萬歲\n香港激情淫色区\n鸡巴插进娇嫩的花蕊\n法*侖\n阿姨的阴道好大啊\n兆鴻\nu r靠\n妈逼\n情色照片\n紧穴\n神州天戈\n瞳-饮精地狱!监禁无限中出\n哥哥使劲插我\n野鸡\n贺国强\n強暴\n毛看棋软件\n免注册淫电影爱的色放\n血腥图片\n民航局\n処女強奪3穴串挿輪姦\n毛日你\n53xp。com 电影免费\n法$倫\n操那吗比\n鹽酸二氢埃托啡\n射进小穴\n粗硬大肉棍… 抽动…好舒服\n本站10013多部激情电影全部免费试看三天\n视频聊天导航\n騷婦掰B\n模拟机\n乳此丝袜\n掰穴打洞\n死全家\n法^^抡\n偷情宝鉴\n安南\n58jb\n傻×\n吴方城\n宋楚瑜\n森水Cocoa\n立即登陆观看激情爽片\n上海孤儿院\n强奸\n肉棒噗嗤一声插入阴道\n工自联\n日本成人贴图区论坛\n午夜剧场成人视频聊天\n淫荡美眉就喜欢玩男人\n做愛寫真集\n阴道\n直操她嘴射精\n强奸44岁处女全过程/淫妹阴毛小穴\nｘｉｎｇ ｚｕａｎ ｗｕ ｙｕ\nproject\n淫水四益\n阴茎在子宫口游荡\n淫水絲襪\n幹炮\n人民币\n公车淫奸jiejie\n装逼靠\n小穴被大鸡吧胀的好痒\n卡弗蒂\n色狼论坛\n免费淫色贴图\n烂逼\n中南海\n昝愛宗\n骚穴怒放\n毛他妈的\n干进妹妹的小穴\n鸡巴用力深度插入\n淫奸jiejie论坛\n媽比\n姐弟激情淫秽\n计牌软件\n伊拉克\n成人影視\n下载黄色淫乱电影\n都市乱伦\n日本情色淫电影\n垮台\nVIP特区 激情在线\nfgmtv\n肉棒深深插入纖弱的肉洞裡\n青天白日\nfeng wu tian jiao\n鹿城娛樂\n傻b\n穴居人淫色贴图\n共黨\nB博士\n新魔界\n封神传说\n東方資通\n免费在线淫电影网站\n宾馆女郎\n性感肉丝\n中共中央中华人民共和国\n毛多维新闻\n打倒中國共產黨\ncaobi\n18dy影院\n舔哥哥鸡巴\n情妇\n杨白冰\n成人影视\n清纯派美女可爱的让人想狂干她！【RMVB/195MB】\nｈｅｒｏ108\n淫语痴女\n大 史 記\n日你妈靠\n色天堂18禁\n毛淫货\n眾志成城\n肉棒狂插好爽　插我\n華夏II Online\n干爆\n华建敏\n毛淫贱\n你妈靠\n少女被强奸到高潮时偷拍小A片\n毛肉棍子\n街頭籃球\n毛人代会\n他吗的\n韓桂芝\n人民幣\n自由亚洲\n湿润的花蕊\njiejie的小穴被大鸡巴插得好舒服哟\n毛发轮\n靠母\n發~~淪\n法鯩功\n厕所偷拍\n強姦\n欧美淫乱美女论坛 清晰的窥阴图\n六四参加者回忆录\n大粗肉棒插入阴户\n本站10014多部激情电影全部免费试看\n四清\nFUCK\nqeeloo\n江氏\n达赖\n台湾分裂\n肏我的小屄\n卫生棉条真人使用图\n李弘旨\n色情淫图\n做愛自拍\nmm在线电影a片3级片\n极欲燃烧\n人都在上的四大当\n东热空姐\n儿届中央政治局委员\n操穴喷水\nfuck十八摸\n金币网\n杀人犯\n校花沉沦记\n少妇野外淫穴\n免费淫色妹妹电影\n干炮\n叼你\n插在小穴里的肉棒\n吴百益\n功夫online\nteen\n真三國無雙OL\n免费淫奸女Ａ片段试看 教你如何鉴别处男处女的方法(组图)18岁禁看\n肉棒在阴道内喷射\n梦中的女孩\n怎样延迟射精25岁以下禁\ntesticle\n非洲\n肉棒深入花心\n天皇陛下\nbignews\n色妹妹大型黄站\n哲 民\n淫奸少女贴图淫奸女护士贴图淫暴亚洲\n破鞋\n賣淫\n地震局\n暴行白書女\n好死不如赖活着\n自由亞洲\nmm电影小片段试看免费淫影片\n体育总局\n达賴\nfuck威而钢\n天安门屠城\n免费激情电影 爽电影\n激情书屋男女做爱片段\n幼齿\n迷藥\n灼热的龟头紧顶住柔嫩的菊花口\n朱蒙\n午夜激爽电影网【成人电影色区】\n歐美無套\n毛外-挂\n鸡巴狠狠插入娇嫩夹紧的阴道中\n馬萬祺\n淫色电影论坛\nsafeweb\nSpecialForce\n曰GM\n紅頂商人\n肉棒深插\n狗乘\n丝袜淫妇\n咪咪情色\n私服\n傻B\nｈｕｎ ｌｕａｎ ｍａｏ ｘｉａｎ\n淫乱金发meinv\n亂世三國\n阴部\n插嫩穴 淫水流\n玩弄她的骚屄嫩肉\n幹bi\n你姥姥的\n爽爽爽台湾成人贴图\n曰Gm\n毛外/挂\n《金瓶梅》的巨著\n插我 用力插 好爽\nwebgame。com。cn靠\n无码内射\n淫荡痴女\n大鸡吧寸寸深入直顶她小屄的花心\n女同志淫乱贴图\n性爱频道\n激情淫色动画片\n操你全家\n掌上书城\n毛妓女\n毛水去车仑\n操操\n英雄纪念碑\n双峰微颤\n妹妹穴\n教你怎样延迟射精19岁以下禁免费淫电影a片\n毛国际法院\n毛黄　菊\n大鸡巴顶进meimei的花心\nnigger nigger\n磕药\n情報\nwg17173\n买卖枪支\n強衛\nfuck五星红旗\n哥哥的肉棒好长啊\n专制\n火線任務\n幸运抽奖\n洗脑班\n教你怎样延迟射精24岁以下禁免费淫电影a片\n毛胡紧套\n塞你娘\n灭绝罪\n尖阁列岛\n龙 虎 门\n舔的下体湿润阴毛\n丽春苑\n陈晓宁\n乳\n骚屄娘们儿\n发~沦\n激情热舞\nprostituta\n禁断少女\n老少乱伦\nｈａｎｇ ｈａｉ ｓｈｉ ｊｉ\n激情小电影\n幹妳媽\n手记注册日韩5001部激情电影\n郑万通\n成人小說\n强奸30岁处女全过程/淫妹阴毛小穴\n狂操你全家\n按摩棒\n台湾成人21贴图\n孫家正\n郑源\n美女口含鸡吧玩内射\n赛你娘\n小电影免费看高潮屋贴图做爱视频表演\n日本美女鲍鱼贴图区\nav贴图\n民主潮\n(人妻。com)人妻の情事2 夫以外の男に中出しされた妻たち\n装备展示\n婷婷淫色贴图\n做爱视频下载\n毛切七\n鹿城色妹妹大型黄站\n106bb成人小电影\n大型免费成人黄色图片区\n毛牛比\n操博讯\n免费免注册淫电影\n姨妈和我乱伦理小说\n猛插小嫩穴\n台湾成人视频聊天\nmeinv激情SM处女淫水\n雪山狮子\n民猪\n豖者\n錢其琛\n毛官商勾结\n台灣倫亂網\n天之游侠\n美女穴\n煞筆\n黄色靠\nfreenet\n毛贾庆林\n开房\n操催情药\n怀念十五年前的大学生\n冰锋\nmankind\n子宮\n本站10017多部激情电影全部免费试看\n子宫\n次郎\n吕秀莲\n玩3p色链\n希拉克\n特別公告\n煞笔\nyouxika\n吸精痴女\n你把妈的穴穴插满了\n一夜情\n肉缝　小肉穴\n仆\nｓｅａｌｏｎｌｉｎｅ\n巡查员\n回民\n毛王八蛋\nmierda\n大麻\nxin chang zhi ye wang\n激情解码两性电影\n亞洲風暴情色\n交通部\n毛狗屁\n赛妳阿母\n狂插meimei穴\n毛毛XX\n对日强硬\n五月四日\n遗精\n毛狗屎\n毛温家保\n洪兴\n弓雖女幹\n轮流执政\n發掄\n流亡\n操大*FA弟*子\n最大的性虐待及另類性生活網站\n粉嫩淫臀\n韩国三级电影\n无码做爱\n反华\n欧美激情淫色贴图\n淫东方\n淫語痴女\nnude nude\n新唐人电视台\n毛奶子\n毛军委\n毛新光明\n叛党\n法^^掄\n毛王岐山\n民进党\n廖晖\n香港激情论坛\n黃易群俠傳\nqi xia\n李沛瑶\n王洪文\nling tu\n龟头顶在花心深处\n丁字褲翹臀\n廖暉\n又骚又大的屄\n姣西\n巡查員\n激情淫穴小说\n信长之野望\n六四死难者\n姐姐的阴户好嫩啊\n免费成人电影\n驶你老师\n美女下体淫水直流\n口交靠\n小姨令我射了好几次\n极品奶妹\n淫戰群P\n毛鸡芭\n毛严方军\n林心如做爱网\n兩腿之間\n那嗎老比\n干爽\n坐庄\n三国群英传OL\n烂货\n免费淫奸女Ａ片段试看 强*十八岁meinv护士\n和姐姐肏屄\n成人小说\n幹妳娘\n天天情色\n磕藥\n干牠\n反動\n色书库\n六。四事件\n13點\n反动\nepochtime\n厕奴\n女醫師過度輪姦陰道麻痺+大量中出強制飲尿小便斗\n鸡巴插入肉洞\n教你怎样延迟射精21岁以下禁免费淫电影a片\n佣兵传说\nfoutre\nｍａｏ ｘｉａｎ ｄａｏ\n親 日\n發**綸\n弹 劾\nsilkroad\n厕所盗摄护士姐姐性感美腿图 肉丝美腿艳情淫欲\n十年動亂\n漢 奸\n萬歲\n毛军妓\n百家性黄站\n家乐福\n田紀雲\n又猛插几下\n儿子淫奸jiejie穴\n王永生将军\n本站10016多部激情电影全部免费试看三天\njiejie的阴户\nｓｉ ｌｕ ｃｈｕａｎ ｓｈｕｏ\n美女激情SM处女淫水\n大揭露\n于大海\n操大便\n粗大的肉棒\n盛大\n弟大勿勃\n李兰菊\n阴道口湿淋淋的\n家庭乱伦\n少妇操尿穴\n東突\n本站10015多部激情电影全部免费试看三天\n大富翁\n捐款名单\n毛胡乔木\n口圭\n将肉棒插在阴道里面\n討伐中宣部\n淫meimei成人娱乐\nfuck傻吊\n米青\nghost\n大 紀元\n藏青会\n反人类\n吾爾開西\n又肥又嫩的阴户\n政治協商會議\n日本激情淫影片下载\n劍俠情緣\n遊戲蝸牛\n它妈地\n又骚又淫的屄\nKamikaze Girls Vol。 27高清晰版本\nArchlord\n爆操\n皮条客\n免费淫奸女护士图片\n淘宝靠\n儿子淫奸姐姐穴\n肉棒狂插流出淫液\n丁元\n淫民激情文学图库\n龟头直抵子宫口\n远程偷拍\nbt淫色电影免费下载\nmeimei淫图\n国产无码\n操林\n毛斧头镰刀\n淫阴贴图\nfuck you\n法~倫\n古龙群侠传\n共产党\n星鑽物語\n淫水狂流\n白立忱\n抽插33岁处女妹妹淫叫不断\n毛王太华\n毛龟头\nａｉｄｓ\n一中一台\n带练\nfuck天安门一代\n一党\n操meinv穴\n张万年\n神淚\n激情成人午夜\n99bb成人小电影\n毛他祖宗\n那娘瘟比\n粉穴嫩逼\n粉嫩粉嫩的小穴流水\n精液喷射小穴\n勁舞團\n死刑枪毙\n至淫绝地\n农业部\nfuck fuck\n扶不起的阿斗\n插后庭\n淫荡美女医生\nmeimei的穴好多水\n和阿姨玩插穴游戏\n看棋軟體\n石首\n公審李鵬\n洛奇\n密传\n真主\n手指用力扣进小穴\n阴茎被肉壁紧紧的夹着\n毛驶你老母\n淫液流入穴\n姊姊的小骚屄\n无疆界\n雨星网\n东亚\napex\n性感美女淫穴\n阴唇 口含鸡巴\n艾森豪威尔\n淫液顺着肉洞流下\n日本激情淫穴片\n自插小穴\nurban\n自慰摳穴\n一根材激情五月天\n毛狗娘\n极品激情电影\n干 …插我的穴\n美女鸡吧图\n韩日成人激情影院\n硕大的肉棒\n夢~ Pure Idol Vol。 20 : 望月なな\n色情电影免费看\n毛外—挂\n军国主义\n淫乱姐姐小说\nｈａｃｋｉｎｇ\n在线淫电影 免费淫奸女Ａ片段试看\n操护士的穴\n自拍美穴\n成人黄色网站\n不玩了靠\n上海帮\n啊 ……用你的鸡吧插我\n法~侖\n叛國\n学生爱国者运动\n婁義\n日本淫色动画片\n粗……肉洞\n挺进小穴\n强奸25岁处女全过程/*妹阴毛小穴\n倚天二\n叛国\n大鸡巴 用力地干\nwg666\n莲花逼\n毛监察部\n操大东亚共荣\n将肉棒插进她身体的最深处\n夫妻俱乐部\n欧美淫乱meinv论坛 清晰的窥阴图\n戳B\n狼族淫色群交蜜穴\nfuck圣女峰\n台湾建国运动组织\n黑鬼\nｗｕｌｉｎ2\n抽插49岁处女meimei淫叫不断\n一股滚热的精液从插得紫红的龟头马眼里激射而出\n淫电影 强*十八岁meinv护士看完保证你打飞机\nfuck文物局\n国家机密\n婷婷激情俱乐部\n保密局\n1989 事件\n英雄王座\n肉棒狂插 浪穴 嫩穴\n毛发论\n肉棒顶开阴唇\n男女激情电影\n它爸爸\n情色藝術天空\nRed Hot Fetish Collection Vol。 34 : Saito真央\n激情淫色网站\n下作\n日韩淫女电影\n龟头在阴道里抽插\n下体\nmeinv发春激情在线电影\n行房\nｓｈａｏ ｌｉｎ ｃｈｕａｎ ｑｉ\n爆乳娘\n大东亚\n曰gM\n哥哥粗大的肉棒\n我漂亮的老师狗爬式做爱现场\n戳b\n傅作义\n发展研究中心\n系統管理員\n奶頭\n七五事件\n曰gm\n法愣\n干进她穴内的龟头\nLIHONGZHI\n陰部特寫\n插进少女粉红阴道\nFuck\n生春袋\n素人娘\n抽插31岁处女妹妹淫叫不断\nMAKINGLOVE\n羅富和\nㄙㄞ你老师\n色meimei乱伦理小说\nenculer\nスクールガール 直美\n母子淫奸乱轮现场看\n猛嫩穴把肉棒包得紧紧\n Maki\n儿子的大肉棒\n妹妹的嫩阴户\njian xia qing yuan\n毛蒙古独立\n靠爸\n淫师荡母\n教你怎样延迟射精\n露B\n发奖使者\nYGBH\n激情网址导航\n女馬\n嗎逼\n狂插淫洞 明星陰穴\nｋａｒｍａ\n淫护士电影\n啊呀娃娃\n毛联易\nりすぎ家庭教師 ほしのキララ\n震級\n露b\n52淫色导航\n足球經理線上\n学生与警察\n海盜王ONLINE\n大使馆\nKamikaze Girls Vol。30-中野美奈\n香港激情淫电影\n帶练\nTora-Tora Platinum Vol。11 S级圣女 持田茜\n熟妇人妻\n淫电影\n小穴……好爽 菊穴\n多维周刊\n古方迷香\n中华真实报道\n又大又硬的肉棒\n高校暴乱\n匯款\n白虎少妇\n色猫情色论坛\n刘宾雁\n你媽比\n侦探设备\n好长的肉棒\n乔冠华\n法~伦\n黄色三级片\n蔡崇国\njianghu\n温加宝\n学习班\nchuan shuo\n反革命暴亂\n插妈妈的小穴\nlei ting zhan dui\n濫B\n发^沦\n废物\n李瑞環\nmo huan sen lin\n唐家璇\n又鳥女幹\nwww。qq965。com\n运营长\n台湾成人22贴图\n阴阜\n刚发育好的处女就这么被干\n歇逼\n本站可能有不适合未成年人观看的内容\n噴精\n穴门美女图库\n蜜穴贴图\n淫奸mm电影\n护士诱惑\n郭罗基\n放尿\n放屁\n成人在线免费淫电影\n狂风暴雨\n不举\n法~仑\n星钻物语\n它妈\nｓｈａｏｌｉｎｃｎ\n鲜族\n阴门\n三夫妻结伴旅游换妻大乱交\n淫民激情文学图库欣赏\n反革命暴乱\n毛我操你\n毛欢城\n劍俠情緣II\nmo擦小肥穴\n值勤帳號\n淫虎成人导航\nエロい女中出し20連発姫野愛\n淫奸小姨电影\n本站绝无任何形式木马病毒\n骚姐乱伦\n無套自拍\n白虎少婦\ntest\n毛麻醉药\nwu jin de ren wu\n粗大的阴茎与紧窄的阴道壁之间的剧烈磨擦\njiejie的嫩阴唇\n賣比\n張榕明\n口合\n慰春情\n偷窥无罪\n幼男\n凌锋\nxyd\n黃色電影\noffgamers\n贞操\n迅雷下载淫乱电影\nxyj\n陰道\n盐酸二氫埃托啡\n台办\n姫野爱\nバコバコ风俗 W指名 遥めぐみ×美优千奈 A\n賤比\nxyq\n夫妻激情自拍图\n歡迎G10情色電影院\n插B\n洪吟\n看单亲妈妈给儿子的性启蒙\n坦克宝贝\n性交靠\nｍａ ｃｈａｎｇ ｄａ ｈｅｎｇ\n懂文华\n售软件\n國權\n雞芭\n毛公安\n放荡熟女\n肉穴肥肥的裹着阴茎\n激情裸体\n抠 插 磨蹭 穴\n性免费电影\n黄色做爱电影\n西藏喇嘛\n六四以後\n插b\n劍網\n王从吾\n麻醉药\n烫的大鸡巴在小穴里来回抽插\n免费视频裸聊\n丰满的视频聊天主持\n熊焱\n大鸡巴 插 少妇\n性爱\n处女膜\n吗个\nPure Idol Vol。20 望月なな ！【RMVB/543MB】\n和嫂嫂淫穴\n网络创世纪\neverstar\n狂插妹妹穴\n小騷貨\n砍翻一條街\n操插gm\n厕所盗摄护士姐姐\n李瑞环\n插你全家\n广电\npopkart\n第九城市\n鬼畜輪奸\nfuck傻卵\n插小穴妹妹直流水\n出售账号\n迷药\n婷婷激情网\n肉棒插入又大又深的穴\n瘟比\n我使劲快速抽插龟头好麻\n插入她的搔B爽死了\n肉摩擦她处女的肉穴深处\n纶功\n插妹妹图\n外  挂\n激情淫贴图网\n狂插护士穴\n吮吸肉棒\n毛一党专制\n午夜情人\nBIAOZI\nfei chang san guo\n我操靠\n陈昌智\n操操逼\n妓院\nhuan ling you xia\n阳具在她的蜜穴里抽插\n卖国求荣\n他马的\n熊炎\n香港激情淫色贴图区\n8仙靠\n女马\n亲 美\n毛喷你\n大众成人网站\n捅进她的阴道\n政府无能\n姐姐被轮奸\n舌頭穴\n新闻出版总署\n催眠悪用集団轮奸30连発\n骚嫩的蜜穴紧紧地裹着大鸡巴\n操插gM\nTTMD\nPink Puncher Premium Angel 光咲玲奈\n李洪志\n立花里子のレズビアン母乳M女コレクション\n八九風波\n西藏獨立\n日本无码成人电影\n阿片烟\n大陆鹿城黄站\n操护士\n杭州二元\n共铲党\n激情淫色论坛\n激情淫穴小少妇\n法錀功\n亚洲色meimei\nhellgate\nwww。gpchina。com。cn\n王沪宁\n狂操空姐\n毛高校骚乱\n世模\n法轮功\n仙劍\nｘｉａｏ ｙｕ ｅｒ ｙｕ ｈｕａ ｗｕ ｑｕｅ\n騷雞\n卫生巾\nfofg妈的\n干妳妈\nchinesenewsweek\n彭佩云\n六-四\n淫meimei\n粗硬 大 龟头 狂插\nrjwg\n喷\n幹拎娘\nfeng shen bang\n教育部\n婷婷淫女小说\n荣毅仁\nApexsoft\n什麼什麼大冒險\nｓｈｅｎ ｚｈｏｕ ｔｉａｎ ｇｅ\n抽插小穴\n8仙\n豔照門\n仙剑\n臭西化\n熟母\nd7se淫色贴图\n騷B熟女\n免费淫电影在线观看\nH淫乱乳娘 Tora Tora Gold Vol。23 星之雪\n法@掄\nhomosexuell\n豬操\n江猪\n新義安\n万淫堂\n令人喷血的春宫108姿势(组图)19岁禁看\nBT亚洲无码影片\n肉棒小浪穴┅\n爱幼阁\n布莱尔\nJHSZ\nlastchaos\n臺灣狗\n打手槍\n不良少女日记\n淫荡的嫂子\n爹娘\n宁王府淫图导航\n成人图库，操女人穴，色情贴图，性交贴图，操ＭＭ穴，超级淫图\n嘉联\n肉棒顶入小穴口\n你色吗\n干妳娘\n稻生美纪 - miki\n做愛電影\n女儿的阴道好紧啊\n操妳妈\n歪逼\n淫水四濺\n激情淫色贴图论坛\n我受不了啦!┅┅好勇猛的鸡巴\n冉英\n免费午夜电影\n毛台湾帝国\n黄家骁\n抽插39岁处女meimei淫叫不断\n狗狼养的\n六。四\nbuiltfree。org\n張 林\n美国小姐淫荡舔乳照\n大力丸\n炮友之家\n色情网站 色情导航\n干一干\n好嫩的屄啊\n免费淫色电影论坛\n網管\n毛射精\n歡歡娛樂時空\n毛西藏独立\n激情 乱伦 性\nasshole\n人大代表\nh站靠\n浪叫\nｍｅｉ ｌｉ ｓｈｉ ｊｉｅ\n法抡功\n逼痒\n情兽\nｍｏ ｈｕａｎ ｓｅｎ ｌｉｎ\n夏夏娱乐免费黄站\n免费淫色情电影 免费淫奸女Ａ片段试看\n法轮大法\n林心如特级片\n你弄得阿姨爽死了\n毛膏药旗\n麗春苑\n狂乳激揺\n钱 达\n緊穴\n超美少女系列 蛯原舞\n本站10019多部激情电影全部免费试看\n激情电影\n宾周\n激情成人桃色电影\n爸干得你┅┅舒服\n台湾情色網\n在乳房和阴道摸弄\n美乳美穴\n大史记\n女卫生间盗摄图\n淫水四溅\nMAD MAD\n毛去你妈的\n毛李宏旨\n毛妳马的\n免费黄色电影/色情电影/激情电影/黄色电影/我该死/A片\n乌兰夫\n肉棒精液淫液阴精\n免费淫色影院\n肉棒好粗啊\n清華同方\n花和尚成人论坛 成娱情色导航\n摸你雞巴\n劉少奇\nflyfff\n日本激情片\n怎么能知道女性手淫过\n大龟头顶住了她的花心深处\n巨骚\n干X\n小泉恶搞\n大肉棒插入阴户内\n給處女開苞\n新聞出版署\n插护士穴真爽\n金瓶梅4\n弓虽\n黑逼\n金瓶梅5\nこどもの时间VOL。1-7\n內衣\n金瓶梅1\n妹妹的阴道好紧啊\n金瓶梅2\n六。四\n金瓶梅3\n怎样延迟射精18岁以下禁\n亚洲穴贴图激情自拍穴\n曲線消魂\n毛经社理事会\n法(轮)功\n淫女教师小说\n枪淫少妇\n巨騷\n本站10021多部激情电影全部免费试看三天\n牛头马面\n日本av女优\nｂａｓｔａｒｄ\n逼癢\n伦乱小说大全\n亚洲美女\n三极试看片\n少妇自淫\n欠骑\n鄭萬通\n淫水橫溢\n肉棒插进子宫了\n毛阴户\nBaichi\n又紧又嫩的屄儿\n大型黄站\nbankai\nhuang yi qun xia zhuan\n毛塞白\n激情黄色在线\n傻鳥\nfuck新疆独立\n基地组织\n抽插47岁处女妹妹淫叫不断\nmeinv穴自拍\n二屄\n妈卖妈屁\n操车仑\n吊\n回教\n大鸡吧来回抽插着小穴\n毛克林顿\n牲口\n事屎\nfuck透视眼镜\nmo yu\n毛可待因\n爱图公园\n六四民運\n黃色漫畫\n朱骏\n毛傻屄\n干机掰\n媽的\n毛日他娘\nU-R\n卫星遭黑客攻击\n抢火炬\n吳 邦 國\n少婦偷情\nMUGEN EX VOL 9 人体喷泉!强烈な潮吹きホール!(MX-09)\n贱人\njiangdongriji\n色网阴道特写强奸32岁处女全过程\n在线黄色电影\n毛发展研究中心\n成人激情视频聊天室秘密入口\n东条\n东方传说\n色聊人午夜激情聊天\n巨炮肉穴\n老师的淫液\nwetback\n手记注册日韩5029部激情电影\n龟头抽插小穴\nTMD\n周總理\n乳峰\nfuck我干\n毛马克思\n成人导航网\n聯 總\n姫辱\n乳房急促而有节奏的起伏着\n操他\n毛耶和华\n肥嫩嫩的大陰唇粉紅色的小陰唇\n操插Gm\n运营\nｚｕ ｑｉｕ ｊｉｎｇ ｌｉ\n裹本\n一边一国独裁\n朝鲜\n插你爸\n色网阴道特写强奸28岁处女全过程\n娇妻超市被奸\n张 伯 笠\n射了\n肉棒 乱伦 肉洞\n东京物语Vol。 3 优质美少女\n龟头在阴道中抽插\n大鸡吧狠狠插进了她那又肥又嫩的小骚屄儿\n二奶大赛\n操插GM\n98bb成人小电影\n雞院\n摸陰蒂\nwtf\n激情淫少女穴\n打飞机\n操得直叫爽\n妈个老比\nf r e e n e t\n阴阜高耸\n强奸20岁处女全过程/*妹阴毛小穴\n天鹅之旅\n淫影院论坛\n铁道部\n丰满肥大有阴唇湿润了\n红宝石\n六四民运\n唐人电视台\n打手枪\nｄｉ ｑｉｕ ｗｅｎ ｍｉｎｇ\n现场做爱偷拍电影\n欧美乱淫贴图\n宮路ナオミ - 美腳★角度 (超美)\nwyd\n毛骡干\n淫荡少妇白洁\n麻醉枪\n法抡\nSex\n每日公告\nT浓厚中出FUCK Red Hot Fetish Collection Vol。44 : 夏川Saya\n六四平反\n顶到阿姨的花心\n歡迎G9情色電影院\n娘b\n法*掄\n射你\n发^^抡\n山本五十六\n如果你未满26周岁请自觉离开。\nｔｉａｎ ｙｉ ｚｈｉ ｌｉａｎ\n淫荡女护士 淫荡少妇穴\n淫奸护士论坛\n乳尻\n色情工廠\n棒插到妈妈的穴里\nｘｉｎ ｓｈｉ ｑｉ\n遊戲送獎員\nfuck售号\n中國政府\n娘B\n激情淫色小穴\ntiao zhan\n抽插淫水\n毛肛\n劍網2\n劍網3\n無碼無套\nphuc\n妈的靠\n烟膏\n乳尖\n成人午夜影院\n魔兽金库\ndroiyan\n雞雞\n强奸24岁处女全过程/淫妹阴毛小穴\n大波骚妇\n少妇扒开阴道图-淫賤少女阴唇特写图免费淫电影a片\n淫水橫流\n色电影\nfuck唐家璇\n西藏天葬\n极品激情影院\n狗娘养的\n食捻屎\n又硬又大的肉棒插入阴户\n程真\n天之炼狱\n龟头在她的阴户里抽插\n仑功\n魔兽金币\nkan66免费电影\n顶级学妹美穴淫图\n地震捐款\n巨乳俏女医\n捐款名單\n美女含双棍玩内射\nluanshi\n賣國\nsan guo\n正見網\n转法轮\n发@@仑\n六四以后\n吳桂賢\n开粉红小阴唇\n日本大型黄色meinv做爱\n發~輪\n台灣豬\n亚洲激情淫贴图\n南联盟\n寻仙\n淫水淋湿的阴毛\n亂倫熟女網\n肉棒捅入阴道里\n淫荡小护士\nfuck社保基金会\n大陆性meinv淫穴\n童屹\n操布雷尔\n毛想上你\n风骚小护士\n朝鮮\nｔｉａｎ ｊｉａｏ\n@sshole靠\ncha你\n带链\n厕所盗摄护士姐姐性感\n龟头狂插肉洞\n亚洲激情淫搔穴贴图\nfuck外挂\n毛情色\n李弘志\n法@抡\n骆 冰 淫 传\n泡365免费电影\n董建華\n处女流水淫图\n草你媽\n小浪穴 插死 人家了\n台湾 18 成人网站\n淫色网址导航\n剃毛\n毛老母\n朱駿\nGroove\n羞恥母\n淫浪女 淫妻浪女 于东 浪女淫叫 淫妻……漫画 淫色贴图东方淫色\n遺忘傳說\n汽车爆炸案\n看棋器\n抽插50岁处女妹妹淫叫不断\n插比\n張梅穎\n萬人暴\n法掄\n激情淫色贴图网址\n红海湾\n本站10010多部激情电影全部免费试看\nwstaiji\ncunt cunt\n粉红色的小嫩逼淫\n妈要吃你的肉棒\n操你奶奶\nｊｉｎ ｗｕ ｓｈｉ ｊｉｅ\n董必武\n狡猾的风水相师\n粗挺的阴茎上沾满了处子的鲜血\n性器\n插gm\n蓋世太保\nU/R\n阿姨的阴道好大\n上海交警\n林榮一\n癡乳\n性感妖娆\n肥嫩的阴唇\n妹妹嫩穴被狂插\n哥哥使劲插我 爽死了 啊 啊\n外交论坛\n毛妹妹免费电影\n梁保华\n发@@伦\n毛小穴\n插gM\n99bb\n托管理事会\nFuzz Vol11 Ririko Asahina 朝日奈りりこ 加藤つばさ\n1989#事件\n骚妹妹\n母子淫奸乱轮现场看，瞄準她的肉穴插了進去……\n扬震\n韦国清\n成人激情电影通道\n湿淋淋的阴道被肉棒插出血\n河 殤\n夏夏娱乐 黄站电影\n淫民俱乐部做爱顶级\n激情淫咪咪贴图区\n毛检察院\n东京热5月新片\n癟三\n瘋狂抽送\n華夏Online\n小泽玛丽亚7~淫乱的肉体\n淫少妇\n鸡巴靠\n免费视频激情聊天室\n台湾18黄网亚洲华人bbs性论坛\n毛窃听器\n美国淫女在线看电影欣赏mm乱淫自拍图片\n賓周\n河 殇\n胡景涛\nWEB战牌靠\n東京熱最受歡迎長腿meinv~藤澤安奈~腿長人又漂亮\n激凸走光\n法~~掄\n操女人穴\n干的你\n魔性的美女 鈴木麻奈美\n强制执行 女体崩壊~YUU Vol。1\n孙文\n草你娘\n令人喷血的春宫108姿势(组图)18岁禁看\n四我周\n毛摇头玩\nfuck四人帮\n午夜的诱惑网友自拍\n少女艳星诱惑色图\n国权\n法$抡\nBT无码区 激情电影\n干八个meinv的小穴\n免费淫爆影片\n电监会\n劳拉\n山西黑砖窑\nmimi淫色贴图\n大海战II\nｓｈａｉｙａ\n毛肉壁\n深愛色色\nmo jian\nmeimei的阴道好嫩啊\n狂插meinv护士小说\n毛台联\n粮食局\n口爆吞精\n经社理事会\n20dy淫色贴图\nsanguohx\n淫色贴图网\n小便\n性感妖嬈\n少儿不宜\n公媳乱\n毛独立台湾会\n蒋震文\n扒开嫩穴\n大龟头在我的阴道口来回磨擦\n發^輪\n淫奸姐姐小说\n台湾黄色成人电影\n草你妈\n日本姐弟乱伦小说\n阿姨的阴户好紧\n免费成人激情视频聊天\n公媳亂\n少女被插\n婷婷成人激情网站\n21dy淫色贴图\n奶大屄肥\n黃色圖片\n李鵬\n狗雜種\n免費A片\n淫荡贵妇\n獨立臺灣會\n奇淫的老板娘\nmeimei穴\nfuck生孩子没屁眼\n温暖的冬夜老师拼命亲吻我\n免费电影 成人电影 激情电影\n肉棒抽插……的爽死了\nfeng shen\n圣战\n台湾政论区\n朱胜文\nｄｅｋａｒｏｎ\n恶搞晚会\n吾尔开希\n钱运录\n秦真\nTora Tora Gold Vol。24~伊泽夕\n插 10p\n高自联\ndu shi 2046\n毛经济社会理事会\n操陈至立\n爆乳人妻\n迷奸药\n狂插处女血流不止!\n童顏巨乳\n饱满的乳房肥嫩的阴唇又肥又嫩的小骚屄儿\n操财政部\n智能H3\n毛国研室\n北大三角地论坛\n民联\n财政部\n热血江湖\n发~仑\n大鸡巴干进妹妹的小穴\n舆论钳制\nｂｉ样\n品嘗不倫主婦\n插 11p\n小姨子穴\n粉嫩小淫穴\n铁血三国志\n无尽的任务\n捅入她的阴道里\n白立朴\n妹妹　MM激情\n帶链\n婊子靠\n肉棒乱插 好舒服\n霍金\nmeinv出租车内强行进入\nfuck死全家\n猛舔她的肥穴\n扉之陰\n操大法\n插 12p\n尋仙\n又粗又大的肉棒\n江八点\n李至伦\n张磊\npetrealm\n淫奸meimei电影论坛\n阿弥陀佛\n台湾建国\n莫伟强\n免费淫电影，免费淫色情电影\n馬万祺\nGold Angel Vol。7 Super Erotic Girl : Ran Monbu 紋舞らん\n插 14p\n激情淫片\n哥哥淫奸妹妹穴\n插 13p\n本站10012多部激情电影全部免费试看\n宋平\n你爺爺的\n发~伦\n成人色情网\n姐弟激情淫影片\n毛傻子\n大龟头顶住子宫深处\n干ＧＭ\n王文京\n洪志\n内衣\n接班\n九评共产党\n中共/中共中央\n乳射\n插 15p\n大花逼\n濕身誘惑\n手记注册日韩5020部激情电影\n教室炉轮奸精液过剰注入 若月秋穂\n真 善 忍\n美媚穴贴图\n杀你一家\n又大又嫩的穴\n淫乱电影\n【亚洲/无码】【生奸中出无套内射-相沢、夏樹、 美月遥】【AVI/1220MB】\n脱衣舞\n淫色电影免费在线观看\n日本激情淫\n艳舞淫业\n强奸17岁处女全过程\n法$掄\n淫奸电影少妇扒开阴道\n杜冷丁\n肉棒插入肉缝\n你它马的\n扒开嫩阴道\n法*抡\nNinn Worx - Fem LAmour\nｌｉｕ ｘｉｎｇ ｘｕｅ ｙｕａｎ\n邪教\n俏臀攝魄\n徐邦秦\n奴隸調教\n淫水漣漣\n無碼\n少妇房中自淫，和两勇男玩9P，操得直叫爽 操日本淫女穴\n操你\ntranssexual\n口含鸡巴\nKEFU\n国安局\n淫电影//处女第一次阴道做爱全过程(全套图片)\n淫妻乱交\n免费色情电影\n林心如淫电影\n色meimei成人论\n热血英豪\nmes couilles\nU R\n内裤\n幹GM\n干鸡\n光通\n毛麻醉钢枪\n毛社。会。正。义。力。量\nbt 色情工厂\n华盛顿\nJiaochun\n扉之阴\nYellows Vol。2 女体天国 2 天然素人娘18人\n毛共产主义\n龟孙子\n赵 紫 阳\n專制\n淫奸meinv图 淫奸刘亦菲\n毛特别公告\nurban靠\nav成人\n日韩成人激情电影\nh r i c h i n a\n她奶奶的\n激情淫贴图\n朱嘉明\n快感\n食屎\n毛国防部\n露逼\ntranssexuel\n骚货\n活动管理员\n任弼时\nctracer\n大妈油\n情色小电影\n宁王府导航\n强奸林心如扒开阴道\n周铁农\n插你祖宗\nａｉ滋\n溫泉室的男女\n淫屄儿\n胡景濤\n射进女儿的屁眼里\nfuck透视软件\n舔她的花蕊\n我和小少妇乱伦理淫穴\nPP一点通 - 成人电影\n母子交歡\n天安門大屠殺\n日本卡通淫色动画片\n公车淫奸姐姐\n大陆淫乱美女图图片\n無界\n嗎b\n鬼畜轮奸\n让我的肉棒缓缓的插进潮湿饥饿的娇嫩的肉洞里\n炮打中宣部\n于永波\n走资派\n內褲\n网游久久金币\n性奴\n成人文學\n明斯克号\n操吹箫\n精液喷涌而出\n毛日X妈\n六月four日\n骚姐姐\n毛外挂\n宝德\n成人文学\n轮盘机\n华义\n三個代表\n系統公告\n免费成人激情视频聊天室\n日本黄色小电影\nda tang hao xia\n中央电视台\n插少女阴道\n抽插 捅 挺 阴唇 处女膜\n母子交欢\n天使成人网我该死免费视频\n江獨裁\n滾那嗎\n毛懒教\n丁關根\n国产偷拍\n肉棒弄得湿溜溜的\n猪聋畸\n全裸\n楊帆\n卐\n刘全喜\n满洲国\n毛普京\n疯狂抽送\nSEX\n區\n淫电影//少妇扒开阴道图-淫賤少女阴唇特写图\n猪毛1\n卵\n89bb成人小电影\nmeinv淫色贴图\n傻鸟\n抗震\n大紀元\n剛比\nTokyo Hot n0188 田中爱\n毛国民党万岁\n和护士jiejie淫穴\n操你妈屄\n毛屁眼\n小穴插的紧紧\n淫穴淫水流\n張 志 清\n55sss偷拍区\n55sss偷拍區\n脱内裤\n發~論\n媽祖\n毛卖逼\n插 操 干 骚穴 嫩穴 小穴 密穴 淫水流\n兔meimei色色贴图区\n毛夜情\n宁王府成人论坛导航\n岩崎 Sky Angel Vol。27\n天地玄門\n淫荡口交\nｗｗｗ。ｇｐｃｈｉｎａ。ｎｅｔ\n聚友網路\n摩門教\n他馬的\n个批\n賣卡\n专政\n人妻熟女\n小骚穴┅┅好舒服\n64xp。com 电影免费\n操陈毅\n安息\n蜜穴肉棒肉縫\n少妇大穴VS驴吊\n把舌头伸入姐姐的\n發~~倫\n日本免费黄站\n一黨獨裁\n於永波\n64事件\ntranny\n郭平\n肛門拳交\n毛强硬发言\n務\n法车仑工力\n輪流執政\n法~~抡\n灨你娘\n我讓朋友強姦我的小姨子\n大城舞衣子(有碼無碼)大合輯~~~超優!!(可分開下載哦!!!)\n操妳娘\n合成淫色贴图\nQqyinsu\nfuck晚年周恩来\n小穴┅┅好棒┅┅好爽\n孔雀王\n操医生小穴\n淫妹妹电影\n强奸42岁处女全过程/淫妹阴毛小穴\n妈逼靠\nlineage2\n大肉棒刺入肉穴\n吴桂贤\n成人激情视频聊天\nGMworker\nｓｅｇａｍｅ\n阴道好肥啊\nTaiwan国\nneosteam\n日本淫色电影\njoyxy\n色情靠\n無毛美少女\n毛王乐泉\n小魚兒與花無缺\n中宣部是社会的艾滋病\n乳头\nepochtimes\n河殇\n插GM\n女秘书做爱吞精图\n少妇房中自淫，和两勇男玩14P，操得直叫爽 操日本淫女穴\n黑五类\n毛法(轮)功\n厲無畏\n摩力遊\n店長推薦最新強片Sky Angel Vol。43最新強片\n操妹妹\n杨震\n發**論\n法轮公\n爽图网\n出售账号靠\n法轮共\n穴太紧了 插进\n你他妈的\n毛神通加持法\n毛我干\n免费操小姨子 操小姨\n性欲\nvoa\n含 肉棒 插 小穴\n潘金莲全传\n发改委\n今晚我把表姐操上了\n欧美激情\n天天任我淫\n网址大全\nｍｏ ｓｈｏｕ ｓｈｉ ｊｉｅ\n阿沛•阿旺晋美\n够酷GOQO\n统独论坛\n毛阝月\n激情淫色故事\nfuck新闻出版\n打飛機\n操妳媽\n佛展千手法\n龙 虎 门靠\n欧美暴淫 rsync 月宫成人贴图区\n国产AV\nchinesenewsnet\nｘｉｎ ｘｉ ｙｏｕ ｊｉ\njin yong qun xia zhuan\n插Gm\n啊……我的小屄……给你……肏得好……舒服……啊\n金新月\n靓穴meinv\nfeltch\n幹全家\n中共中央\n操那嗎逼\n子女任职名单\n卖卡\n谷牧\n幹逼\n東方傳說online\n無需刷卡\n俞正声\n淫穴小说\n台湾成人25贴图\n梅毒\n騷浪美女\n致幻劑\n法沦\n毛乡巴佬\n自由亚州\n黄色做爱小说\n成人激情影视区在线视频脱衣服\nｇａｍｅ　ｍａｓｔｅｒ\n昝爱宗\n歐美大乳\n淫秽小电影\n致幻剂\n法輪功\n肥嫩的小骚屄儿阴部\nmeinv淫色贴图区\n弟弟粗大的肉棒\n人民代表大会\n天地玄门\n圣女峰\n成人激情电影网\nｗｏｒｍｓ\n臭b\n大……鸡巴……操得……很……很……舒服\n淫奸jiejie\n江择民\nxiao77\nbaichi\n大亂交\n农村公公与媳妇乱伦\n曾 庆 红\n超激情电影快速注册 在线观看爽!爽!爽!！\n毛水利部\n恥辱醫護免费\n风骚女秘书\n我也要泄了\n操产权局\n毛国管局\n法~掄\n浪婦\n肏妹妹屄\n毛投毒杀人\n搖頭丸\n庆红\n看着有点舍不得操\n89學潮\n亚洲激情\n法@@仑\n伦理淫穴小说\n阿呀娃娃\nｔｉａｎ ｘｉａ ｗｕ ｓｈｕａｎｇ\n姘頭\nItsuka_part2\n毛三唑仑\n性交自拍\n无码做爱淫图\n撒尿\n好紧的阴道\n妹妹的小阴户\n火辣辣免费爽片\n火辣写真\n用手指分开两片肥嫩的阴唇\n肉棒插入妹妹小穴\n傲神傳\n毛徐光春\n她妈靠\n自由亞州\n姨妈的肥阴唇\n１９８９ 天安門\n法*淪\n强　奸\n免费淫奸女Ａ片段试看 少妇扒开阴道图-淫賤少女阴唇特写图\n１９８９ 天安门\n挺着硬梆梆的肉棒\n干逼\n網路公祭\n欧女乱欲\nkart\n骚姨妈\n5jq\n电影小片段试看免费淫影片\n红月\n賣幣\n一夜情聊天室\n天安门屠杀\n龚学平\n六四之後\n毛哈批\n嫂嫂我要\n鞭王\n最淫熟女网\n肉棒插入淫荡少妇穴\n陰道圖片\n後庭\n把精液射进我的小穴里\n亚洲激情淫色网址\nｈｕａｎｇ ｙｉ ｑｕｎ ｘｉａ ｚｈｕａｎ\n韩正\n邓力群\n性交贴图\n乱伦淫书成人网\n三级片看美国女人的性交\n莫索里尼\nｓｈｅｎｇ ｚｈｅ ｗｕ ｄｉ\n淫逼嫩穴清晰图\n主席\n恥辱醫護\n奇侠XIAH\n屙\n人代\n手记注册日韩5030部激情电影\n操女人嫩穴\n總書記\n毛游戏发奖员\n總理\n屍\n妈妈美穴\n屌\n福尔摩萨\n屎\n屄\n男女激情淫影片播放\n淫女刮毛\n尿\n乱伦禁书\n尻\n毒販\n少妇房中自淫，和两勇男玩0P，操得直叫爽 操日本淫女穴\n激情日本电影_成人情色电影_成人小电影\nmeimei淫水 流\n卢跃刚\n余英时\ntianji\n激情做爱色情淫妇做爱\n經血\n密宗\n美女偷拍成人小说激情网\n亚洲成人激情\n神话R。Y。L\n毛卫生部\npolla\n共*党\n日九城\n卡通动漫美女淫乱\n毛胡主席\n台湾共产党\nＵＲ靠\n先烈的电话\n反日活动\n毛贺国强\n台湾国\n抽插48岁处女妹妹淫叫不断\n射\n捏你雞巴\nfuck温加宝\n疆独\n李師父\n护士淫乱\n新唐人\n插得好爽啊\n发@抡\n任你淫\n鄧小平\n小骚货\n周鉄農\n两片暗紅的陰唇一張一合\nsoundofhope\n幼逼\n熟美婦淫交實錄\n美少妇的哀羞\n硕大龟头紧紧顶著她的阴道\n肉洞深处不断地蠕动\n盘古软件\n王秀丽\n淫乱色情小说\nfuck王昊\n24dy淫色贴图\n安拉\n卖号\n星光伴我淫\n和姐姐插穴\n共軍\n午夜激情免费电影免费看激情小电影\nzhuanfalun\n打倒\n激情贴图\n淫荡妖艳女医生\n少妇做爱小说\n核潜艇\n原千寻~白领迷奸 Finger Charm\n怎样延迟射精20岁以下禁\n毛网管\nqq视频脱衣秀亚洲少女韩国无码片\n淫奸姐姐\n肉棒插入肉穴\n法@沦\n恐怖分子\n干meimei小穴\n毒贩\n水浒Q传\n167网址宽频影院网\n日本激情免费小电影\n(无)导航网站连接\n李大钊\nSOD 厳选!残虐レイプ集\n帝国传奇\n薑春雲\n你他马的\n赵 晓 微\n毛多维周刊\n免费meimei黄色淫乱电影\n法淪\n草拟妈\n疆獨\n如果你未满18周岁请自觉离开。\n日韩淫乱美女图\n滾那嗎錯比\nＧＡＭＥ　ＭＡＳＴＥＲ\n人民之声论坛\nｓｅ\n厕所盗摄meinv\n光明日报\nfuck统计局\n毛高俊\n他妈地\n蒙古独立\n鐵甲爭雄\n日本情色无码光碟\n搞比\n劳改\n大肉棒插进我的肉屄\n如果你未满25周岁请自觉离开。\n動漫色圖\n看牌器\n性感乳娘\n新人调教 Red Hot Jam Vol。9彻底调教凌辱人靓逼美\n情色成人大全\n淫荡的妈妈8淫荡护士9淫荡空姐\n海天英雄传\n好紧的屄\n9之遊\n鹿城百家性大型网站\n爽你\n全国两会\n成人视频\n武雷\n大乱交\n欲仙欲死\n干你媽逼\n淫奸护士穴论坛\n身份生成器\n法@@倫\n欧洲男女激情淫乱穴 mm贴图网\n明慧網\n52xp。com 电影免费\nmei li shi jie\n阴道夹裹住阴茎\n卖淫女全过程\n未满21岁的请自行离开 ！\n民航\n操小姨子\n淫奸小姨淫水流\n毛联大\n强奸20岁处女全过程/淫妹阴毛小穴\n日本有码\n毛威而柔\nfuck王太华\n那吗逼\n免费看三级电影淫电影艳情电影\n拉案)\n搞死\n毛六合彩\n64動亂\nａｒｃａｎｅ\n欧美靓穴小小论坛帖图母子姐弟淫……男男激情欧美淫贴图\n下载淫电影\n淫电影艳情电影\n起义\n骚B熟女\n贾育台\n张伟华\n富人与农民工\n毛台湾共产党\n金日成\n法$沦\n性愛擂臺\n新聞出版總署\n免费黄色淫秽\nLady Sonia-熟女絲襪 3\n司馬義？鐵力瓦爾地\n东京热 n0151 工藤裕子 美丽子宫大精液轮奸教室\n日本有碼\n百家性大型黄站meimei\n又解开乳罩 ┅鸡巴\n黑手黨\n赌博\n造爱\n龟毛\n豔照\n赛他娘\n免费情色电影情色五月天\n毛售号\n阳具在他的穴里大幅抽插\n客报\n宁王府\n亚洲激情淫影院\n薛伟\n肏肉~X屄爽歪歪\n强奸大奶嫩逼meinv\n毛王宝森\n屌七\n淫奸妹妹电影\n苯丙胺\n法碖功\n中华时事\n遊戲管理員\n红楼 绮梦\n素人著エロ俱樂部 こももちゃん\n办公室性欲\n大肉棒在妈妈的阴道\n朱熔基\n毛审计署\nfuckyou\n怎样免费看淫电影\n毛世界日报\n插姐妹小穴\nｍｅｔｅｏｒ ａｃａｄｅｍｙ\n胡锦濤\n色情 娱乐 导航\n风骚女友浴室放荡\nㄙㄞ你老母\nMysupersales\n小穴里抽插\n網络公祭\n糖糖小穴\n八路\n售ID\n小泽玛丽亚~闷绝痉挛无限制潮吹\n毛阴核\nAYAWAWA\n干jiejie小穴\nfuck倭国\n强奸43岁处女全过程/淫妹阴毛小穴\n劫机\n黎阳评\n集體性愛\n大j8\n少妇激情电影\n解放軍鎮壓\n国产av\n龚學平\nfuck无界浏览器\n幹\n美国成人激情淫洞网站\n开苞\n骚b熟女\n浪妇\n嫩穴 小穴 花心\nｗｕ ｌｉｎ ｗａｉ ｚｈｕａｎ\n三去車仑\n幹死CS\n浪女\nfuck十年动乱\n性交\n法*沦\n傳奇3G\n旅馆自拍\n粉饰太平\n少妇房中自淫，和两勇男玩18P，操得直叫爽 操日本淫女穴\n独裁政治\n淫妇周艳茹和儿子的性战\n毛特码\n奥马尔\n高校骚乱\n毛驶你老师\n法@@伦\n小嫩穴贴图\n法~~淪\n欢乐数码\n统战\n马良骏\n苯環已哌啶\n工商局\n法~抡\n絲襪寫真\n免费淫奸女直接试看\n手机注册色情电影网站\n美國之音\n江八条\n淫荡女优放荡插逼\n妹妹的大阴户\n大 纪 元\n超性感裸体艳舞 孔裏唾梦 DANCE 2 アナリズ现场收音\n九城\n成人片\n幾芭\n罗  干\n绵恒\nｌａｇｈａｉｍ\n10 Little Asians Vol。 14\n@sshole\n把你的小穴夹紧……用力……\n总局\n天安門\nｍｏ ｆａ ｑｉ ｂｉｎｇ\n不得入内\n不用注册免费淫 电影\n小姨子的阴道好紧啊\nd7se淫色穴图片和meinv发春激情3级片试看\n幹死GM\n强奸33岁处女全过程/淫妹阴毛小穴\nGameMaste\n淫奸阿姨电影\n插入 嫩穴\n正见网\n法$淪\n法@@侖\nbedclip。com\njing tian dong di\n插死你\n公车淫奸妹妹\nteensexy\n一夜情异物插穴性爱网\n暗黑之門：倫敦\n成人激情色情电影\n亲情淫乱\n骚逼\n粗肉棒 大鸡巴\n淫奸女同事穴\n9城\n劳教\n我就去色\n林心如被疯狂轮奸\n在线淫电影网站\n天安门\n尸体\n舌头在小穴周围舔弄\n郭岩华\n肉棒插进了湿湿的阴道\n淫奸姐姐 妹妹 MM 姨妈 舅妈直到流水\n性佣\n免费淫水流\n彩虹骑士\n天安門屠城\nling you ji\n法^掄\n歡迎光臨亞洲瘋情\n游戏道\n無碼小處女\n龟头顶开阴户\n周子玉\n凤舞天骄\n刀劍？英雄\n破天一劍\n淫荡少妇\n老騷比\n台湾19岁成人\n棉条\n陳希同\n肛门喷水\ndafa\n强奸46岁处女全过程/淫妹阴毛小穴\nNPC靠\n畜生\n大陆温州鹿城黄站\n王子淫传\n親民黨\nre xue jiang hu\n奸\nE块冒险\n升天\n余秋里\n爆吧\nbiaozi\n未成年人禁入\n毛短信群发器\n蔣彥永\n射奶\nfuck台联\n毛欲火焚身\n破天一剑\n成人影院\n欲仙欲浪\n徐向前\n发论公\n干妈妈的小穴穴\n功法\ncounselor\n廁所盜攝\nfuck外交部\n三去車侖\n我和网吧老板娘\n胡锦淘\n365淫色贴图\nQQtang\n免费淫 色电影\n妈\nmeimei嫩穴\n毛你大爷\nTMD靠\n密室淫行\n靠背\n日本激情淫骚穴\n干 淫洞\n嗑药\nfuck兴奋剂\n官方\n强奸17岁处女全过程/淫妹阴毛小穴\n小穴好爽\n泽民\n妓\n蜜穴被阴茎插入\n劉伯承\n游戏管理员1\n鸡巴全根没入 阴道\n姦\n淫色宝贝导航\n禽獸\n明慧网\njiejie\n嫩穴\n床上激情自拍图\n6。4\n完美世界\n透視軟體\n瞄準她的肉穴插了進去……\nｍｅｒｄｅ\n江罗\nWG武功\nA片大放送\n用手指挖她的小穴，弄得她娇喘连连\n哦……你插的好深啊……又顶到我的子宫……啊\n毛燕玲论坛\n肏meimei淫屄\n香港成人激情论坛\n吴宏达\n毛肛交\n死刑过程\n李师父\n妹妹的阴唇好嫩啊\n卖国\n遊龍線上\n姘\n免费品嘗不倫主婦\n抽插28岁处女meimei淫叫不断\n皇军\n操吹喇叭\n捅B\n毛鸟gM\n婷婷激情淫液\n六四以前\n大肉棒插进了她的体内\n用舌头对着阴道阴蒂等地方用力地舔着\n毛发奖使者\n毛我日你\n萬稅\n测拿\n本站10005多部激情电影全部免费试看\n荡妖艳女医生\nchinamz\nfuck希望之声\n淫电影//令人喷血的春宫108姿势(组图)18岁禁看\n幼女口交\n嫩穴\n屍体\n狂插嫩穴 浪穴 骚穴\n宗教\nmeimei小穴好紧\nｍａｄｅｌｏｖｅ\n阴道深处\n天安門事件1989\n郁慕明\n建国党\n外$$挂\nfuck小乳头\nfuck天安门屠杀\n慈善会\n国外A片电影-美国色情电影一点点激情下载\n性愛\n偷窺圖片\n毛驶你爸\n免费成人聊天室\njiejie的嫩阴户\n胡启立\napexon\n激情淫色漫画论坛\n亚洲成人激情网站\nｆａｌｕｎ\nqi shi\n素人初撮り生中出し 观月雏乃\n去氧麻黃碱或安非他命\n强奸18岁处女全过程/*妹阴毛小穴\n免费淫乱卡通动画片\n淫荡家教\n性傭\n张萬年\n365成人贴图\n毛黄菊\n44lian色站大全\n亞甲二氧基甲基安非他明\n情色图片\n少妇白洁\n床上功夫\n日本淫荡少妇\n毛公安部\n江core\n亚洲meinv\n幼女情色论坛\n积克馆\n鸡巴插肉缝 淫荡 小穴\n舔着两片湿润的阴唇\n反人民\n龙洲影院日本娱乐性免费\n萨拉托加\n焚烧\n胡锦滔\n金瓶梅情色網\n坐台\n淫蕩貴婦\n发论功\nｔｕｉ ｔｕｉ ｘｉａ\n插入妈妈的穴好爽\n肉棒插入乱伦射精\n婷婷成人激情淫洞\n万润南\n老师嫩穴 极品嫩穴 淫穴嫩穴\n戀愛盒子OL\n性息\n逼樣\n大话西游\n彭真\n干丰满少妇\n買幣\n干妳老母\n掰穴\n鸡巴猛插\n江八條\n插进小穴射了\n新人调教 Red Hot Jam Vol。9 Myuu\n毛奸你\n恐怖主义\n勁樂\n性爱插穴\n你呀的鸡巴长歪了吧\n採花堂\n毛鸟gm\n干一家\n性情\n宋庆龄\n十三点\n反政府\n姐姐和我乱伦理电影\n异见人士\n淫荡┅插 小穴\n法~~沦\n少妇房中自淫\n韩 联 潮\nyouxia\n短信群发器\n發倫\n毛孙中山\n爱图公园论坛\nbutthead\n毛希望之声\n客服\n六？四\nwowcaifu\nEzgaming\n絲襪\n富豪们的疯狂做/爱方式\n肏烂你的屄\n成人小电影\n天安门录影带\n少妇\n皓宇互動\n狂舔少妇两腿之间\njie tou lan qiu\n操婊\n365情色小说\n淫乱护士激情电影\n肏meimei屄\nPENIS\n大奶骚女\nf l g\n招雞\n亂交\n孕\n免费黄色淫乱电影\n日本风骚女电影\n皓宇互动\n法^^侖\n你爷爷的\n在线观看淫电影\n弟弟的肉棒又粗又大\nRed Hot Fetish Collection Vol。28\n肉棒又粗又大少女又痛又爽\n少修正\n被操\n女老板一丝不挂躺在我身边\n阻无通畅\n骚妇露逼\nmeng huan xi you\n和親jiejie作愛\n見到這個美女就會有性欲\n把肉棒抽出来\n咬着她的奶头干了她\n在线淫电影\n几芭\nH动漫\nlaghaim\n無需入會\n幻灵游侠\n毛商务部\n貞操\n日本淫色漫画 淫色少妇小说 淫色少妇\ntmd\n品色激情电影\nfuck特码\n……好舒服 ……好爽\n大鸡巴\n肉棒抽出她嘴中\n林心如色情贴图 性交贴图\n发$抡\n曰gＭ\n任建新\n毛贱\nＧＡＭＥＭＡＳＴＥＲ\n鑽插\n肉棒插进来肉穴\n妈妈挺动雪白的肥臀\n鸡巴顶在女儿的嫩穴\n李紅智\n大型色站\n新鄭和\nU-R靠\n中央\n和妹妹玩插穴\n翹臀嫩逼\n在线激情淫电影\n乳此丰满\n针扎\n法^^仑\n勁暴\n蒙尘药\n張筱雨\n毛胡紧掏\n舔她的小洞洞\n免费淫奸女Ａ片段试看 欢迎点击进入观看/林/心如/20岁处女偷拍\n鞭神\n免费淫色meimei电影\n免费成人娱乐電影\n小骚逼\nTora Tora Gold Vol。23 : ほしのゆき Yuki Hoshino\n鸡鸡\n法^^伦\n性交极品大片 美腿少妇\n共榮圈\n学生淫图\n毛鬼村\n毛塞你老师\n毛淫秽\n89学潮\n毛色情服务\n弟弟淫奸姐姐论坛\n阎明复\nTmd\n淫色护士小说 淫色故事 淫色文章\n熟睡中的妹妹\n强*十八岁meinv护士看完保证你打飞机免费电影网站\n外$$掛\n性教育片－性教育电影\n劉淇\nsnatch\n全球华人春节联欢晚会\n毛湿透的内裤\n中奖\n日本av\n精子射到姐姐的穴里\n账号\n公车上干女司机\n学生爱国者运动证明\n他奶奶的\n貓撲\n娘\n北川\nshine\nｊｉａｎ ｘｉａ ｑｉｎｇ ｙｕａｎ\n偷窥拍摄\n颜射\n夏夏小说黄站\n注册免费淫电影\n曹长青\nRed Hot Jam Vol。8 让你用子宫感觉 藤崎夕凪\n干的好爽……要爽死我了\n盜竊犯\n敢坐飞机吗\ndamn\n美国凯德\ndamm\n操她妈\n百家性大型bt黄站\n娼\n李建國\n宁王府成人导航\nsurvivalproject\n李建国\n海底水中插屄~Aqua Sex\n李淑娴\n毛教育部\n劲樂\n都市情爱乱伦淫妻古典武侠制服明星性爱宝典24式教材\n毛阴水\n师春生\n动漫激情淫色贴图\n发伦功\n好大的肉棒\n发*抡\n少女bb图\n64动乱\n不斷收緊陰道來吞噬我的龟头\n婊\n神通加持法\nMM被干小穴\n甲基苯丙胺\n教師禁斷接吻\n胡锦涛\n强奸meinv狂插处女阴道\n免费色情电影 强奸小说\n操淫荡 空姐\n浴尿\n处女性开苞 免费黄色淫乱电影bt下载试看\n操她\n被插\n神雕外传之郭襄\n公公狂操儿媳妇小穴\n102bb成人小电影\n婬\n幹機掰\nkai tian\n溫傢寳\n少妇操b图 操农村少妇 少妇操尿图\n征服\nn0255 三国冬子 - 不用厌牝中出し廃弃処分 _k7\n苯甲酰甲荃芽子堿\n毛阴毛\n市長\n抽插44岁处女meimei淫叫不断\n周恩來\n市长\n东南西北论谈\n媽\n發侖\n大 紀 元 新 聞 網\n再用力插…。啊\n狂插小浪穴\n┅插┅小穴┅插我\n新石器\n13点靠\n公交车上淫乱小穴狂插\n女死囚\n干小蜜洞\n操妳\n輪奸大meinv\n毛枪支弹药\n钱国梁\n一个事实\n操妻\n毛李宏志\nwww。gpchina。com\n亂倫\n日本淫虫在线看电影\n問道\n淫亂\n毛王刚\nheterosexuell\n毛江猪媳\n嫖\n法^抡\nh动漫\n五不\n江澤民\n淫乱\n赵洪祝\n法^^倫\n倚天屠龙别记\n98bb\n西藏独立\n靠腰\n蒋彦勇\n逼毛\n華義\n淫书\n上床\n广闻\n血灑\n你色嗎\n毛吴邦国\n握着他的鸡巴上下套弄\n六四周年祭\n淫液流入\nadministrator\n肉棒狂插嫩穴\nsan guo yan yi\n巨乳meinvRINA无码第2弹\n董建华\ncrazyspeed\n去妳的\nAV女优-杏子\n封淫 Sky Angel Vol。50 宫泽Keito 超美系模特儿初无修正引退纪念作\n淫女 喷精\n骚浪美女\n毛卖。国\n迅雷下载免费黄色淫乱电影 免费淫奸女Ａ片段\n毛江青\n成人情色贴图\n51yxb\n大粗鸡巴插进小穴\n毛回回\n日韩免费午夜电影\n道教\n鸡巴在她的阴道里抽插\n毛国防科工委\n社会进步党\n姐弟淫穴小说\n爆乳\n世紀天成\n淫丝荡袜\n丫的\n毛骚逼\n大鸡吧插进阴道\nｌａｔｅｉｎｏｓ\n阿兵哥艳遇录\n逼样\n出租汽车罢工\n成人激情\n哥哥淫奸meimei论坛\ndigimagics\n本站10003多部激情电影全部免费试看\n烂游戏靠\n淫*女电影片段试看\n彭德懷\n曰\n毛乳交\n阿兵哥言语录\n王震\n日本淫蕩孕婦\n天皇\n格老子\n塞你老母\nJap Jap\n日死你\n大鸡巴在她的蜜穴里抽插\n日屄\n免费台湾18电影\n日本淫乱漫画图片 欧美母子淫乱图片\n中华人民实话实说\nhuanet\n车臣\n幸存\n淪功\n阿里布达年代记\n小穴 肉棒 插穴用力插\n紅 燈 區\n全免费淫色电影\n抽插22岁处女meimei淫叫不断\n功夫小子\n香港激情淫色影院\n女教師赤版免费下载\n成人 性爱\n迷奸系列\n法輪大法\n在乳房和阴道mo弄\n婊zi\n发@论\n带套肛交\n毛售ID\n强奸大奶嫩逼美女\n现金\n操布莱尔\n18dy淫色贴图\n淫花宫色女中心\n欢迎点击进入观看/林/心如/20岁处女偷拍/免费A。B片\n无毛美少女\n精水\n国产熟女做爱深喉肛交无码\n濫逼\n屌鸠\n张立昌\n干它\n嫩B弓雖女幹\n激情淫色文章论坛\n幼女狗交\n私#服\n花样性交\n劲乐\n無網界流覽\n郭伯雄\n男人若妻夢交下载\n厕所盗摄meinv性感\n做雞\n教你如何延迟射精\nFUCK YOU\n艷情小說\n小电影\n亲日\n太監\n太监\n黑鲍娇\n梅德韦杰夫\n欧美尤物炮图图库\n臭西\n发$轮\n想不到的黑幕\n色狼論壇\n20岁处女偷拍/免费A。B片\n乳沟\n私%服\n法蜦功\n中华大地\n生成身份证\n Kurumi\n欧美清晰打炮能看的淫乱\n毛神经病\n没有人权\n孙中山\n妈的穴让你插\n幼妓\n色猪猪成人导航\n(現映社)女の最強は30代エロ真っ只中\n應招\n冰毒\nｈａｉ ｔｉａｎ ｙｉｎｇ ｘｉｏｎｇ ｚｈｕａｎ\n那媽\n強姦你\n毛恐怖份子\n达癞\n神風痴女生中出！【MKV/767MB】\n隐窝窝\n免费偷窥网\n妈比\n宋任穷\n街头扒衣\n非常不错的人妻轮奸\nSM女王\n姦淫未成年的小女护士\n情色\n台湾岛国\n精液射进小穴里到了高潮\njiejie的肥阴户\n鸡巴插进穴\nQ币\n欣赏特级毛片pp 点点毛片 meinv毛片 大陆毛片\n万税\n三个代婊\n窝窝客\n毛三个呆婊\n那可汀\n禽兽\nrivals靠\n干老师的穴\n奸淫\n台湾是主权国家\n操操比\n操那吗逼\n賣党求榮\n嫩嫩 小肉缝 小肉穴\n少女被操喷精图\n抽插20岁处女妹妹淫叫不断\n人學生妹妹 埼玉援交 vol。2 なな＆まい\n色魔堂电影论坛\n丁香欧美成人贴图区\n僵賊民\n教派\n本\n骚女淫频乐园\n免费淫电影下载\n姦情\n幹她\n运营者\n强奸meinv\n多維社\n毛高校暴乱\n日本女优激情电影\n四川独立\n颜射自拍\n嫩女~嫩屄~插的红肿喷浆\n誘惑視頻\n私*服\nfuck天皇陛下\n熟婦\n讲法\n郭 罗 基\n阴茎在我阴道里急速抽送\n多位东热女优参演\n幹七八\n免费淫电影\n鸡吧勃起插入阴道\n他母亲\n 七海りあ\n插的小穴都流水了\n动乱\n回民吃猪肉\n套牌\n激情人体18禁\nliu xing xue yuan\n狂插meimei的小穴\nfuck吴官正\n發~綸\n幹妳\nｐｉａｏ ｌｉｕ ｈｕａｎ ｊｉｎｇ\nｋｋ粉\n毛上妳\n幼女性交\n謝長廷\n挺动阿姨雪白肥臂\n册那娘比\n强奸39岁处女全过程/淫妹阴毛小穴\n幼女\n日X妈\n大奶騷女\n向忠发\n中公網\n愛幼閣\n曾庆紅\n欧美淫色贴图\nMUGEN EX Vol。 8 美脚痴女 红音まい\n我和meimei肏穴\n性福联盟 淫宝贝导航\n私/服\n日X 妈\n法**論\n扒光\n操布什\n那妈\nAlod\n私-服\n苏兆征\n身障\nsystem\n六月联盟\n色情电影 免费电影 成人影院\n淫水翻騰\n强奸美女\nｘｉａｎ ｊｉｎｇ ｃｈｕａｎ ｓｈｕｏ\n干妳\n航海世纪\n六四點擊\na 片成人免费激情小电影\n宁王府娱乐\n真三国无双OL\n陈慕华\n真孔雀王\nyeeplay\n操\n东突\n毛鸟GM\n淫穴兽交\n胡锦涛靠\n蔡和森\n痴乳\n干娘\n风骚护士\n东方互通\n少妇 淫穴\n4。25\n阴茎在子宫喷射黏液\n阴部特写\n毛鸟Gm\n高丽樸\n我也要泄了┅┅喔\n大 紀 元\n人权\n免费淫电影吴宗宪搞笑语录(27禁看)\n通用娼婦\npeachall\n神婆\n成人聊天室\nonlineworms\n成人漫畫\n国军\n一夜情的免费电影\n15768\n欧美激情淫电影\n日她\n红嫩的小肉穴\n黄金圣水\n日本激情淫穴\nTokyo Hot n0248 菊池奈津美 本物RQ体内汁注入22连发六月联\n勁乐\n江戲子\n做爱图片\n蒋彦勇上书\n我奸\n狂插荡护士\nAdministrator\n美神ルナ Runa Mikami\n毛傻逼\n瘸腿幫\n李铁映\n操淫荡少妇穴 操淫荡meimei穴 操淫荡jiejie穴\n騷浪人妻\n登 辉\n毛军长发威\n水利部\n法轮大法靠\n婷婷淫色\n臀部\n丰唇艳姬\n嗎的\nBT色工厂\n川岛芳子\n淫叫\n布朗\n中年美婦\n本站10007多部激情电影全部免费试看\n阴道好紧啊\n外~~挂\n淫民堂\n道县公安\n孙逸仙\nmerde\nCUP爆乳家庭教师\nfuck商务部\n成人漫画\n小姨子淫色导航\nMM激情视频聊天\n台湾情色香港顶级淫片\n毛妹妹淫色\n游戏管理者\n少妇房中自淫，和两勇男玩7P，操得直叫爽 操日本淫女穴\n22park\n抢粮记\n午夜激情宽频影院\n插屁眼\n反革命\n干她\n我和jiejie\n小骚屄儿妹妹\n极品做爱套图\n强奸你\n东社\n钢管舞\n江湖使者\n僕街\n支持台湾\n婷婷激情小说\n毛生春袋\n爷爷顶到嫂子的花心\n天畅\n新疆分裂\nKefu\n搖头丸\n战场\n打倒\n法**论\n毛胡紧涛\n強姦媽媽…肉缝\n乳溝\n６１０办公室\n苯比\n发$$抡\n大奶头\n祖宗\n中公网\n韩杼滨\n登 輝\n毛私—服\nRape\n亚洲最大激情电影\n6-4tianwang\n小嫩穴流血了 流水了\neight仙靠\n刘文胜\n又长粗的肉棒\nｉ3ｈｕｎ\n好嫩的阴户\n挺入阴道\n董贱华\n…你的鸡巴 用力干\n亚洲激情淫少妇贴图\n回回\n李作鹏\n瘸腿帮\n徐水良\n日\n自由门\n阴茎挺进狭窄的阴户\n日本激情视频聊天室\n好色meimei电影做爱论坛\n歡迎G8情色電影院\n妹妹小穴好紧\n嚮導\n屍體\nwow520\n张 万 年\n網星艾尼克斯\n淫穴电影网\n八九年那個春夏之交\nwangce\n武林群侠传\n恼杀美巨乳美女~吉川萌\n肉棒沾满了唾液\n成人 激情 性爱 午夜 高潮 情色\nMUGEN EX Vol。4 吉川萌\n抑制剂\n舆论反制\n亞洲自然超可愛美巨乳\n幼图\n毛强奸犯\n77bbb\n極景\n男男激情影院\n外~~掛\n淫欲世家\n…好棒啊……好舒服\n何勇\n毛广电局\n幼圖\n小屄儿里又暖又紧\n亚洲激情淫色文章\nｗａｉｇｕａ\n日本小泉\n观世音\n奸污\n江流氓\nmeimei的嫩屄\nａｓｇａｒｄ\njiejie被轮奸\n18dy免费观看\n免费放淫电影**日本高校女生性交\n好紧的屄啊\n祁建\n18岁少女处夜激情性爱电影下载中心\n美神ルナ\n毛系统讯息\nKamikaze Girls Vol。 30~中野美奈\n阴道夹的好紧\n发*论\n嫩穴把肉棒包得紧紧\n太王四神记\n航海世紀\nfreedom\n午夜激情美穴\nK姐\n激情淫乱图片穴欧美男人淫色贴图\n麦司卡林\n铁甲争雄\n舔脚\n馬場大亨\neight仙\n电影\n┻┼\n地下刊物\n鬼轮奸\n操赤匪\n民运\n插入meimei穴中\n免费电影\n文殊\n联合国大会\n淫色释放贴图\n性交吞精\n肉棒插入湿淋淋的肉洞\n三民主義\n田纪云\n麦角酸二乙基酰胺\n淫奸处女论坛\n8？9靠\n日本成人激情电影\nfuck湿透的内裤\n欧美激情淫色下载\n发$论\n阿姨的阴道好紧啊\n星战情缘\nfuck升达毕业证\n周恩来\npangya\n赤龙剑心\n天之遊俠\nwow711\n免费bb电影\nnaive\n野性家庭女教师\n抽\n母女雙飛\n舔腳\n黄色电影\n囘良玉\nsh!t\nwg9996\n針孔偷拍\n毛妈B\n我淫我色导航\n免费淫色情电影 处女第一次阴道做爱全过程(全套图片)\n私？服\n突破封锁\n伟哥\n張萬年\n操戳你\n同性恋\n少妇房中自淫，和两勇男玩3P，操得直叫爽\nａｓｓｈｏｌｅ\nTNND\n沈阳公安\n娼妓\n娼妇\n我日你\nKamikaze Girls Vol。29 : Arisa Suzufusa\n党中央\n毛嫖客\n色界\n經典炮圖\n吴 邦 国\n令人喷血的春宫108姿势(组图)20岁禁看\nha bao\n激情电影免费下载裸体meinv乳房图\n幹妳老母\n射了还说要\n镇压\n安纳咖\n成人電影\nbastard\n出售神符\n大鸡巴 干进 花心\n美女潮吹\n吴 方 城\n買財富\n黄色激情电影\n午夜激情电影\n掰开MM嫩穴\n鸡吧顶住阴道口\n戳\n日本性爱电影\n穿豹紋的meinv，很有野性美。看她咬著肉根就可看出來。\n黑毛屄\nmeinv电影\n日本人淫乱操淫荡少妇操淫荡妹妹\n性欲喘息女教師\n尼克松\n李 瑞 環\n夫妻作爱电影\nASS李弘旨\n肉棒插入肉洞\n幾巴\n光線\n妹妹张开双腿等你操　\n色网阴道特写强奸27岁处女全过程\n插淫穴的快感\n操腫\n瓦良格\n你是鴨\n賣ID\n免费激情电影\n操丰满小穴\n新手指导员\n小嘴吞吐的肉棒\n拦截器\nbatard\n东北独立\npetgirl\n肏jiejie屄\n日X 媽\n喷精\n本站的免费成人电影\n草嫣\n少妇床上欲火难耐自mo图\n作弊器\n操淫女穴贴图\n强奸36岁处女全过程/淫妹阴毛小穴\n镇压学生\n毛鸡鸡\n插\n免费淫电影网站\n劲舞世界\n小姨的嫩阴道\n裸体meimei林心如穴\n不需付费的色情电影\n淫电影//令人喷血的春宫108姿势\n弟弟的肉棒好长啊\n剛癟三\n仙境傳說\n李月月鸟\n乱伦小荡妇\n联 总\n最新一本道~超级名模系列第13弹 : 真乃瞳\n成人免费影院\n掯\n轻舟快讯\n硅谷动力\n草的你媽\n湿淋淋的阴道\nSM处女淫水图\n久游\n臭鸡巴\n掛\nPaki Paki\n绕过封锁\n午夜成人\n玩逼\nmysupersales\n魔兽世界\n保证你打飞机\n香港激情片色情工厂直播香港彩福联盟激情\n玛雅网\n發^綸\n激情黄色电影\n毛国家主席\nGamemanger\n足球经理在线\n无套内射\n捻\n毛共狗\n干干\n百家性大型黄站视频\n蜜洞\n激情3级片试看\n鸡毛信文汇\n宁王府成人\nFLG 媽批\n张小平\n针孔摄像机\n避孕\n野性家庭女教師\n法*\n免费顶级欲图禁片\n26dy淫色贴图\n同性戀\n肉棒狂插穴口 ┅好爽┅┅\n鸟你\n毛蒙独\n天安门母亲运动\nfuck石进\n干妈妈的爽\n马英九\nClient Server\n人大常委\n以及家人\n步非烟\n干保姆小穴\n我幹\nda hang hai\n双十节\n我干\n葫芦案\n本站10013多部激情电影全部免费试看\n免费淫色电影-无需注册\n淫骚穴电影\n搓揉裸露在外的那对奶子\n你是鸡\n你是鸭\n去氧麻黄碱或安非他命\n钻插\n淫奸meimei穴\n中年美妇\n李 瑞 环\n挂\n抽插32岁处女meimei淫叫不断\n淫乱潮吹\n168www\nDJMAX\n慾火\n午夜影院秘密通道_免费不需注册看电影\n毛妹妹黄站\n江豬媳\n伦功\n和护士姐姐淫穴\nmeinv淫水狂流\n鲜嫩蜜穴\n烂b\n两性激情电影\n香港激情淫色贴图\nfelch\n999日本妹\n母子淫奸乱轮现场看……扶正肉棒\n李先念\nxinguangming\n废墟\n小灵通\n吸精痴女護士\n中文搜性網\n黄色免费淫电影网\n苏秀文\n魂online\n夜色城\n老师小穴\n烂B\n他妈ㄉ王八蛋\n18岁女生主动让男生开苞全过程◥免费黄色电影\n國民黨萬歲\n……太舒服了 淫肉穴\n日本激情淫色贴图\n网络公祭\n猛烈 挺送 肉棒 激烈 撞击\n毛华建敏\n激情视频聊天\n肉棒插入骚穴\n邓颖超\n肉棒好大啊\n賣QQ\n淫电影免费在线观看\n发~纶\n奇迹世界\n联易互动\n十七大\nfuck想上你\nｃｈｉ ｌｏｎｇ ｊｉａｎ ｘｉｎ\n毛吗啡片\n操少妇嫩穴\n白漿四濺\n原味絲襪\n刘晓波\n他奶奶\n酣乐欣\n示威\n狂插儿媳妇的阴道\n李克強\n李克强\n刘士贤\n駛你老師\n妩媚挑逗\n天翼之鏈\n美女吞精\n东热美女一个比一个强\n台湾成人电影\n特别公告\n仙界传\njiejie的穴好大啊\n福摩薩\n大鸡巴 插入 肉缝\n强奸25岁处女全过程/淫妹阴毛小穴\n狂插妹妹的小穴\n阿雅娃娃\n夫妻3p\n少妇扒开阴道图-淫賤少女阴唇特写图\nfuck台湾共和国\n抽插54岁处女妹妹淫叫不断\n江则民\n熟妇\n免费dse淫乱图片\n懒教\n成人免费电影\n毛威而钢\n淫电影//教你怎样延迟射精18岁以下禁免费淫电影a片\n亞無碼\n买财富\n操成人片\n法×\n新手指導員\n淫声浪语\n剛度\n李 嵐 清\n熟女\n又大又嫩的阴户\n江戏子\n干他妈\n久游网\n淫色少妇狗小说大全\n性\n淫图淫片全部免费\nTOM在线\n剑侠情缘II\nDROP\n┅大力插┅大鸡巴\nMM的小嫩穴\nBASTARD\n台湾狗\nParade Vol。57：巨乳絞りボンデージ\n用你的阴茎插我的阴道\n鴉片\nfuck乡巴佬\n咪咪色界\nservice\n社会院\n最淫大陆成人贴图电影区 处女一丝不挂自慰图片\n台湾身材一流美女楼梯性爱\n插你奶奶\n台湾独\n淫民激情文学图库下载淫民激情文学图库专题\n毛驶你母\n８８科技\n流氓\n淫奸少妇穴\n顶级乱伦高潮淫水\n普京\n毛靠\n免费妹妹黄色淫乱\n风水\n女教师禽兽轮奸陷阱\n亚洲有码\nＸ到喷屎尿\n毛賊東\n赵 海 青\n李 淑 嫻\n六月聯盟\n動亂\n中獎\n天翼之链\nucdao\na4y\na4u\n家庭教师は女子校生 藤沢美歩\n大史纪\n操陈良宇\n海天英雄傳\nbxqt\n鹿城娱乐成人俱乐部\n射　精\n朱容雞\nｋｕａｉｋ\n刚比样子\n处女淫水图台湾女人裸\nfalun\n毛胡XX\n刘山青\n兔妹妹贴图区\n動乱\n大肉棒\n愛液橫流\n最新成人激情电影网站\n女干三亏\ndi guo chuan qi\n撅起大白腚\nfuck社科院\n操肿\n濫貨\n學生妹\n强奸32岁处女全过程/淫妹阴毛小穴\n大阳具从后面插入她的小穴\n賀國強\n亲民党\n龟头喷射出浓浓的精液\n淫亂工作\n陆委会\n免费淫色妹妹电影网\n毛发改委\n淫圖\nKeFu\n苏振华\n老b\n(Waap)女教師 盗撮 - 志保\n荡女\n萨达姆\n操婊子\n万王之王\nearthciv\n傻逼\n毛傻B\n亚洲激情淫穴\n淫图\n夠酷GOQO\n大鸡吧在小穴内壁摩擦\n老B\n要射了\n肉棒已经整根插入穴中\n大众色情成人网\n網 特\nfuck双峰微颤\n操肏\n光宇華夏\n江山美人志\nボディフェティッシュ　桜子 DVDISO\ntian shi\n毛干妳老母\n∪R靠\n赵小明\n慰\n中文搜性网\n毛青天白日\n民運\n黄　菊\n毛监管局\n抽插30岁处女meimei淫叫不断\n李月月鳥\n在线淫电影 淫奸电影 黄色电影\n慾\n龟头 花心 骚屄\n毛希特勒\ndragonheart\n救災\n救灾\n午夜成人影院\n日韩淫女\n他干\n童颜巨乳\n淫女论坛\n精子射到jiejie的穴里\n鸡巴小穴\n手伸到两腿间\n法埨功\n私\\服\n荡妹\n插白洁小穴\n鸡八\n露點\n免费成人十八影院\n消滅共產黨\n黄永胜\n肉缝　嫩嫩\n整个龟头插入小穴\n干尼\n淫女贴图区\n濕透的內褲\nｌｉｎｅａｇｅ2\n男女激情淫影片图片\n枪支弹药\n肉棒插入 嫩穴\n荡妇\n风雨神州\n麦克阿瑟\n银行帳號\n礼品机\n把大龟头对准她的阴道口\n妹妹的阴蒂\n馬凱\n妹妹骚图\n毛迷药\n淫妞\n大鸡巴好粗…\n免注册淫电影在线观看\n賤人\n萬淫堂\n萬王之王\n你的小穴好紧\n及びタイトル ？(Calen)セーラー服とマシンガン 柚木ティナ\nｗｕ ｊｉｎ ｄｅ ｒｅｎ ｗｕ\n粉穴\n全免激情聊天室\n毛食屎\n父女激情淫色文章\n戳你\n肮脏美学\n揉搓淫骚穴\n22dy淫色贴图\n淫妻\n他奶娘的\n姦染\n天纵\nminhui\n鳥gm\n淫妹\nray-city\n下载免费淫色强奸电影\n宇明网\n卫星接收器\n媚藥少年\n郑义\n毛卖软件\n遊戲新斷線\n淫间道\n爽mm\n黄色免费淫电影母子淫奸电影\n李鹏\n淫姐\n陈总统\n群交亂舞\n内射美妇\n摸阴蒂\n去他妈\n抽叉jiejie\n亚情\n淫电影//少女被强奸到高潮时偷拍小A片\n布雷爾\n淫娃\n18禁-空中小姐的淫荡性生活(无码)。\n欧美淫图片 咪咪爱论坛\n智冠\n淫乱金发美女\n他妈\n蒙古獨立\n新郑和\n官逼民反\n李 淑 娴\n内测帐号\n做鸡\nmeinv含双棍玩内射\n騷婦露逼\n發^論\n嫩穴里他的鸡巴\n蔓ぺ\nWaigua\n偷拍掰开阴唇做爱\n性无能\n做鸭\n天縱\nfuck私\\服\n龌龊\n我淫我色网站\nmeimei的阴道好紧啊\n遥想当年春衫薄\n毛共产党\n社保基金会\n天堂2\n抽插49岁处女妹妹淫叫不断\n輪子功\n政治避难\n买春\n法*輪*功\n消息公告\n民進黨\nstreet of EROS 07 小泉リカ\nwebmaster\n俄罗斯\n水去车仑\n毛天安门\nMugen Vol。11 Ai Hazawa羽沢愛！【AVI/ 699MB】\n淫婦\n婊子\n肉棒猛烈拔插\n荡母淫儿\n精液射入了她的体内\n色图专区\nfofg\nｍｅｓ ｃｏｕｉｌｌｅｓ\n女激情淫影片下载\n107bb成人小电影\n谭政\n亞情\n吕京花\n淫妹妹贴图\n身份证生成\n101bb成人小电影\n觀世音\n滚\n滛\n鷄巴\n手记注册日韩5002部激情电影\n干死你\n渡口\n毛回良玉\n又黑又粗长的鸡巴\nf r e e c h i n a\n鸡巴插入拔出 真舒服\n瘪三\n欧美激情淫色\n电影淫电影艳情电影\n毛妈祖\n最新黄站\n龟头抽插很紧的处女阴道\nfuck卖．国\n龟头 插进 阴户\n她马的\n悔过书\nshyt\n日他娘\n少妇床上欲火难耐自摸图\n毛法轮\n性感诱惑\n毛死全家\n张潮阳\n阴茎在我的肉洞中抽送着\n核武器\n写真\n性虐待\n成人贴图区论坛\nWEB战牌\nfuck新生网\n发^轮\n性免費電影\n激情meinv贴图九九情色\n教你怎样延迟射精23岁以下禁免费淫电影a片\n成人网址导航\nPainful Groins Getting Pounded : Akira Shiratori 白鸟あきら\nmo你\n中华人民正邪\n抽Ｂ淫穴\n∪R\nｘｉｎ ｊｕｅ ｄａｉ ｓｈｕａｎｇ ｊｉａｏ\n伍凡\n(Tokyo Neko) Tokyo Neko GN Vol。1 Ibuki XVID+Ogg\n淫人情色导航\nsm調教\n毛社保基金会\n妹妹的阴唇好大啊\n大鸡巴 大肉棒狂插嫩穴\n中央军委\n释迦牟尼\n大阴唇jiejie\n柯賜海\n干Gm\n煞笔靠\n常委\n邱会作\nfuck他爹\n天天免费成人\n做鴨\n水去车侖\n操蛋\n穴门meinv图库\n現金\n管里员\n干GM\n发~论\n鸡叭\n阴唇阴道大图三级\n連戰\n性视频聊天图幼女日屄视频脱衣聊天室明星\n易当\njiejie的阴唇好大啊\n幾八\n淫乱图片\n所盗摄护士姐姐性感美腿图 肉丝美腿艳情淫欲\n淫色BT娱乐导航\n干勒\n徐和柴学友\n肉缝 蜜穴 肉棒\n流淫\n混亂冒險\n毛人体炸弹\n刘杰\n半夜我把龟头插进meimei的深处\n江西田园置业集团\n豆豆秀\n手记注册日韩5014部激情电影\n淘宝\n干小姨子小穴\n性趣\n亚洲成人情色网站\n你妈的\nEVENT\n国外A片电影\n胡耀帮\n淫奴\n法陯功\n淫女\n淫奸\n钱 其 琛\n龟头插进\n少妇被强奸密穴流水\n肉棒插入美女的肉缝\nhellgate:london\n抽插42岁处女meimei淫叫不断\n白嫩騷婦\njiangshan\n淫妇\n大鸡吧操小逼\n傳銷\n王润生\n免费看淫电影\n淫奸熟睡小姨子\nMUGEN EX Vol。7 濱崎(MX-07)\n毛刘淇\n邹家华\n湿透的内裤\n乳房 小穴 鸡巴\n干入\n肉棒插进阴道里\n濕了還說要\n成人色情乱伦影院\n激情淫色合成贴图\n一本道~松下美里\n插入穴 爽\nredmoon\n毛鮑\n┅┅啊┅┅好舒服啊\nｋｊｋｉｎｇ\nmeimei社区淫色\nRed Hot Jam 9 ミュウの新人调教\n李贵鲜\n妳妈的\n港澳成人午夜激情\n免费淫 电影a片\n桃色\n傅申奇\n毛李岚清\n毛傅鹏\n插入媽媽…肉缝\n中華人民共和國\ndi qiu wen ming\n八老\n膏藥旗\n她的小屄不紧\n中華人民共和国\n鸡吧\n桃色星期五\nputain\n群交乱舞\nfuck我日\n邓发\n毛摇头丸\nｂａｉｃｈｉ\n漢娃娃\nBLOW\n遺囑\n騷B賤\n外　挂\nMix Studio Vol。 7女医\n妹妹的小淫穴\n在线免费成人电影\nmoyu\n淫屄\n卡斯特罗\n达赖喇嘛\n草的你妈\n李立三\nd7se\n厕所盗摄\n女教師赤版\n人弹\n27军军长\ngong fu xiao zi\n高丽棒子\n肉棒在jiejie的阴道里\n遗忘传说\n愛滋\n天聯世紀\nKOKESHI COWGIRL Vol。24 炎的10番勝負2 前編 : 工藤Hatsumi\n美国激情淫色网站\n幼女淫图\n法綸\n成熟性感大美女\ncao\n根正苗红\n诱色uu\n好爽\n大阳具\nｓｈｅｎ ｚｈｉ ｌｉｎｇ ｙｕ\n彭珮雲\n黄色淫乱电影\n江豬\n干到\n大乳頭\nｘｉａｎ ｊｉｅ ｚｈｕａｎ\n本站10008多部激情电影全部免费试看三天\n谋杀\n津瑞\n６。４事件\n国家民委\n【亚洲/无码】极品女优-姬野愛！【RMVB/488MB】\n日韩免费成人电影\nmao xian dao\n猛男狂干meinv\n值勤帐号\n老骚比\n毛江核心\n女教师淫乱课堂\n懶叫\n造愛\nurban-rivals靠\n日批\n大話西遊\ntie jia zheng xiong\n护士嫩穴扒开嫩穴嫩穴\n遺嘱\n毛疆独\n天安們母親運動\n毛旅游局\n桃色　激情电影\n5173\n强奸26岁处女全过程/*妹阴毛小穴\n銀行账號\ngoqo\n巨乳素人\n色色\n毛淫毛\n幹你\n陳總統\npp点点激情片播放\n他娘\n毛骚\n妹妹騷圖\n洱\n邓小平\n永恒之塔\n三国豪侠传\n%共产党烂逼\n妹妹的小屄好痒\n乳暈\nfuck我操\n僵贼\n姐姐的小屄\n布希\n彩票机\n激情貼圖\n智傲\n明星裸体写真图 meinv网络视讯三点\n淫告白\n插穴止痒\n梦幻情人射了10次的强片\n支持达賴\n刘延东\n毛蒙尘药\n发^^沦\n拔出来\n绿色數碼\n熟女玩3P内射来满足欲望\nfosaon\n發~~侖\n發@輪\n精液射进妈妈穴里面\n乳晕\n鸡巴受不了了┅┅要泄\n完全免费在线淫电影？\n插　小姐\nteen sexy\n楊振\n毛蒙古达子\n毛淫水\n义母\n他媽\n禁书\n最新 Queen8 无修正素人\n多维\n米田共\n锦天\n少林传奇\n乳波臀浪\n吃雞巴\n凸点装\n大力抽送\n极品黑丝\n绿色數码\n安非他命\n高莺莺\n毛港澳办\n猛烈的挺送、肉体激烈的撞击\n支持达赖\n习仲勋\n脸盘美身材好就是屄丑点\n39仓Ku\nroiworld\n插穴止癢\n淫\n淪\n肏妹妹的小骚屄\n强*十八岁meinv护士看完保证你打飞机免费淫电影a片\n在线淫色电影\n天安门英雄纪念碑\n儿子添着妈妈肥大湿润阴唇\n免费电影 meinv电影\n绿色数碼\n她的阴道好紧\n日本激情淫色贴图区\n午夜场\n暴亂\n图片不爽你砍我\nfuck邪教\n白嫩丰腴的大腿掰开成大字形，肥嫩的小骚屄儿\n无码长片\n亚洲成人网 鹿城最新娱乐网\nｔｉａｎ ｓｈａｎｇ ｂｅｉ\n添 穴 两腿间\n和阿姨乱伦\n龙虎靠\n手记注册日韩5035部激情电影\n抽插45岁处女meimei淫叫不断\n發^^倫\n現貨\n淫*女电影片段试看淫mm\n365激情淫色贴图\ncocksucker cocksucker\n情色导航 成人电影\n毛老逼\n激情淫色文章网站\n日本AV女優\n毛法*轮*功\n3p炮图\n性感动感meinv日本幼少女性感写真\n鳥Gm\n搜神记\n60xp。com 电影免费\n歡樂數碼\n黄色情挑艳情小说网骚妹妹成人论坛\n淫語連連\n苏东解体\n日你九城\n色男淫女\n顏射自拍\n暴乱\n润星\n台湾身材一流meinv楼梯性爱\n暴乳\n文物局\n鳥GM\ntnnd\n痴女吸精\n免费操小姨子 操小姨 操小姐图片 操小姐小说 操小姐自拍 按摩洗浴操小姐 操外国小姐b\nzhenshanren\n真封神\n时代论坛\n海之樂章\n小日本\nｓｈｅｎ ｍｅ ｓｈｅｎ ｍｅ ｄａ ｍａｏ ｘｉａｎ\n毛上你\n仿真假钞\n亚洲**联盟激情视频 免费下载做爱图片\n她爸爸\n新华通论坛\n保證很像學生妹\n好大的阴唇\n应召\n絲襪淫婦\n鸡吧猛的抽插嫩穴\npopoming\n操日风骚少妇穴\n黎安友\n淫荡护士穴\nfuck小日本\n毛我日\n綸功\n马时敏\nｈｕａ ｈｕａ ｍａｎ\n狂插儿媳\n三级小电影\n性爱电影网\n公车淫奸女学生\n1989天安门\n激情色情\n台湾女人裸体贴图香港成人电影网\n亞洲色\n温州鹿城成人网\n使劲插我啊\n东北xx网\n蕾丝内裤被龟头顶穿\n发@@抡\n艳照門\n则民\n射了還說要\n淫女嫩穴图\nDuty Vol 50背徳の近亲相奸\n劳动教养所\n人大\n潘国平\n朱可夫元帅\n军民血腥冲突\n免费淫奸女Ａ片段试看 20岁女大学生被强\n免费观看美女裸体贴图\n輪奸內射\n西遊伏魔錄\n毛淫欲\nmeinv极品穴\n毛鸡\n舔西\n绿色数码\n毛鸠\npp242淫色导航\na片/A片下载/毛片/毛片下载\n毛江Core\nsofben sofben\n淫水也流湿了我干进她穴内的龟头\n干bi\n暴操淫荡小meimei\n管里\n民阵\n卖财富\nrunstar\n逼孕\n麦角酸二乙基醯胺\n死\n毛天皇陛下\n自由之門\n日本娱乐性免费电影在线\n又肥又大的穴\n刚比\n中國分裂\nSYSTEM\n电影免费试看\n仙侣奇缘\n大学 高清女厕盗拍 vol。01\nK房\n视频自拍\n独占动画~潮崎~高校女教师陵辱1+2\n免费聊天室\n刚瘪三\n騷女叫春\n成人社区\n党主席\n西藏问題\n成人社區\n中國恐怖組織\nｆｅｉ ｃｈａｎｇ ｓａｎ ｇｕｏ\n手记注册日韩5024部激情电影\nsperm\n三国世纪\n毛鲍\nchinaliberal\n阴精喷向龟头\n少妇房中自淫，和两勇男玩16P，操得直叫爽 操日本淫女穴\nsm援交\n幹x娘\n辉煌\n下注\nｍｏ ｘｉａｎｇ\n强奸45岁处女全过程/淫妹阴毛小穴\n女巨乳女教師\n翘臀嫩逼\n新闻出版\n錯B\n捅进她的阴道里\nbitch\n抽插38岁处女meimei淫叫不断\n艳照门\n巡查\n吸精小少女\n89-64cdjp\n干gM\n淫秽男女激情淫乱\nhanbitsoft\n西藏问题\n18岁处女床上开苞\nmore\n白虎小穴\nbukakke\n欢乐城美国在线 咪咪爱论坛\n香港马会\n嫩穴狂插直到痉挛\n李 岚 清\n人大代表大会\n毛日本鬼子\n午夜激情情色网\n肉棒进入肉洞\n自由之门\nｃａｏｂｉ\nfuck王震\n肉棍干騷婦\n啊 啊 啊 好哥哥\n日本meinv图库\n毛强卫\n淫奸少妇电影\n墨索里尼\n免费免注册在线看电影\n交媾\n東北獨立\n法纶\n肉棒的抽插 ……啊\n下流\n色网阴道特写强奸29岁处女全过程\n礼品\n发^论\n品色永远免费堂pin6 meinv发春激情3级片试看\n兔妹妹色色贴图区\n叶剑英\n乱伦+都市+制服\nq币\n毛懂文华\n二五互联\n发@轮\n姦汙\n高丽棒\n國家機密\n毛肉缝\nyuanming\n干gm\n紅月\n淫水大量流出\nmetin\n由喜贵\n谢长廷\n三去車侖工力\n大鸡巴插进meimei的小穴\n二五互聯\n娇妻超市被奸，淫荡妈妈之办公室助理\n江贼\n鸡吧插到嫩穴\n久之遊\n美女激情性交射精电影\n买春堂\n奴隷女教師－女尻狩り　吉野あゆみ\n淫蕩少婦好色老師\nｇａｔｅ ｔｏ ｈｅａｖｅｎｓ\n册老\n麦角酸\n他祖宗\nfuck新光明\n操蠢猪\n88bb成人小电影\nfuck铁道部\n欢乐城\n亮屄\n大唐风云\n宋祖英\n傅全有\n神之领域\n龟头插入淫荡少妇\n混沌靠\n巧淫奸戏\n……媽的騷穴癢死了\n發$輪\n幾叭\n蒙哥马利\n大话春秋\n一股淫液从肉洞喷出\n淫荡少妇穴 操淫荡少妇\n曾培炎\nkefu\n花心大型色站\nmeinv偷拍成人小说激情网\n断水\n干啦\n冊老\n官方管理者\nxian jing chuan shuo\n免费注册激情视频聊天\ntransvestite\n性情中人\n刘凯中\n狂插空姐\n操冰毒\n妹妹的大阴唇好嫩啊\n毛我操\n日本AV女优\n抽插37岁处女meimei淫叫不断\n湿了\n毛吴　仪\n伤亡\n售號\n朱容鸡\n日韩淫乱meinv图\n酥胸诱惑\nｌｉａｎ ａｉ ｈｅ ｚｉ\n法**輪\n含着龟头\n分裂祖國\nmeng huan zhi xing\nTora-Tora-Tora Vol。22 : 冬月ひな\nJi女\n淫水和精液浸濡的小穴里\n秘密通道\nfuck驶你老母\n烂比\n新视野工作室\n狂操小姐\n分裂祖国\n陈随便\n鲍鱼贴图妖色成年网电影\n大哥 等等 我尿完后 再给你干肉穴\n操b\n学生妹同男友夜晚做爱并轮插\nhypermart。net\n狂插妹妹穴 淫水流\n巧淫奸戲\nfuck水利部\n何德普\n成人午夜激情\n操B\n海盗王ONLINE\nｙｉｎｇ ｘｉｏｎｇ ｗａｎｇ ｚｕｏ\n遗囑\n毛抢劫\n大東亞共榮\n*李*洪*志*\n辛灏年\n保证你打飞机免费电影网站\nkaixuan\n毛新唐人\n發*輪\nAV OPEN)(TOD129)近亲3姐妹狂乱夜\n尸體\n别他妈的装\nfuck私—服\n虎虎虎~变态心Tora Tora Gold Vol。17 : Mako Takeda\n一贯道\nfuck宋祖英\n台灣民國\n肉棒插入阴户\nJ-Girl 双穴~水原 小笠原崎\n妈的\n值勤账号\n屎你娘\n试看片\n妳她媽的\n出售手枪\n喵的\n365免费情色网欢欢成人娱乐\nfuck天安门档案\n逼奸\n鸡巴磨得舒服\n遗嘱\n白嫩美女穴\nfuck私-服\n灌满精液\n桑国卫\n操妳全家\n玖玖金币\nyour mom your mom\ncreaders\n搞大肚婆爽+口交泻水淫图\n温馨提示\n张 宏 堡\n斷水\n人妻\nshit\n我和姐姐肏穴\n如果你未满21周岁请自觉离开。\n威胁论\n方毅\n肉棒插入\n法**轮\n舆论\n斯大林\nAids\nｌｉｎｇ ｙｏｕ ｊｉ\n插的妹妹流淫水\n瓜娃子\njing ling\n賣月卡\n刘淇\n你他馬的\n发*轮\n鸡奸\n王俊博\n鸡女\n乳房\n僵贼民\n公务员调资\n制服美少女 鮎川なお\n新聞管制\nfuck私/服\n毛他马的\n阴壁的嫩肉夹得我非常舒服\n淫店\n黄色淫电影/大幅的陰毛覆蓋著她的迷人洞\nTokyo Hot n0232 : 真宫梨沙子\n西哈努克\n莎士比亚\n台灣民国\n硕大的龟头\n黄  菊\n毛吗啡碱\n操你爷爷\nrenmingbao\nmeimei的屄好紧啊\n分裂\n日本\n欢城互动\n57xp。com 电影免费\n消灭共产党\n个人资料\n王 秀 丽\nUPDATE\n分裂中國\n调教\n天安門檔案\n日本顶级淫女\n傅鹏\n68wow\n无产阶级\n贾廷安\nbt免费淫电影下载\n毛交通部\n高丽朴\n偷操嫂嫂\nmm要我使劲插它的穴\n鲜红小嫩屄被群奸真可惜\nｄｕ ｓｈｉ 2046\n妈b\n喬石\nLateinos\n严隽琪\n淫穴 大鸡巴\n狂插我的小嫩穴\n街上钓个漂亮马子回家玩\nmeinv主持25小时全天激情热舞\n輿論鉗制\n蒋正华\n遭警察殴打\n本拉丹\n房事\n妈B\n美女性交极品大片\n红肿的小穴摩弄\n跨下呻吟\n毛骚货\n插小穴\n大腿\n啊…我的亲丈夫…用他的手…插…啊…啊…进了人家的小穴\n最新一本道)超正MODEL系列 仲里愛\n精液\n红 灯 区\n希望online\n天然むすめ~08-15 素人むすめたち\n拍卖官\n新浪\nｑｑ堂\npeacehall\n肉棒……插死浪穴了\n快操我快操我的小B电影\n把阴户内塞得满满\n预测\n大肉棒干进小穴\n鄧朴方\n大幅的陰毛覆蓋著她的迷人洞\n教你怎样延迟射精40岁以下禁免费淫电影a片\n19dy淫色贴图\n少女露嫩穴 少妇嫩穴 美女的嫩穴\n徐建國案件\n龟头插入阴唇\nappie\n玉女心經\n罗荣桓\n精液喷射在子宫上\n又粘又湿的阴户\n弹劾\n日韩激情电影\n發@@倫\n巨乳女教師\n迷奸熟妇怎样抚摩女性的私处更刺激\n午夜激情剧场\n瞳\n黄大仙\n法**綸\n我和她\n扒开淫女的骚逼\nb毛\nObama\n發~淪\n龟头插入蜜穴\n共荣圈\n肉穴 大鸡巴 ……骚穴\n发$纶\n毛民运\n恥辱醫護下载\n快乐AV\n邮政局\n我和小姨子\n插嫩逼\n李洪宽\n操小穴\n哪有a片 百万meinv贴图夫妻激情免费视频\n如何推翻中共\n血洗天安門\n渣波波\n李兆焯\n公安部\n表姐含我小鸡鸡\n中蕐\n情色淫电影\nherogame\n騷乳\nAIDS\n教你怎样延迟射精27岁以下禁免费淫电影a片\n妹妹┅┅爽快┅┅死了\n毛毛泽东\n神泣\nｄｉｃｋ\n戴海静\nASS\n神泪\n打倒中华人民共和国\n视频\n枪决现场\n刀剑\n成人禁书\n插入穴爽死了\n保钓\nfuck食捻屎\n骯髒美學\n釣魚島\n干……干我……用力地干我……\n毛西藏天葬\n免费色站\n毛干你\n开天\n手机复制\n毛兴奋剂\nSHIT\n89六四\n淫情\n金尧如\n雪腿玉胯\n(Nadeshiko)夢の超高級癒し系Wおでかけソープ嬢 完全版\n干嫂子\n法溣功\nNEWSPACE\n狂插秘书穴\nKamikaze Girls Vol。23 : Haruka Kaede 枫はるか\n可愛均整清純派meinv可愛均整清純派meinv\n韩天石\n吸吮套弄了起来\n奥美电子\n眯\n免费台湾18 电影\n东方资通\ngan你\n騷小浪女\n無碼電影\n我和老板娘\n淫穴更紧紧将我的肉棒吸住\nfuck他妈\n男女激情图片\n國家安全\n浴室亂倫\n虎门靠\nmegaone。com\ncronous\n叼你媽\n毛狗卵\n牛鬼蛇神\n冊那娘餓比\n儂著岡巒\n睾\n我要性交\n将肉棒插入漂亮的老师小淫嘴抽插数十次\n群奸轮射\n全球第一淫图城\nsan guo hao xia zhuan\n夜夜色大型网站\n龍陽\n毛靠你妈\nbing feng chuan qi\nfuck我日你\n洗腦班\nfuck吴邦国\nｇｒｏｏｖｅ ｐａｒｔｙ\n贾庆林\n娇嫩的小阴核\nmo fa fei qiu\n福呵定\n毛肉穴\n穆斯林\n虎騎\n疯狂的抽插\n敬国神社\nｋｏｎｇ ｑｕｅ ｗａｎｇ\nｚｈｅｎ ｋｏｎｇ ｑｕｅ ｗａｎｇ\n干死GM\n徐炳慧\n肉棍 嫩穴 肉洞\n操大B\n手记注册日韩5028部激情电影\nmeimei美图网\n妹妹的屄\n母子乱淫贴图\n色咪咪\n干比\n毛十五周年\n生孩子沒屁眼\n情色激情论坛\n性虐\n免费成人午夜电影\n日本淫贴图 淫妻贴图\n性虎\n阴茎被她紧紧地夹住感到肉洞急促地收缩\n居家女人的淫荡性事\n温州黄站\n抽插56岁处女妹妹淫叫不断\n华岳时事论坛\n儿子粗大的肉棒\n淫电影 强*十八岁美女护士看完保证你打飞机\nｂ\n张开的小阴唇\n李小鹏\n玉女心经\n毛机八\n家人一起凌辱母亲和叔母\n激情成人乐园\nxia ke tian xia\nrape\n盛宣鳴\n黄色淫穴电影\n乘骑按摩\nｘｉｎ ｚｈｅｎｇ ｈｅ\n壮汉给纯情处女开苞见血\n應召\n免费淫色情电影 淫电影//少女被强奸到高潮时偷拍小A片\n台湾18成人网站\n本土无码\n賣B發論公\n司徒华\napesoft\n温总理\n迷人阴部护士meimei也疯狂\n情獸\n智能Ｈ３\n骚妹妹成人导航\n作愛\n古龙\n抽插28岁处女妹妹淫叫不断\n神汉\n美丽素女の性游戏\n色网阴道特写强奸35岁处女全过程\n新西遊記\n西藏國\n回民人吃豬肉\n干死CS\n反攻大陆\n证监会\n西藏国\n杜智富\n毛塞你爸\n毛气象局\n反攻大陸\n又大又紧的阴户\n激情成人聊天室\n秘书自慰被偷拍\nC N\n金三角\n一夜欢\n妈妈的\n强卫\n毛邪教\n胡志明\n狂插小嫩穴\n咪咪爱大型黄站\n东热meinv一个比一个强\n迷人小穴好紧好湿\n蝉童\n免费台湾激情片\nTrickster\n激情淫色贴图网\n发*纶\n18歲少女\n情色电影注册观看\n小穴夹的我好紧\n撒切尔\nhua xia\n江泽慧\n性爱城\n騰仁\n空中小姐的淫荡性生活\n一夜歡\n干死\n以茎至洞\n亲 日\n品色永远免费堂pin6 美女发春激情3级片试看\n王太华\n磨蹭阴道阴毛\n妹的穴\n两会报道\n毛工作人员\n你妈逼\nfuck斯大林\n淫色bb贴图\n無碼彩圖\n於幼軍\n内挂\n精液射进她的小洞\n慶 紅\n日本插穴\nhongzhi\n懒8\n淫电影//富豪们的疯狂做/爱方式\n新党\n免费淫奸女Ａ片段试看 母子淫奸乱轮现场看……\n淫荡少妇激情自拍 成人激情\n免费成人两性电影\n抽插大淫穴\n邹德威\n飘流幻境\n语句\n大FA弟子\n小淫穴\n淫奸秘书电影\n剑侠情缘OnlineII\nSKY宫泽Keito 超美系模特儿初无修正引退纪念作\n毛反革命\n龟头插入少妇阴道\n手记注册日韩5031部激情电影\n黃孟複\n盛宣鸣\n疯狂姐姐淫穴\nwg8800\n上访\n壓迫\n大B\n王乐泉\ntta2\n运營组\n人民代表大會\n刘军\n愛色cc\n博白县\n抽插35岁处女meimei淫叫不断\n死亡人數\n李鐵映\n鹽酸二乙酰吗啡\n毛淫叫\n丝袜\nconnard\nｐｏｐｋａｒｔ\n天安门事件1989\n处女小穴\n成人爽电影网站\nMM的诱人双峰\n9游\n超强护士团\n东方传说online\n他爹\n政府\n怎样延迟射精22岁以下禁\n代炼\n午夜影院\n十八dy毛电影 BTA片 免费激爽电影\n美穴張開貼圖區\n毛南蛮子\n两性小说\n尖閣列島\n如果你未满28周岁请自觉离开。\n精液喷入穴\n项英\n林心如合成淫照图片林心如淫照图片\n脫內褲\n肉棒 小穴里抽送\n丝袜高跟\n淫女炮圖\n金泽辰\n畜\n穆罕默德\n我跟meimei肏穴\n本站10000多部激情电影全部免费试看三天\n日本meinv吃精图\n秋本純菜\n操日本淫女\nhelbreath\n粉红嫩穴 鲍鱼嫩穴=3\n粉红嫩穴 鲍鱼嫩穴=1\n抽插51岁处女meimei淫叫不断\n粉红嫩穴 鲍鱼嫩穴=2\n天晴数码\n倪志福\n万晓东\n色色婷婷\n(最新一本道)独占动画~薰~陵辱巨乳\n手伸到下体抚摸嫩穴\n八 仙靠\n儿子的肉棒好硬\n本站10000多部激情电影全部免费试看\nsm舔穴\n人民代表\n素人之中的极品漂亮妹\n小癟三\nｆｅｎｇ ｗｕ ｔｉａｎ ｊｉａｏ\n大血B\n刘刚\n成人激情淫影院\n爽女美图美搜性感图涩界成人电影网小小鸟绝色美女写真图\n用舌尖插着阴道口\n我占有了妈妈和阿姨\n情色六月天 人间仙境情色网\nxin shi qi\n熟女网爱记\n姐姐的穴夹的我好紧小说\n乱淫自拍图片\n俄国\n操比\n淫女炮图\n运營者\nTokyoHot之极品妹妹\nttee\n毛新华社\n天晴數碼\n610办公室靠\n陈伯达\n赤裸天使\n虎骑\n台聯黨\n毛龟儿子\n白嫩骚妇\n毛傅作义\n小穴的周围不断地缠绵着\n台湾18成人\n毛尉健行\n阿姨的穴┅好爽\n韩东方\n媽個老比\n毛毛主席\ntth2\n和jiejie插穴\n華建敏\n精童\n毛李长春\n香港两性激情文学区\nfuck身份生成器\n死亡人数\n插入妹妹穴中\n狂操淫荡女\n干你妈逼\n华语世界论坛\n天葬\npet520\n夫妻自拍\n色诱\n毛文化部\n秦基伟\n宋xx\n陈锡联\n山不过来\n逃亡艳旅\n11。xp。com成人影院\nmeimei小穴\n激情裸聊 性爱电影\nBT成人无码\n血洗天安门\n人妻做爱\nfuck新党\n可憐賣菜婦\n遊戲宮理員\n情色 激情 meinv 穴\nmir3\n湿了还说要\nzhengjian\n免费裸体meinv性交电影\n洗腸射尿\nｇａｎｇ ｊｉａ ｈｏｎｇ ｌｉｕ\n诱惑女教师\ndick dick\n顶到了欣虹阴道最深处\nｚｈｅｎｇ ｑｉ ｈｕａｎ ｘｉａｎｇ\n阿罗约\n口交放尿\n连战\n…好深啊…操我 插我\nda hua chun qiu\nb样\n性福源亚洲激情\n恐怖组织\n温家宝靠\n乳罩\nmeimei吸精\n操死\n色聊激情视屏聊天室\n戰國英雄\n蔣介石\n海关总署\n帶炼\n姦淫\n懒八\n午夜免费成人黄色\n妈祖\n农民伯伯乡下妹\n强奸十八岁meinv护士看完保证你打飞机免费电影网站\nU   R靠\n風豔閣\nlihongzhi\n玉蒲团 玉女心经\n王力雄\nlian ai he zi\n阿姨的阴户好大啊\njiaochuang\n淫奸少女论坛\n新金瓶梅成人片\nB毛\n香港激情夜\n香港激情淫穴\n草芝麻\n大 史 紀\n网星艾尼克斯\n淫奸meinv穴\nmeimei张开双腿等你操　\n舔逼\nwg2222\n毛妹妹贴图区\n龜頭\n装备演示\n浩天銀科\nhai zhi yue zhang\n知障\nfuck希特勒\n援交薄碼\n処女\n欣赏特级毛片pp 点点毛片 美女毛片 大陆毛片\n激情电影在线注册\n免费我该死淫水流出图\n淫女与猪乱搞图\n女良\n郭 岩 华\n龟头插入少妇\n激情视频\n懶八\n星际online\n人民大会堂\n发^^纶\n韩国成人激情影院\n金庸群侠传\n粉紅穴\n毛驶你娘\n操狗\n插穴好舒服\n好爽……顶得好深啊……\n龟頭\n駭客\n毛劳动保障部\n丁字裤翘臀\n虎门\n色成人\n可卡葉\n51vip\n纠察员\n遊戲橘子\n儂著卵拋\n黑马@美女全身神秘黑让你爽到眼框黑\n淫战群p\n毛黄色小电影\n肉棒在阴道口游荡\n你的 嫩穴 好粗\nbasterd\n肉淫器吞精\n食精\n强奸24岁处女全过程/*妹阴毛小穴\n钢甲洪流\n松花江污染\n免費成人網站\n騷包\n淫战群P\n叫小姐\n诱惑骚妹\n人妖@Lady Sonia-熟女丝袜 12\n林心如做爱网有激情\n操嫂嫂\n淫乱的女老板\n肏穴好舒服\n激情淫色电影\n獸\n老少亂倫\n成人淫穴导航\n老毛子\n你祖宗\n毛乳头\n体育练习场盗撮スポーツジム(4カメ洋式)\n插meimei图\n膣穴大陵辱 松岛\n籐原紀香Norika Fujiwara SEX\n錯那嗎逼\n獨\n精童欲女\n高级逼\n李 總 統\n新四\nmingpaonews\n成人\n淫肉诱惑\n毛烂逼\n┅插┅小穴 肉缝\n粉紅愛液\n浪女自慰诱惑你操\nnnd=\n魏新生\n援交薄码\n免费操小姨子 操小姨 操小姐图片\n狗養\n操爽\n日你祖宗\n少妇淫图电影网\n免费激情影院\n毒龙舔脚\n上山下乡\nbusterd busterd\n狗娘養的\n警匪一家\n15年前的“共和國衛士”\n激情聊天室\n大话\n厕所盗摄美女\n丁香成人社區\n穴里嫩肉把鸡巴包得紧紧\n日韩成人电影\nsex大型色站\n胡总书记\n操爛\n去氧麻黃堿或安非他命\n闹事\n梦遗\n彭冲\n本站10014多部激情电影全部免费试看三天\n抽插38岁处女妹妹淫叫不断\n激爱贴图 加西激情中文网\n女性私处锻炼法\n操\n我的穴\n税力\n裝備演示\n毛叫鸡\n好大阴户\n陈永贵\n流淫水\n操mm穴\nasshole asshole\n免费淫电影吴宗宪搞笑语录(20禁看)\n丁香成人社区\n粗大肉棒从后面插入\n插進\n妳娘的\nfree style\n插逼\n撒泡尿\n秘书的大乱交\n手记注册日韩5005部激情电影\n学生妹美女在野外偷情\n赵品潞\n坐台的\n无码H漫\nrong yao\n毛鲜族\n祭日\n吳官正\n口含住鸡巴 精液\n成人午夜激情视频\n色狼小說\n扒开小穴\n激情淫色组图\n捐赠排行\nygbh\n見到這個meinv就會有性欲\nas$hole\n总裁\n菊花洞\n黄色免费淫奸电影我和女友干爽后再她妹妹\n运營組\n裸体妹妹林心如穴\n蘚鮑\n愛液\nTransformaition 栗田Hiromi\n插进\n疯狂姐姐教弟弟小说\n轮流干\n365成人网站\n删号\n阿姨的小穴\n伊朗\nguang zhi guo du\n肉棒全部插入\n少妇房中自淫，和两勇男玩11P，操得直叫爽 操日本淫女穴\n淫meimei贴图\n戴维教\n亚洲成人无码\n台湾成人20贴图\n大肉棒……好粗\n牙齿轻咬他的龟头\n免费淫奸女Ａ片段试看 居然花钱请人教性爱姿势(组图)\n小瘪三\n午夜激情meimei\n毛寇晓伟\n手机注册看性爱电影\n肉棒插入妹妹的肉缝\n快乐西游\nfuck司法部\n百家性大型娱乐黄站\n爛\n林心如做爱我该死快乐情感乐园\n大b\n毛联合国大会\nke luo si\n剑侠情缘\n原味丝袜\nsars\n日你\n肉棒摩擦她屁眼里的嫩肉\n淫女露穴\n禦の二代目\n收缩的阴道中抽出肉棒\n爱液横流\nｊｉａｏ ｃｈｕａｎｇ\nwu hun\nsan guo qun ying zhuan\n嗎比\n江青\n成人激情淫论坛\n懒叫\n神曲\n洗脑\n完全免费在线淫电影\n发$$仑\n女人被干小穴\n淫电影网站\n欢乐时代\n翹臀嫩穴\n程凯\n機巴\n国 贼\n三P\n骆冰淫传\n政治局常委\n肉棒插肉缝\n大龟头顶紧子宫口\n法$$轮\n毛妈的\n香港顶级淫片\n韩日成人午夜激情电影\nfuck新闻出版总署\n免费注册 试看50部\n抽插23岁处女妹妹淫叫不断\n毛共青团员\n操烂\n湿身诱惑\n干爹的龟头\n华夏文摘\n日他\n狂插妹妹小穴\n抽插40岁处女妹妹淫叫不断\nShit\n自慰女學生廁所偷拍in Toilet 1\n北京xx网\n日你媽\n谢选骏\n色情电影 卡通 成人 av 少女\n非常诱惑\n6。4事件\n狂插美女老师\n淫奸meinv\n夜话紫禁城\n臺灣國\nbo ke\n中國豬\n激情bt亚洲区\n国产经典 - 4级红楼梦！【wm/113MB】\n操草的你妈\n白莲教\n妈妈的穴\n開天\n涵涵\n大 史 纪\n教你怎样延迟射精42岁以下禁免费淫电影a片\nping guo pai\n法$$輪\n燕玲論壇\n迷奸\n淫民俱乐部和美女发春激情3级片试看\n诺亚\n教徒\n央视内部晚会\n操粗制吗啡\n浪穴 肉缝 嫩穴\n强奸26岁处女全过程/淫妹阴毛小穴\n大阴唇夹着大龟头\n拍肩神药\ncunt\n肉棒 用力 肉縫\n淫書\n幹比\ntravesti\n魏京生\n墨香\n網域\n毛狗养\n王志珍\n毛耶苏\n一国两制\n江ze民\n劲舞团\n毛主席\n東突組織\n大 纪 元 新 闻 网\n222se图片\n三p\n四川独\n齷齪\n成人激情性爱电影网\n暗黑之门：伦敦\njiejie的屄\n本站10009多部激情电影全部免费试看三天\n人妻自拍\n操出售枪支\n靖國神社\n易玩网\n不注册淫电影\n高清性爱\n毛发伦功\nallsexpages\nOL美女街上遥控震动A\n日本人\npiao piao\n法车仑功\n天龙八部\n干汝\n班禅\n新华举报\n幹死\nfuck王宝森\n毛私\\服\n毛全国人大\n真善忍\nserver\nre xue ying hao\n捐贈排行\n肉棒插到妈子宫 ┅┅爽死了\n台湾男女激情淫乱小说\n激情 少女 成人论坛 乱伦 强奸\n免费淫奸女Ａ片段试看 教你怎样延迟射精18岁以下禁免费淫电影a片\n无界浏览器\n热比娅\n淫荡jiejie勾引小弟玩操屄\n國產騷貨\n瓜婆娘\n移彩联合\n开着摩托干了岳母\n红色恐怖\n神出鬼没!3个丁字裤辣妹到处袭击肉棒(中文字幕\n迫害\n毛穴贴图\n亚洲激情淫色网站\n烂\n淫荡老师\n小姨子的小嫩屄\ncreate\n亚洲激情BT\n省长的儿媳妇\n苹果日报\nｘｉａｎ ｌｖ ｑｉ ｙｕａｎ\n于天瑞\n鸡吧在肉洞里四处搅动\n中医药局\n１６大\n手记注册日韩5008部激情电影\n肉棒 插 穴\n江主席\n发@@沦\n阴小撕大\n毛台湾民国\n泡泡岛\n大鸡巴狠插小穴\n成人电影网站\n柔阴术\n勁舞世界\n淫样\n正义党论坛\n啊……我的小屄……给你……肏得好……舒服……\n强*十八岁美女护士看完保证你打飞机免费电影网站\n一黨專制\n深爱色色\na级电影\n情色 性息\n乳此豐滿\n166网址宽频影院网\n慈善會\nxinguangming鳥你\n无码丝袜\nonhave\n猛力的抽送大肉棒\nfalu\n空姐性交\n妈妈帮儿子套动着\nmeinv野兽做爱淫图\nｖｕｌｖａ\n求自己的爸爸和她干\n乳波臀娘\n色狼小说\n肥穴jiejie\n灾\n獨裁\n裸体做爱\n将阴茎尽根插入\n毛刘军\n摳穴\n龟头用力刺入阴道\n王樂泉\n周永康\n成人网站排行榜\n儿子淫奸妈妈穴\n肉棒 用力 肉缝\n淫乱护士\n六四真相\n客户服务人人员\n地 下 教 会\ntw18\n含鳥\n手记注册日韩5011部激情电影\n发$$伦\n小鱼儿与花无缺\n叼你妈\n紧致小穴抽插\nB样\n摩力游\n蓮花逼\n大肉棒插入湿湿的肉缝\n异议人士\n懶8\ndou dou\n娘比\n本站10005多部激情电影全部免费试看三天\n插阴茎\n三国演义\n毛统计局\n法侖功\n北京xx網\n奶罩\nhai tian ying xiong zhuan\n三级图片论坛禁自拍偷拍论坛日本黄色三级\nｇｕａｉ ｓｈｏｕ ｚｏｎｇ ｄｏｎｇ ｙｕａｎ\n地富反坏右\n96bb成人小电影\nmeimei的大阴户\n信仰\n迫奸\nsheng zhe wu di\n牛B靠\n色女乳房阴道\n麥司卡林\n毛李鹏*\n多維新聞\n嫩女\nｔｉａｎ ｓｈｉ ｚｈｉ ｌｉａｎ\n妹妹嫩穴\n点击这里试看meinv做爱现场\n世维会\n嫩奶\n小日本爆撮女子大生vol。01\n乔石\nｆｕｃｋ ｙｏｕ\n豪乳\n習近平\n三去仑功\n高麗朴\navatar\n成人激情淫穴影院\nhong yue\n免费在线淫电影下载\n大陆娱乐最新黄站\n龙虎豹\n嫖客\n大逼\n操那吗B\n超毛大鲍\n我和少妇\n现代艳帝传奇\nrealexterme\n操表姐\n朝倉Riho ！【RMVB/405MB】\n光宇华夏\n淫棍\nｓｈｅｎ ｊｉａ ｑｉ ｂｉｎｇ\n陈希同\n发廊性息\n百家性大型黄站网址\n湿湿的阴户不断地在大鸡巴上磨擦\n日你妈\n干进meimei的小穴\nTora Tora Gold Vol。14 星野みく\n手记注册日韩5003部激情电影\n嘻游\n俪影蝎心\n佈雷爾\n小雞雞\n網易\n猛插表姐\n64yl\n母子淫*乱轮现场看扶正肉棒\n法論\n網星\n大 史\n┅你的大肉棒┅┅好粗\n于幼军\n美マンの極意 : 相原梨花\n43lian色站大全\n台湾ye77\n肉棒里插肉洞\n亚甲二氧基甲基安非他明\n人民\nTEsT\n成人论坛\n几巴\n免费免注册在线淫电影\n人民日报\n体奸\n我淫我色\nmeinv直播做爱\nBi样\n性感肉絲\n李嵐清\n美女高潮\n儿子顶到jiejie的花心\n强奸37岁处女全过程/淫妹阴毛小穴\n黄色激情论坛\n淫女乱伦小说\n小穴被阴茎插的好痛\n少妇淫心我要看淫心\nwan wang zhi wang\n免费黄色电影\n私处\n爱巢滴水\n嫐屄\n给女人快速达高潮的秘密\n淫奸jiejie小说\n欧洲极品性交图片\n下三烂\n氵去车仑工力\n色网阴道特写强奸36岁处女全过程\n骚妹阴道\n裙内偷拍\n往事追忆录\n廁奴\n他妈的\n床上的淫荡少女\n本站10012多部激情电影全部免费试看三天\n轮奸做爱高潮\nｅｎｃｕｌｅｒ\n毛烂货\n情色导航色情排行榜大陆黄网导航免费成人电影\n少妇淫乱小说\n丰满小妹的丰褪美腿 嫩得流水的小妹妹\n306医院\nnmis\n金伯帆\na片\n超毛大鮑\n17爱网\n升达毕业证\nJ-spot PA-39\n東洋屄\n色网阴道特写强奸20岁处女全过程\n本站10009多部激情电影全部免费试看\n插在妈妈嫩嫩的小肉穴\n猛操狂射\n外汇局\n淫奸教师论坛\n卡通meinv淫色贴图\n手记注册日韩5021部激情电影\n鸡巴暴胀\n学自联\n日内瓦金融\n夜總會\n法*轮*功\n造反\n夏夏娱乐黄站 乱伦熟女网\n林心如做爱网有激情自拍\n野外性交\n身体障害者\n肉棒插护士穴骚穴\n好硬的肉棒\n肉棒插入小穴里\n狂插学生妹\n鹿城最新导航\n张朝阳\n肉棒淫水嫩穴\n叫雞\n蔣正華\nc a o\n处女第一次阴道做爱全过程\n淫乱激情电影网站\n火棒\n色窝窝网之家\n幹一家\n江則民\n通告\n东突组织\n濕穴\n亚洲情色网\n嫖娼\n大海戰II\nfreechina\n干美女小穴\n新闻封锁\n高麗棒\n18dy\n毛俞正声\n秘唇\n淫色五月天\n42lian色站大全\nfuck驶你老师\n淫樣\n下三濫\n毛建设部\n极品白虎\n毛列宁\n肉棒狂插小嫩B\n远志明\n淫色论坛\n亚洲淫色贴图365\n下载免费淫电影淫穴流\n法论\n東條英機\n肥穴自拍\n发骚\n法谪\n六四屠殺\n奶挺臀翹\nｍａｂｉｎｏｇｉ\n济南建设路\n韓 東 方\ng片\n駛你老母\nspikegirls\n日你娘\n系統\n淫色少妇做爱淫穴流\n情色 meinv -公安机关 -记者 -犯罪嫌疑人\n*法*輪*功*\n侖功\n强奸17岁处女全过程/*妹阴毛小穴\n奶挺臀翘\n肉棍干骚妇\n强奸18岁处女全过程/淫妹阴毛小穴\n淫穴内好痒\n激情午夜\n兽欲\nfuck万税\n厕所盗摄美女性感\n石进\nmeimei的阴唇好大啊\n法~~仑\nai滋\n你奶\n复兴论坛\n妈**菊花穴\n恩格斯\n鬼輪奸\nKamikaze_Girls_Vol。12\n黃　菊\nbb论坛 操bb图\n洪興\n台湾淫色网\ng点\n客戶服務\n快……快……幹我……幹……我快忍不住了……哼…\nkai xuan\n嗯┅┅嗯┅┅舒服死了……妹妹又要丢了\n你妈\n驶你老母\n舌尖舔着肉棒 阴门\n肉棒 抽插 小穴\n征途\n毛台湾独立\nWEB牌戰靠\n手机自慰网\n外交与方略\n明诺东方\n操jiejie的穴\nfuck手淫\n浪穴\nfuck新义安\n毛四人帮\n吴官正\n肉棒插得湿润了\n統\n反右题材\ncao你\ni3hun\n干您\n轮子功\n毛天皇\n法~~伦\n好紧的穴\n辦公室淫亂\nkong que wang\n免费成人激情影院\n安理会\n軍民血腥衝突\n张宏堡\n联通移动秘密通道\n夫妻电影\n滕仁\n公媳性交淫荡图片\n大鸡巴┅┅爽\n搞b\n劉雲山\n鹿城黄站图\n滕人\n中科院\n少妇房中自淫，和两勇男玩10P，操得直叫爽 操日本淫女穴\n毛贼东\n政治压迫\n下三滥\n你姥\n陈丕显\n駛你公\n賣國求榮\n綸\nf u c k\n王岐山\nfuck色情小电影\nyuanming噴你\n屆中央政治局委員\n你娘\n美女穴自拍\n移彩聯合\n吴学灿\n法$仑\ncock cock\n龟头插花心深处\nOLmeinv街上遥控震动A\n淫欲\n台湾成人骚b\n江泽民\n激情交友\nsega\n华国锋\n滕任\n干 淫穴好舒服\n两性淫乱贴图\n又紧又嫩的屄\n台湾18dy影院\nkarma\nｃａｏ ｎｉ ｍａ\n美国佬\n淫荡少妇极品穴\n騷姐姐\n美女成人\n南大自由论坛\n色情论坛\n出售神符靠\n毛干妳\n殺你全家\n秘书长\n成人色情午夜激情\n政权\n爱因斯坦\n法~~侖\n神之領域\n娘个比\n卖党求荣\n肉棒入少妇嫩穴嫩穴\n蜜穴\n法*仑\n免费观看女教師赤版\n高麗樸\n激情少妇\n美國佬\n成人动漫 成人文学网 成人小电影\n免费淫电影吴宗宪搞笑语录(21禁看)\n六四正名\n天安門錄影帶\n色影院\nｍａｎｋｉｎｄ\n顺着红嫩的肉缝上下抚弄后插入小穴\n毛贱人\n初中meimei的小美穴\n人民礼堂\n疯狂抽插她的小穴\n破處\n肉棒一阵猛烈的抽插\n世纪天成\n亚历山大\n淫毛\n日军\n男女激情淫贴图\n强奸电影网站\n游戏送奖员\n操王\n嫩鮑魚\n你媽\n吸吮著我的阳具\n淫母\n你娘的\n我和姐姐的淫乱史\n阴唇好嫩啊\n言论自由\n亚洲成人俱乐部\n真主安拉\n美腿美穴大屌\n法$伦\n又大又嫩的屄\n統治\n222se圖片\n操迟浩田\n风骚小阿姨\n淫女 幼B\n機八\n毛塞你母\n法~~倫\n服务天使\n一起玩\n错比\n淫美贴图区\n发~~轮\n肏屄好舒服\n发^纶\n粗肉棒阴户\n阴茎被子宫夹住好爽\nJj插入嫩穴\n成人激情午夜场\n武林外史\n强奸31岁处女全过程/淫妹阴毛小穴\n肥厚柔软的大阴唇夹着大龟头\nFALUNDAFA\n俄罗斯激情淫色贴图\n法*伦\n淫汁\n毛外　挂\n33bbb走光\n伊斯兰\nav小电影\n淫水\n马加爵\n处女的肉穴给他的肉棒一插到底\n快来┅┅插 肉穴\nmeinv网站脱衣meinv百分百作爱\n调教虐待\n成人电影导航\nVol。 17 ~巨乳美熟女\n帕巴拉•格列朗傑\n妹妹的小穴好紧啊\n娱乐性大型黄站\n复辟\n我第一次舔表姐的阴\n强奸系列精品\n陌生做爱在车里发生\n游戏米果\n小屄儿温暖滑嫩\n十八摸\n豔乳\nfuck神经病\n骚姐乱伦网\n无码淫漫\n国外成人激情网站\n无码彩图\nｋｕａｉ ｌｅ ｘｉ ｙｏｕ\n轮暴\ntibetalk\n92bb成人小电影\n免费淫电影网\n干我\n天然むすめ 素人~みか\n狂插白嫩幼女bb还射乳\n救援\nｘｉｎｇ ｚｈａｎ ｑｉｎｇ ｙｕａｎ\n多維網\n巨乳女优 千秋大合集\n换妻大会\n新观察论坛\n人妻阴穴\n色情帝国黄站\n强奸处女\n糞\n我的肉棒仍然在滴著精液\n舔舔私处妹妹嗷嗷叫\n侨办\n孙大午\n插的姐姐好爽\n嫩肉 嫩肉 大腿\n淫奸姐姐电影\n法$侖\n…太爽了…大鸡巴\n狂操少妇逼\n嫩穴 肉棒 肉缝\n伱妈\n(桃太郎)ファーストアナル生中出しSpecial 佐藤江梨花 特典\n毛我妳老爸\ntEST\n激情淫穴的嫂嫂\n毛机巴\n代挂\n肉棒插 嫩穴 淫穴\nｆｒｅｅ ｓｔｙｌｅ\n日本激情成人乱伦\nTora Tora Gold Vol。11 : 松田亜美\ngong fu\n狂插淫穴淫水\n89年的鬥爭\n台湾十八电影\n小淫虫电影\nCREATE\n外阴\n外？？挂\n毛爷爷复活\n本站10004多部激情电影全部免费试看\n富兰克林\n免费淫奸女Ａ片段试看 居然花钱请人教性爱姿势\n操穴\ntransvestit\n政治\n滥交\n色貓\n李 录\n免费在线观看 免费淫奸女Ａ片段\n肉棒狂插花心深处\n回族人吃猪肉\n淫河\n淫水留入穴中\n機吧\n林祖涵\n水去車仑\n抽插44岁处女妹妹淫叫不断\n毛精液\n列寧\n发@纶\n44xp。com 电影免费\n又肥又大的骚屄儿\n淫色贴图论坛\n侬着卵抛\n色妹妹成人论\n获救\n臺灣是主權國家\n戳那嗎逼\n毛蒋介石\n成人黄色三级网站\n陳宗興\n超meinv优MUGEN EX Vol。 7~浜崎ひめ\n操姐姐的穴\n新江湖\n性交无码\nyuckfu\n淫語痴女性欲\n毛驶你公\n药物奸淫女高校生A片\nＦＵＣＫ\n瓜批\n嫩逼 淫水\n硬绑绑的阴茎插入阴道\n王若望\n网絡公祭\nｚｈｅｎｇ ｔｕ\n1989事件\n玉蒲园淫书淫图射\n三男轮番干一个荡妇\n你是雞\n外？？掛\n東京熱最受歡迎長腿美女~藤澤安奈~腿長人又漂亮\n强奸27岁处女全过程/淫妹阴毛小穴\n毛淫娃\n夜夜色大型黄站\nfuck田纪云\n毛手淫\n诱奸\n毛回族人吃猪肉\n好粗大的肉棒\n黑社会\n幼穴贴图 毛穴贴图 穴淫贴图\n含着他的龟头\n法輪\n花蕊\n内测\n黑鮑嬌\n快乐av\n冈村秀树\n捅死你\n娘的\n曼德拉\n插的meimei好爽啊\nmo xiang\n周贺\n淫荡 空姐\nｄｉｇｉｍａｇｉｃｓ\n分开两片肥嫩的阴唇\n应招\n藓鲍\n淫奸电影 淫色猎暴淫电影 射雕淫女传 风骚小龙女 meinv淫女图-小龙女淫女俱乐部影 在线淫电影\n列宁\n人民广场\n山西洪洞\n淫浪\n毛龙新民\n海乐神\nplanetside\n3P炮图\n淫流\n男女激情淫乱穴下载\n摩洛客\njiejie的小屄\n龙图智库\nda qing di guo\n王寶森\n淫浆\n高级淫口手淫口交\ntESt\n金瓶梅免费电影院\n毛李山\n淫洞\nbt淫色贴图\nmm穴激情小说\n3P炮圖\n亚洲色\nBITCH\n毛三去车仑\n毛奸淫\n姐弟乱轮 yy玛雅激情贴\n干林\n大鸡巴就慢慢的插入 小穴\n混沌\n聖女峰\n超級可愛巨乳妹被狂插得鼻血滿天飛\n揚震\nｗｗｗ。ｇｐｃｈｉｎａ。ｃｏｍ\n怎样延迟射精24岁以下禁\n测试员\n18dy桃色\nｘｉｎｇ ｆｕ ｈｕａ ｙｕａｎ\n穴\n師春生\nfuck太子党\n鸡巴淫沟插B大图\n曾庆红\n插女教师的屁眼\n54xp。com 电影免费\nFALUN\n成人电影网\n毛法制办\n马国瑞\nmeimei的阴道真紧\nur靠\n带炼\n淫电影a片\n轮攻\n神风- Kamikaze Premium Vol。 32 : 一色Azusa\n韓國三級情色电影\n手淫~少妇自慰喷淫水\n張 萬 年\n艷照门\n幹你妈b\n法轮\n(Madonna) 熟女義母 11套 AVI\n操神\n民族矛盾\n淫穴影院\n烟草局\n劉曉竹\n免费淫奸女Ａ片段 免费在线观看\n习近平\n嫩穴\n淫液\n操大波波\n干拎娘\n综联数码\n居然花钱请人教性爱姿势(组图)\n淫蕩妖艷女醫生\nmeimei的小穴……舒服死了……\n情色 信息\n多人性愛\n干机\n汉娃娃\n邵家健\n法踚功\nfuck色情电影\n測试員\nA片\n藤井せいら - ボルテージ X\n大参考\n韓 聯 潮\n(无关键词)导航搜索\n一次可看四位美女同床較勁很可愛的女主角\nmeimei的小淫穴\n淫色成人网\n毛太监\n淫湿\n少妇穴\ntriangleboy\n鬼輪姦\nabc abc\n乳爆\n他ㄇ的\n色网阴道特写强奸34岁处女全过程\n前任男友\n嫩屄\n成人 激情 性爱\n不注册看艳片\n小穴被大鸡巴插得好疼啊\n轮操\nfuck台湾狗\n系統訊息\n台湾淫色电影\n亚洲\n毛阴小撕大\n賤bi\nUR靠\n生鸦片\n操小姐穴\nfuck孙逸仙\n(虎虎虎)风间ゆみ-新婚\n色窝窝\nｃｏｎｎａｒｄ\n自由亞洲電臺\n妹妹的阴道好嫩啊\nfuck孙中山\n毛抗日\n谁知道激情网站\n香港成人电影网\nｌｕａｎ\n假钞\n毛耶稣\n十八岁meinv护士电影网站\n李 小 朋\n賞圖交流園\n精液射入花心\n國務院\n反黨\n丝袜足交\nfuck特别公告\n肉棒插入肉\n毛妈比\n丁石孙\n淫虫电影\n免费淫电影试看\n李師傅\n夫妻乱交\n广安第二人民医院\n臭鸡\n鹿城色meimei大型黄站\n揭批书\n互淫\n少妇白洁的淫乱生活\n真理組織\n丁石孫\n手记注册日韩5017部激情电影\n插的妈妈好爽\n肉棒插进她的密洞使劲的抽送着\n我和少妇做爱\nｇｕａｎｇ ｚｈｉ ｇｕｏ ｄｕ\n狂插美少女穴\n手记注册日韩5019部激情电影\n小穴流出\n免费Ａ片在线\nGカップカリスマ店員松嶋れいな20連発中出し！\n国防部\n裸女乳房阴道丰臀\n制服誘惑\nｋｅ ｌｕｏ ｓｉ\n丁加班\n尼米兹\nfuck天安门事件\n你它馬的\n操逼你老母\n成人激情淫网站\n国共合作\n毛血逼\nwowbank\n色网导航\n毛流氓\n免费视讯妹妹\n1989\n金庸群俠傳\nBT激情无码\n迷人阴部护士妹妹也疯狂\n毛新手指导员\n學生愛國者運動\n毛激情小电影\n例假\n干空姐小穴\n成都锦天\n透视软件\n糾察員\n肉棒插入湿湿的肉缝\n插屄好舒服\n悠游网\n那娘錯比\nsecretchina\n江泽民靠\n錢 其 琛\n和妹妹做爱\n服务器\n性交做爱\n毛李鹏\n狗卵子\n少女被逼吃精喝精轮奸灌精\n八方链流量交换链\nfuck生鸦片\n發^^掄\n臺灣總統\n下载免费黄色淫乱电影\n双飞+毒龙直到分享了精液\n蒙古独\n媽個比\n餓比\n騷姨媽\n1989 天安門\n毛客户服务人员\nｇｏｎｇ ｆｕ ｘｉａｏ ｚｉ\n冯素英\n丝袜写真\n调理劳务系\n激情淫穴小\n测试員\nｄａ ｔａｎｇ ｈａｏ ｘｉａ\n干死 狂插 操\n我周容\n叫鸡\n熟女乱伦网\n免费看激情视频免费成人做爱\n本站10017多部激情电影全部免费试看三天\n风骚欲女\n艷照門\n乱伦(无码)母爱包( 乳母\n柯赐海\n1989 天安门\n周总理\n免费淫奸女Ａ片段试看\n吴学璨\n肉壁紧紧的包著龟头\n哥哥的肉棒好大\n打到\n7hero\nzhengwunet\n拿着鸡巴对准小穴\n幼香閣\n毛烂B\n一起玩遊戲網\n高清晰无码内射\n紧窄的洞口只能含进半个龟头\n游戏管理员\n淫乱少妇\n圣淫乱学院\n小穴被阴茎顶住\n捅入妹妹的阴道\n母女双飞\n希望之聲\n淫漿\n整根肉棒被紧箍在幽暗深遽的娇嫩小穴内\n妻子小穴\n超美混血兒(小泽玛利亚出道时的珍藏)\n刚度\nluo qi\n张健\n烂屄\n卡通meinv淫乱图\n烂屌\n叛黨\n可可精\n淫贱meimei贴图论坛\n锦涛\n姐姐的肥阴唇\n毛发骚\n础\n激情淫文章\nfuck太监\n强奸21岁处女全过程/*妹阴毛小穴\n骚meimei成人导航\n夹得我好舒服\n傅志寰\n朱鎔基\n免费淫奸黄色淫乱电影\n共狗\n老騷貨\n精液射进穴里视频\n日啊\n幼香阁\n西藏事件\n亚/无码\n淫奸护士\n免费成人网站\n创意鹰翔\n厕所性感美女清晰淫穴\n免费观看王小丫做爱\n纪股票市场五卅惨案\n插那嗎逼\n黑鮑嬌\n色窩窩\n美女孩含双棍玩内射\n张德江\n续金瓶梅\nju shang\n18dy大众成人网\nｚｈｅｎｇ ｆｕ\n淫妹妹\n被噁心男搞到高潮不停的立花里子\n激情视频聊天室\n成人論壇\n中央政治局\n发~~论\n毛新华内情\n天遊\npp 点点激情片播放\n毛人民日报\n成人联盟\nmeinv偷拍成人小说激情网香港激情图源成人色情论坛脱衣meinv做愛\n制服古典乱伦都市\n插陰\n操成人电影\n傲视\n氯胺酮\n和平\n彭德怀\n老师的肉棒好大啊\nfuck天皇\n李師付\n国民党\n国 家 安 全\n少妇房中自淫，和两勇男玩4P，操得直叫爽 操日本淫女穴\n大眾色情成人網\nrx008\n舔　蜜穴好爽\n错那吗B\n成人情感裸女裸聊图片\n肉縫分開大腿\n水去車侖\n鸡吧一柱擎天\n阳具在阴道里来回抽插\n巨大的鸡巴戳了进去\n遊戲嚮導\n美幼专区\n賈慶林\n廢墟\n斧頭鐮刀\n退党\nmeimei的阴户\nTokyo Hot n0232 : 真宫梨沙子 傲慢RQ中出责备紊乱呼吸\n淫荡的奸淫\n亚洲激情淫色贴图区\n马万祺\n高速下載!\n100ONE VIP MEMBERS\n法**纶\n一貫道\n插着淫穴\n漏逼\n隱窩窩\n熟睡中的meimei\n毛无界浏览器\n夫妻亂交\n毛金正日\n酒店援交\n張 鋼\n小肉粒\nfuck外—挂\n新义安\n劉傑\n夜总会\n聖堂科技\n司法部\n插阴\n網愛\n下三爛\n客户服务\n免费小淫虫\n"
  },
  {
    "path": "README.md",
    "content": "<h1 align=\"center\">\n  电音组队交友小程序-派之城\n</h1>\n\n<p align=\"center\">\n  <a href=\"https://github.com/fl1906/music-city/blob/master/LICENSE\"><img src=\"https://img.shields.io/github/license/zyronon/douyin\" alt=\"License\"></a>\n  <a><img src=\"https://img.shields.io/badge/SpringBoot-v2.7-pink\"/></a>\n  <a><img src=\"https://img.shields.io/badge/若依-基础框架-red\"/></a>\n  <a><img src=\"https://img.shields.io/badge/Satoken-v1.37.0-red\"/></a>\n  <a><img src=\"https://img.shields.io/badge/Vue-v2.0-red\"/></a>\n  <a><img src=\"https://img.shields.io/badge/Uniapp-微信小程序-red\"/></a>\n</p>\n\n`派之城` 是一个微信音乐组队交友小程序，功能包括：发布音乐节活动、发布组队活动、城市组队匹配、聊天室、音乐播放、个人中心等。\n它基于 [`SpringBoot`](https://spring.io/projects/spring-boot)、[`若依`](https://ruoyi.vip/)、[`Vue`](https://cn.vuejs.org/)、[`Uniapp`](https://uniapp.dcloud.io/) 实现。使用了最新的 `SpringBoot` 全家桶技术栈。使用Mysql数据库存储，通过 `SpringBoot` 提供的接口返回数据，前端使用 `Vue` 和 `Uniapp` 实现。\n\n\n## 项目预览\n\n<div>\n<img width=\"150px\" src='docs/imgs/1.png'/>\n<img width=\"150px\" src='docs/imgs/2.png'/>\n<img width=\"150px\" src='docs/imgs/3.png'/>\n<img width=\"150px\" src='docs/imgs/4.png'/>\n<img width=\"150px\" src='docs/imgs/5.png'/>\n<img width=\"150px\" src='docs/imgs/6.png' />\n<img width=\"150px\" src='docs/imgs/7.png' />\n<img width=\"150px\" src='docs/imgs/8.png' />\n<img width=\"150px\" src='docs/imgs/9.png' />\n<img width=\"150px\" src='docs/imgs/10.png' />\n</div>\n\n## 在线体验\n\n\n<img width=\"150px\" src=\"https://img.flya.top/mac/202405022018128.png\">\n\n## 免责声明\n\n本项目仅适用于学习和研究，不得用于商业使用\n\n## 运行\n\n后端运行：\n\n```bash\ngit clone -b master --single-branch https://github.com/fl1906/music-city.git\n\ncd music-city\n\n```\n\n修改配置：\n![img.png](docs/imgs/img.png)\n\n![img_1.png](docs/imgs/img_1.png)\n\n![img_2.png](docs/imgs/img_2.png)\n\n![img_3.png](docs/imgs/img_3.png)\n\n小程序端运行：\n\n打开HbuilderX，导入项目，运行到微信小程序\n\n```bash\ngit clone -b uniapp --single-branch https://github.com/fl1906/music-city.git\n\ncd music-city-uniapp\n\n```\n\n## 致谢\n<div> 网易云音乐API ： https://binaryify.github.io/NeteaseCloudMusicApi/#/ </div>\n<p></p>\n<div> 高校查询接口 ： https://hn216.api.yesapi.cn/</div>\n<p></p>\n<div> 若依框架： https://ruoyi.vip/ </div>\n<p></p>\n<div>  Uniapp框架： https://uniapp.dcloud.io/ </div>\n<p></p>\n<div>  Satoken框架： https://sa-token.dev33.cn/ </div>\n\n\n## 许可协议\n\n[GPL](LICENSE)\n\n## 联系作者\n\n微信：fl1906\n\n<img  width=\"150px\" src=\"https://img.flya.top/mac/202405022144221.png\">"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.ruoyi</groupId>\n    <artifactId>ruoyi-vue-plus</artifactId>\n    <version>4.7.0</version>\n\n    <name>RuoYi-Vue-Plus</name>\n    <url>https://gitee.com/dromara/RuoYi-Vue-Plus</url>\n    <description>派之城后台管理系统 </description>\n\n    <properties>\n        <ruoyi-vue-plus.version>4.7.0</ruoyi-vue-plus.version>\n        <spring-boot.version>2.7.11</spring-boot.version>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n        <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>\n        <spring-boot.mybatis>2.2.2</spring-boot.mybatis>\n        <springdoc.version>1.6.15</springdoc.version>\n        <poi.version>5.2.3</poi.version>\n        <easyexcel.version>3.2.1</easyexcel.version>\n        <velocity.version>2.3</velocity.version>\n        <satoken.version>1.34.0</satoken.version>\n        <mybatis-plus.version>3.5.3.1</mybatis-plus.version>\n        <p6spy.version>3.9.1</p6spy.version>\n        <hutool.version>5.8.18</hutool.version>\n        <okhttp.version>4.10.0</okhttp.version>\n        <spring-boot-admin.version>2.7.10</spring-boot-admin.version>\n        <redisson.version>3.20.1</redisson.version>\n        <lock4j.version>2.2.3</lock4j.version>\n        <dynamic-ds.version>3.5.2</dynamic-ds.version>\n        <alibaba-ttl.version>2.14.2</alibaba-ttl.version>\n        <xxl-job.version>2.4.0</xxl-job.version>\n        <lombok.version>1.18.26</lombok.version>\n        <bouncycastle.version>1.72</bouncycastle.version>\n        <!-- 离线IP地址定位库 -->\n        <ip2region.version>2.7.0</ip2region.version>\n\n        <!-- 临时修复 snakeyaml 漏洞 -->\n        <snakeyaml.version>1.33</snakeyaml.version>\n\n        <!-- OSS 配置 -->\n        <aws-java-sdk-s3.version>1.12.400</aws-java-sdk-s3.version>\n        <!-- SMS 配置 -->\n        <aliyun.sms.version>2.0.23</aliyun.sms.version>\n        <tencent.sms.version>3.1.687</tencent.sms.version>\n    </properties>\n\n    <profiles>\n        <profile>\n            <id>local</id>\n            <properties>\n                <!-- 环境标识，需要与配置文件的名称相对应 -->\n                <profiles.active>local</profiles.active>\n                <logging.level>debug</logging.level>\n            </properties>\n        </profile>\n        <profile>\n            <id>dev</id>\n            <properties>\n                <!-- 环境标识，需要与配置文件的名称相对应 -->\n                <profiles.active>dev</profiles.active>\n                <logging.level>debug</logging.level>\n            </properties>\n            <activation>\n                <!-- 默认环境 -->\n                <activeByDefault>true</activeByDefault>\n            </activation>\n        </profile>\n        <profile>\n            <id>prod</id>\n            <properties>\n                <profiles.active>prod</profiles.active>\n                <logging.level>warn</logging.level>\n            </properties>\n        </profile>\n    </profiles>\n\n    <!-- 依赖声明 -->\n    <dependencyManagement>\n        <dependencies>\n\n\n\n\n\n            <dependency>\n                <groupId>cn.hutool</groupId>\n                <artifactId>hutool-http</artifactId>\n                <version>5.8.18</version>\n            </dependency>\n\n            <!-- SpringBoot的依赖配置-->\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-dependencies</artifactId>\n                <version>${spring-boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n\n            <!-- hutool 的依赖配置-->\n            <dependency>\n                <groupId>cn.hutool</groupId>\n                <artifactId>hutool-bom</artifactId>\n                <version>${hutool.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>org.springdoc</groupId>\n                <artifactId>springdoc-openapi-webmvc-core</artifactId>\n                <version>${springdoc.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.springdoc</groupId>\n                <artifactId>springdoc-openapi-javadoc</artifactId>\n                <version>${springdoc.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.projectlombok</groupId>\n                <artifactId>lombok</artifactId>\n                <version>${lombok.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.poi</groupId>\n                <artifactId>poi</artifactId>\n                <version>${poi.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.apache.poi</groupId>\n                <artifactId>poi-ooxml</artifactId>\n                <version>${poi.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>easyexcel</artifactId>\n                <version>${easyexcel.version}</version>\n                <exclusions>\n                    <exclusion>\n                        <groupId>org.apache.poi</groupId>\n                        <artifactId>poi-ooxml-schemas</artifactId>\n                    </exclusion>\n                </exclusions>\n            </dependency>\n\n            <!-- velocity代码生成使用模板 -->\n            <dependency>\n                <groupId>org.apache.velocity</groupId>\n                <artifactId>velocity-engine-core</artifactId>\n                <version>${velocity.version}</version>\n            </dependency>\n\n            <!-- Sa-Token 权限认证, 在线文档：http://sa-token.dev33.cn/ -->\n            <dependency>\n                <groupId>cn.dev33</groupId>\n                <artifactId>sa-token-spring-boot-starter</artifactId>\n                <version>${satoken.version}</version>\n            </dependency>\n            <!-- Sa-Token 整合 jwt -->\n            <dependency>\n                <groupId>cn.dev33</groupId>\n                <artifactId>sa-token-jwt</artifactId>\n                <version>${satoken.version}</version>\n                <exclusions>\n                    <exclusion>\n                        <groupId>cn.hutool</groupId>\n                        <artifactId>hutool-all</artifactId>\n                    </exclusion>\n                </exclusions>\n            </dependency>\n\n            <!-- dynamic-datasource 多数据源-->\n            <dependency>\n                <groupId>com.baomidou</groupId>\n                <artifactId>dynamic-datasource-spring-boot-starter</artifactId>\n                <version>${dynamic-ds.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.baomidou</groupId>\n                <artifactId>mybatis-plus-boot-starter</artifactId>\n                <version>${mybatis-plus.version}</version>\n            </dependency>\n\n            <!-- sql性能分析插件 -->\n            <dependency>\n                <groupId>p6spy</groupId>\n                <artifactId>p6spy</artifactId>\n                <version>${p6spy.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.squareup.okhttp3</groupId>\n                <artifactId>okhttp</artifactId>\n                <version>${okhttp.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.amazonaws</groupId>\n                <artifactId>aws-java-sdk-s3</artifactId>\n                <version>${aws-java-sdk-s3.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.aliyun</groupId>\n                <artifactId>dysmsapi20170525</artifactId>\n                <version>${aliyun.sms.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.tencentcloudapi</groupId>\n                <artifactId>tencentcloud-sdk-java-sms</artifactId>\n                <version>${tencent.sms.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>de.codecentric</groupId>\n                <artifactId>spring-boot-admin-starter-server</artifactId>\n                <version>${spring-boot-admin.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>de.codecentric</groupId>\n                <artifactId>spring-boot-admin-starter-client</artifactId>\n                <version>${spring-boot-admin.version}</version>\n            </dependency>\n\n            <!--redisson-->\n            <dependency>\n                <groupId>org.redisson</groupId>\n                <artifactId>redisson-spring-boot-starter</artifactId>\n                <version>${redisson.version}</version>\n                <exclusions>\n                    <exclusion>\n                        <groupId>org.redisson</groupId>\n                        <artifactId>redisson-spring-data-30</artifactId>\n                    </exclusion>\n                </exclusions>\n            </dependency>\n            <dependency>\n                <groupId>org.redisson</groupId>\n                <artifactId>redisson-spring-data-27</artifactId>\n                <version>${redisson.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.baomidou</groupId>\n                <artifactId>lock4j-redisson-spring-boot-starter</artifactId>\n                <version>${lock4j.version}</version>\n            </dependency>\n\n            <!-- xxl-job-core -->\n            <dependency>\n                <groupId>com.xuxueli</groupId>\n                <artifactId>xxl-job-core</artifactId>\n                <version>${xxl-job.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>transmittable-thread-local</artifactId>\n                <version>${alibaba-ttl.version}</version>\n            </dependency>\n\n            <!-- 离线IP地址定位库 ip2region -->\n            <dependency>\n                <groupId>org.lionsoul</groupId>\n                <artifactId>ip2region</artifactId>\n                <version>${ip2region.version}</version>\n            </dependency>\n\n            <!-- 临时修复 snakeyaml 漏洞 -->\n            <dependency>\n                <groupId>org.yaml</groupId>\n                <artifactId>snakeyaml</artifactId>\n                <version>${snakeyaml.version}</version>\n            </dependency>\n\n            <!-- 加密包引入 -->\n            <dependency>\n                <groupId>org.bouncycastle</groupId>\n                <artifactId>bcprov-jdk15to18</artifactId>\n                <version>${bouncycastle.version}</version>\n            </dependency>\n\n            <!-- 定时任务 -->\n            <dependency>\n                <groupId>com.ruoyi</groupId>\n                <artifactId>ruoyi-job</artifactId>\n                <version>${ruoyi-vue-plus.version}</version>\n            </dependency>\n\n            <!-- 代码生成-->\n            <dependency>\n                <groupId>com.ruoyi</groupId>\n                <artifactId>ruoyi-generator</artifactId>\n                <version>${ruoyi-vue-plus.version}</version>\n            </dependency>\n\n            <!-- 核心模块-->\n            <dependency>\n                <groupId>com.ruoyi</groupId>\n                <artifactId>ruoyi-framework</artifactId>\n                <version>${ruoyi-vue-plus.version}</version>\n            </dependency>\n\n            <!-- 系统模块-->\n            <dependency>\n                <groupId>com.ruoyi</groupId>\n                <artifactId>ruoyi-system</artifactId>\n                <version>${ruoyi-vue-plus.version}</version>\n            </dependency>\n\n            <!-- 通用工具-->\n            <dependency>\n                <groupId>com.ruoyi</groupId>\n                <artifactId>ruoyi-common</artifactId>\n                <version>${ruoyi-vue-plus.version}</version>\n            </dependency>\n\n            <!-- OSS对象存储模块 -->\n            <dependency>\n                <groupId>com.ruoyi</groupId>\n                <artifactId>ruoyi-oss</artifactId>\n                <version>${ruoyi-vue-plus.version}</version>\n            </dependency>\n\n            <!-- SMS短信模块 -->\n            <dependency>\n                <groupId>com.ruoyi</groupId>\n                <artifactId>ruoyi-sms</artifactId>\n                <version>${ruoyi-vue-plus.version}</version>\n            </dependency>\n\n            <!-- demo模块 -->\n            <dependency>\n                <groupId>com.ruoyi</groupId>\n                <artifactId>pai-zhi-cheng</artifactId>\n                <version>${ruoyi-vue-plus.version}</version>\n            </dependency>\n\n        </dependencies>\n    </dependencyManagement>\n\n    <modules>\n        <module>ruoyi-admin</module>\n        <module>ruoyi-framework</module>\n        <module>ruoyi-system</module>\n        <module>ruoyi-job</module>\n        <module>ruoyi-generator</module>\n        <module>ruoyi-common</module>\n        <module>PaiZhiCheng</module>\n        <module>ruoyi-extend</module>\n        <module>ruoyi-oss</module>\n        <module>ruoyi-sms</module>\n    </modules>\n    <packaging>pom</packaging>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.9.0</version>\n                <configuration>\n                    <source>${java.version}</source>\n                    <target>${java.version}</target>\n                    <encoding>${project.build.sourceEncoding}</encoding>\n                    <annotationProcessorPaths>\n                        <path>\n                            <groupId>com.github.therapi</groupId>\n                            <artifactId>therapi-runtime-javadoc-scribe</artifactId>\n                            <version>0.15.0</version>\n                        </path>\n                        <path>\n                            <groupId>org.projectlombok</groupId>\n                            <artifactId>lombok</artifactId>\n                            <version>${lombok.version}</version>\n                        </path>\n                        <path>\n                            <groupId>org.springframework.boot</groupId>\n                            <artifactId>spring-boot-configuration-processor</artifactId>\n                            <version>${spring-boot.version}</version>\n                        </path>\n                    </annotationProcessorPaths>\n                </configuration>\n            </plugin>\n            <!-- 单元测试使用 -->\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <version>2.22.2</version>\n                <configuration>\n                    <argLine>-Dfile.encoding=UTF-8</argLine>\n                    <!-- 根据打包环境执行对应的@Tag测试方法 -->\n                    <groups>${profiles.active}</groups>\n                    <!-- 排除标签 -->\n                    <excludedGroups>exclude</excludedGroups>\n                </configuration>\n            </plugin>\n        </plugins>\n        <resources>\n            <resource>\n                <directory>src/main/resources</directory>\n                <!-- 关闭过滤 -->\n                <filtering>false</filtering>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n                <!-- 引入所有 匹配文件进行过滤 -->\n                <includes>\n                    <include>application*</include>\n                    <include>bootstrap*</include>\n                    <include>banner*</include>\n                </includes>\n                <!-- 启用过滤 即该资源中的变量将会被过滤器中的值替换 -->\n                <filtering>true</filtering>\n            </resource>\n        </resources>\n    </build>\n\n    <repositories>\n        <repository>\n            <id>public</id>\n            <name>aliyun nexus</name>\n            <url>https://maven.aliyun.com/repository/public/</url>\n            <releases>\n                <enabled>true</enabled>\n            </releases>\n        </repository>\n    </repositories>\n\n    <pluginRepositories>\n        <pluginRepository>\n            <id>public</id>\n            <name>aliyun nexus</name>\n            <url>https://maven.aliyun.com/repository/public/</url>\n            <releases>\n                <enabled>true</enabled>\n            </releases>\n            <snapshots>\n                <enabled>false</enabled>\n            </snapshots>\n        </pluginRepository>\n    </pluginRepositories>\n\n</project>\n\n\n"
  },
  {
    "path": "ruoyi-admin/Dockerfile",
    "content": "FROM anapsix/alpine-java:8_server-jre_unlimited\n\nMAINTAINER Lion Li\n\nRUN mkdir -p /ruoyi/server/logs \\\n    /ruoyi/server/temp \\\n    /ruoyi/skywalking/agent\n\nWORKDIR /ruoyi/server\n\nENV SERVER_PORT=8080\n\nEXPOSE ${SERVER_PORT}\n\nADD ./target/ruoyi-admin.jar ./app.jar\n\nENTRYPOINT [\"java\", \\\n            \"-Djava.security.egd=file:/dev/./urandom\", \\\n            \"-Dserver.port=${SERVER_PORT}\", \\\n            # 应用名称 如果想区分集群节点监控 改成不同的名称即可\n#            \"-Dskywalking.agent.service_name=ruoyi-server\", \\\n#            \"-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar\", \\\n            \"-jar\", \"app.jar\"]\n"
  },
  {
    "path": "ruoyi-admin/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>ruoyi-vue-plus</artifactId>\n        <groupId>com.ruoyi</groupId>\n        <version>4.7.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>jar</packaging>\n    <artifactId>pzc</artifactId>\n\n    <description>\n        web服务入口\n    </description>\n\n    <dependencies>\n        <dependency> <!-- 表达式计算 选项3 -->\n            <groupId>com.googlecode.aviator</groupId>\n            <artifactId>aviator</artifactId>\n            <version>5.2.6</version>\n        </dependency>\n\n        <!-- spring-boot-devtools -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-devtools</artifactId>\n            <optional>true</optional> <!-- 表示依赖不会传递 -->\n        </dependency>\n\n        <!-- Mysql驱动包 -->\n        <dependency>\n            <groupId>com.mysql</groupId>\n            <artifactId>mysql-connector-j</artifactId>\n        </dependency>\n        <!-- Oracle -->\n        <dependency>\n            <groupId>com.oracle.database.jdbc</groupId>\n            <artifactId>ojdbc8</artifactId>\n        </dependency>\n        <!-- PostgreSql -->\n        <dependency>\n            <groupId>org.postgresql</groupId>\n            <artifactId>postgresql</artifactId>\n        </dependency>\n        <!-- SqlServer -->\n        <dependency>\n            <groupId>com.microsoft.sqlserver</groupId>\n            <artifactId>mssql-jdbc</artifactId>\n        </dependency>\n\n        <!-- 核心模块-->\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>ruoyi-framework</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>ruoyi-system</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>ruoyi-job</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>ruoyi-oss</artifactId>\n        </dependency>\n\n        <!-- 代码生成-->\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>ruoyi-generator</artifactId>\n        </dependency>\n\n        <!--  demo模块  -->\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>pai-zhi-cheng</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <!-- skywalking 整合 logback -->\n<!--        <dependency>-->\n<!--            <groupId>org.apache.skywalking</groupId>-->\n<!--            <artifactId>apm-toolkit-logback-1.x</artifactId>-->\n<!--            <version>${与你的agent探针版本保持一致}</version>-->\n<!--        </dependency>-->\n<!--        <dependency>-->\n<!--            <groupId>org.apache.skywalking</groupId>-->\n<!--            <artifactId>apm-toolkit-trace</artifactId>-->\n<!--            <version>${与你的agent探针版本保持一致}</version>-->\n<!--        </dependency>-->\n\n    </dependencies>\n\n    <build>\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring-boot.version}</version>\n                <configuration>\n                    <fork>true</fork> <!-- 如果没有该配置，devtools不会生效 -->\n                </configuration>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-war-plugin</artifactId>\n                <version>3.2.2</version>\n                <configuration>\n                    <failOnMissingWebXml>false</failOnMissingWebXml>\n                    <warName>${project.artifactId}</warName>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/RuoYiApplication.java",
    "content": "package top.flya;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;\n/**\n * 启动程序\n *\n * @author ruoyi\n */\n\n@SpringBootApplication\npublic class RuoYiApplication {\n\n    public static void main(String[] args) {\n        System.setProperty(\"spring.devtools.restart.enabled\", \"true\");\n        SpringApplication application = new SpringApplication(RuoYiApplication.class);\n        application.setApplicationStartup(new BufferingApplicationStartup(2048));\n        application.run(args);\n        System.out.println(\"Hello,World!\");\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/RuoYiServletInitializer.java",
    "content": "package top.flya;\n\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.boot.web.servlet.support.SpringBootServletInitializer;\n\n/**\n * web容器中进行部署\n *\n * @author ruoyi\n */\npublic class RuoYiServletInitializer extends SpringBootServletInitializer {\n\n    @Override\n    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {\n        return application.sources(RuoYiApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/common/CaptchaController.java",
    "content": "package top.flya.web.controller.common;\n\nimport cn.dev33.satoken.annotation.SaIgnore;\nimport cn.hutool.captcha.AbstractCaptcha;\nimport cn.hutool.captcha.generator.CodeGenerator;\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.core.util.RandomUtil;\nimport top.flya.common.constant.CacheConstants;\nimport top.flya.common.constant.Constants;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.enums.CaptchaType;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.email.MailUtils;\nimport top.flya.common.utils.redis.RedisUtils;\nimport top.flya.common.utils.reflect.ReflectUtils;\nimport top.flya.common.utils.spring.SpringUtils;\nimport top.flya.framework.config.properties.CaptchaProperties;\nimport top.flya.framework.config.properties.MailProperties;\nimport top.flya.sms.config.properties.SmsProperties;\nimport top.flya.sms.core.SmsTemplate;\nimport top.flya.sms.entity.SmsResult;\nimport top.flya.system.service.ISysConfigService;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.validation.constraints.NotBlank;\nimport java.time.Duration;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 验证码操作处理\n *\n * @author Lion Li\n */\n@SaIgnore\n@Slf4j\n@Validated\n@RequiredArgsConstructor\n@RestController\npublic class CaptchaController {\n\n    private final CaptchaProperties captchaProperties;\n    private final SmsProperties smsProperties;\n    private final ISysConfigService configService;\n    private final MailProperties mailProperties;\n\n    /**\n     * 短信验证码\n     *\n     * @param phonenumber 用户手机号\n     */\n    @GetMapping(\"/captchaSms\")\n    public R<Void> smsCaptcha(@NotBlank(message = \"{user.phonenumber.not.blank}\") String phonenumber) {\n        if (!smsProperties.getEnabled()) {\n            return R.fail(\"当前系统没有开启短信功能！\");\n        }\n        String key = CacheConstants.CAPTCHA_CODE_KEY + phonenumber;\n        String code = RandomUtil.randomNumbers(4);\n        RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));\n        // 验证码模板id 自行处理 (查数据库或写死均可)\n        String templateId = \"\";\n        Map<String, String> map = new HashMap<>(1);\n        map.put(\"code\", code);\n        SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class);\n        SmsResult result = smsTemplate.send(phonenumber, templateId, map);\n        if (!result.isSuccess()) {\n            log.error(\"验证码短信发送异常 => {}\", result);\n            return R.fail(result.getMessage());\n        }\n        return R.ok();\n    }\n\n    /**\n     * 邮箱验证码\n     *\n     * @param email 邮箱\n     */\n    @GetMapping(\"/captchaEmail\")\n    public R<Void> emailCode(@NotBlank(message = \"{user.email.not.blank}\") String email) {\n        if (!mailProperties.getEnabled()) {\n            return R.fail(\"当前系统没有开启邮箱功能！\");\n        }\n        String key = CacheConstants.CAPTCHA_CODE_KEY + email;\n        String code = RandomUtil.randomNumbers(4);\n        RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));\n        try {\n            MailUtils.sendText(email, \"登录验证码\", \"您本次验证码为：\" + code + \"，有效性为\" + Constants.CAPTCHA_EXPIRATION + \"分钟，请尽快填写。\");\n        } catch (Exception e) {\n            log.error(\"验证码短信发送异常 => {}\", e.getMessage());\n            return R.fail(e.getMessage());\n        }\n        return R.ok();\n    }\n\n    /**\n     * 生成验证码\n     */\n    @GetMapping(\"/captchaImage\")\n    public R<Map<String, Object>> getCode() {\n        Map<String, Object> ajax = new HashMap<>();\n        boolean captchaEnabled = configService.selectCaptchaEnabled();\n        ajax.put(\"captchaEnabled\", captchaEnabled);\n        if (!captchaEnabled) {\n            return R.ok(ajax);\n        }\n        // 保存验证码信息\n        String uuid = IdUtil.simpleUUID();\n        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;\n        // 生成验证码\n        CaptchaType captchaType = captchaProperties.getType();\n        boolean isMath = CaptchaType.MATH == captchaType;\n        Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();\n        CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);\n        AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());\n        captcha.setGenerator(codeGenerator);\n        captcha.createCode();\n        String code = captcha.getCode();\n        if (isMath) {\n            ExpressionParser parser = new SpelExpressionParser();\n            Expression exp = parser.parseExpression(StringUtils.remove(code, \"=\"));\n            code = exp.getValue(String.class);\n        }\n        RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));\n        ajax.put(\"uuid\", uuid);\n        ajax.put(\"img\", captcha.getImageBase64());\n        return R.ok(ajax);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/monitor/CacheController.java",
    "content": "package top.flya.web.controller.monitor;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport cn.hutool.core.collection.CollUtil;\nimport top.flya.common.constant.CacheConstants;\nimport top.flya.common.constant.CacheNames;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.redis.CacheUtils;\nimport top.flya.common.utils.redis.RedisUtils;\nimport top.flya.system.domain.SysCache;\nimport lombok.RequiredArgsConstructor;\nimport org.redisson.spring.data.connection.RedissonConnectionFactory;\nimport org.springframework.data.redis.connection.RedisConnection;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 缓存监控\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/monitor/cache\")\npublic class CacheController {\n\n    private final RedissonConnectionFactory connectionFactory;\n\n    private final static List<SysCache> CACHES = new ArrayList<>();\n\n    static {\n        CACHES.add(new SysCache(CacheConstants.ONLINE_TOKEN_KEY, \"在线用户\"));\n        CACHES.add(new SysCache(CacheNames.SYS_CONFIG, \"配置信息\"));\n        CACHES.add(new SysCache(CacheNames.SYS_DICT, \"数据字典\"));\n        CACHES.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, \"验证码\"));\n        CACHES.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, \"防重提交\"));\n        CACHES.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, \"限流处理\"));\n        CACHES.add(new SysCache(CacheNames.SYS_OSS_CONFIG, \"OSS配置\"));\n        CACHES.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, \"密码错误次数\"));\n    }\n\n    /**\n     * 获取缓存监控列表\n     */\n    @SaCheckPermission(\"monitor:cache:list\")\n    @GetMapping()\n    public R<Map<String, Object>> getInfo() throws Exception {\n        RedisConnection connection = connectionFactory.getConnection();\n        Properties info = connection.info();\n        Properties commandStats = connection.info(\"commandstats\");\n        Long dbSize = connection.dbSize();\n\n        Map<String, Object> result = new HashMap<>(3);\n        result.put(\"info\", info);\n        result.put(\"dbSize\", dbSize);\n\n        List<Map<String, String>> pieList = new ArrayList<>();\n        if (commandStats != null) {\n            commandStats.stringPropertyNames().forEach(key -> {\n                Map<String, String> data = new HashMap<>(2);\n                String property = commandStats.getProperty(key);\n                data.put(\"name\", StringUtils.removeStart(key, \"cmdstat_\"));\n                data.put(\"value\", StringUtils.substringBetween(property, \"calls=\", \",usec\"));\n                pieList.add(data);\n            });\n        }\n        result.put(\"commandStats\", pieList);\n        return R.ok(result);\n    }\n\n    /**\n     * 获取缓存监控缓存名列表\n     */\n    @SaCheckPermission(\"monitor:cache:list\")\n    @GetMapping(\"/getNames\")\n    public R<List<SysCache>> cache() {\n        return R.ok(CACHES);\n    }\n\n    /**\n     * 获取缓存监控Key列表\n     *\n     * @param cacheName 缓存名\n     */\n    @SaCheckPermission(\"monitor:cache:list\")\n    @GetMapping(\"/getKeys/{cacheName}\")\n    public R<Collection<String>> getCacheKeys(@PathVariable String cacheName) {\n        Collection<String> cacheKeys = new HashSet<>(0);\n        if (isCacheNames(cacheName)) {\n            Set<Object> keys = CacheUtils.keys(cacheName);\n            if (CollUtil.isNotEmpty(keys)) {\n                cacheKeys = keys.stream().map(Object::toString).collect(Collectors.toList());\n            }\n        } else {\n            cacheKeys = RedisUtils.keys(cacheName + \"*\");\n        }\n        return R.ok(cacheKeys);\n    }\n\n    /**\n     * 获取缓存监控缓存值详情\n     *\n     * @param cacheName 缓存名\n     * @param cacheKey  缓存key\n     */\n    @SaCheckPermission(\"monitor:cache:list\")\n    @GetMapping(\"/getValue/{cacheName}/{cacheKey}\")\n    public R<SysCache> getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey) {\n        Object cacheValue;\n        if (isCacheNames(cacheName)) {\n            cacheValue = CacheUtils.get(cacheName, cacheKey);\n        } else {\n            cacheValue = RedisUtils.getCacheObject(cacheKey);\n        }\n        SysCache sysCache = new SysCache(cacheName, cacheKey, JsonUtils.toJsonString(cacheValue));\n        return R.ok(sysCache);\n    }\n\n    /**\n     * 清理缓存监控缓存名\n     *\n     * @param cacheName 缓存名\n     */\n    @SaCheckPermission(\"monitor:cache:list\")\n    @DeleteMapping(\"/clearCacheName/{cacheName}\")\n    public R<Void> clearCacheName(@PathVariable String cacheName) {\n        if (isCacheNames(cacheName)) {\n            CacheUtils.clear(cacheName);\n        } else {\n            RedisUtils.deleteKeys(cacheName + \"*\");\n        }\n        return R.ok();\n    }\n\n    /**\n     * 清理缓存监控Key\n     *\n     * @param cacheKey key名\n     */\n    @SaCheckPermission(\"monitor:cache:list\")\n    @DeleteMapping(\"/clearCacheKey/{cacheName}/{cacheKey}\")\n    public R<Void> clearCacheKey(@PathVariable String cacheName, @PathVariable String cacheKey) {\n        if (isCacheNames(cacheName)) {\n            CacheUtils.evict(cacheName, cacheKey);\n        } else {\n            RedisUtils.deleteObject(cacheKey);\n        }\n        return R.ok();\n    }\n\n    /**\n     * 清理全部缓存监控\n     */\n    @SaCheckPermission(\"monitor:cache:list\")\n    @DeleteMapping(\"/clearCacheAll\")\n    public R<Void> clearCacheAll() {\n        RedisUtils.deleteKeys(\"*\");\n        return R.ok();\n    }\n\n    private boolean isCacheNames(String cacheName) {\n        return !StringUtils.contains(cacheName, \":\");\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/monitor/SysLogininforController.java",
    "content": "package top.flya.web.controller.monitor;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.constant.CacheConstants;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.common.utils.redis.RedisUtils;\nimport top.flya.system.domain.SysLogininfor;\nimport top.flya.system.service.ISysLogininforService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.List;\n\n/**\n * 系统访问记录\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/monitor/logininfor\")\npublic class SysLogininforController extends BaseController {\n\n    private final ISysLogininforService logininforService;\n\n    /**\n     * 获取系统访问记录列表\n     */\n    @SaCheckPermission(\"monitor:logininfor:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<SysLogininfor> list(SysLogininfor logininfor, PageQuery pageQuery) {\n        return logininforService.selectPageLogininforList(logininfor, pageQuery);\n    }\n\n    /**\n     * 导出系统访问记录列表\n     */\n    @Log(title = \"登录日志\", businessType = BusinessType.EXPORT)\n    @SaCheckPermission(\"monitor:logininfor:export\")\n    @PostMapping(\"/export\")\n    public void export(SysLogininfor logininfor, HttpServletResponse response) {\n        List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);\n        ExcelUtil.exportExcel(list, \"登录日志\", SysLogininfor.class, response);\n    }\n\n    /**\n     * 批量删除登录日志\n     * @param infoIds 日志ids\n     */\n    @SaCheckPermission(\"monitor:logininfor:remove\")\n    @Log(title = \"登录日志\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{infoIds}\")\n    public R<Void> remove(@PathVariable Long[] infoIds) {\n        return toAjax(logininforService.deleteLogininforByIds(infoIds));\n    }\n\n    /**\n     * 清理系统访问记录\n     */\n    @SaCheckPermission(\"monitor:logininfor:remove\")\n    @Log(title = \"登录日志\", businessType = BusinessType.CLEAN)\n    @DeleteMapping(\"/clean\")\n    public R<Void> clean() {\n        logininforService.cleanLogininfor();\n        return R.ok();\n    }\n\n    @SaCheckPermission(\"monitor:logininfor:unlock\")\n    @Log(title = \"账户解锁\", businessType = BusinessType.OTHER)\n    @GetMapping(\"/unlock/{userName}\")\n    public R<Void> unlock(@PathVariable(\"userName\") String userName) {\n        String loginName = CacheConstants.PWD_ERR_CNT_KEY + userName;\n        if (RedisUtils.hasKey(loginName)) {\n            RedisUtils.deleteObject(loginName);\n        }\n        return R.ok();\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/monitor/SysOperlogController.java",
    "content": "package top.flya.web.controller.monitor;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.SysOperLog;\nimport top.flya.system.service.ISysOperLogService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.List;\n\n/**\n * 操作日志记录\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/monitor/operlog\")\npublic class SysOperlogController extends BaseController {\n\n    private final ISysOperLogService operLogService;\n\n    /**\n     * 获取操作日志记录列表\n     */\n    @SaCheckPermission(\"monitor:operlog:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<SysOperLog> list(SysOperLog operLog, PageQuery pageQuery) {\n        return operLogService.selectPageOperLogList(operLog, pageQuery);\n    }\n\n    /**\n     * 导出操作日志记录列表\n     */\n    @Log(title = \"操作日志\", businessType = BusinessType.EXPORT)\n    @SaCheckPermission(\"monitor:operlog:export\")\n    @PostMapping(\"/export\")\n    public void export(SysOperLog operLog, HttpServletResponse response) {\n        List<SysOperLog> list = operLogService.selectOperLogList(operLog);\n        ExcelUtil.exportExcel(list, \"操作日志\", SysOperLog.class, response);\n    }\n\n    /**\n     * 批量删除操作日志记录\n     * @param operIds 日志ids\n     */\n    @Log(title = \"操作日志\", businessType = BusinessType.DELETE)\n    @SaCheckPermission(\"monitor:operlog:remove\")\n    @DeleteMapping(\"/{operIds}\")\n    public R<Void> remove(@PathVariable Long[] operIds) {\n        return toAjax(operLogService.deleteOperLogByIds(operIds));\n    }\n\n    /**\n     * 清理操作日志记录\n     */\n    @Log(title = \"操作日志\", businessType = BusinessType.CLEAN)\n    @SaCheckPermission(\"monitor:operlog:remove\")\n    @DeleteMapping(\"/clean\")\n    public R<Void> clean() {\n        operLogService.cleanOperLog();\n        return R.ok();\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/monitor/SysUserOnlineController.java",
    "content": "package top.flya.web.controller.monitor;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport cn.dev33.satoken.exception.NotLoginException;\nimport cn.dev33.satoken.stp.StpUtil;\nimport cn.hutool.core.bean.BeanUtil;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.constant.CacheConstants;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.domain.dto.UserOnlineDTO;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.StreamUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.redis.RedisUtils;\nimport top.flya.system.domain.SysUserOnline;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * 在线用户监控\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/monitor/online\")\npublic class SysUserOnlineController extends BaseController {\n\n    /**\n     * 获取在线用户监控列表\n     *\n     * @param ipaddr   IP地址\n     * @param userName 用户名\n     */\n    @SaCheckPermission(\"monitor:online:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<SysUserOnline> list(String ipaddr, String userName) {\n        // 获取所有未过期的 token\n        List<String> keys = StpUtil.searchTokenValue(\"\", 0, -1, false);\n        List<UserOnlineDTO> userOnlineDTOList = new ArrayList<>();\n        for (String key : keys) {\n            String token = StringUtils.substringAfterLast(key, \":\");\n            // 如果已经过期则跳过\n            if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {\n                continue;\n            }\n            userOnlineDTOList.add(RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token));\n        }\n        if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {\n            userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->\n                StringUtils.equals(ipaddr, userOnline.getIpaddr()) &&\n                    StringUtils.equals(userName, userOnline.getUserName())\n            );\n        } else if (StringUtils.isNotEmpty(ipaddr)) {\n            userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->\n                StringUtils.equals(ipaddr, userOnline.getIpaddr())\n            );\n        } else if (StringUtils.isNotEmpty(userName)) {\n            userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->\n                StringUtils.equals(userName, userOnline.getUserName())\n            );\n        }\n        Collections.reverse(userOnlineDTOList);\n        userOnlineDTOList.removeAll(Collections.singleton(null));\n        List<SysUserOnline> userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class);\n        return TableDataInfo.build(userOnlineList);\n    }\n\n    /**\n     * 强退用户\n     *\n     * @param tokenId token值\n     */\n    @SaCheckPermission(\"monitor:online:forceLogout\")\n    @Log(title = \"在线用户\", businessType = BusinessType.FORCE)\n    @DeleteMapping(\"/{tokenId}\")\n    public R<Void> forceLogout(@PathVariable String tokenId) {\n        try {\n            StpUtil.kickoutByTokenValue(tokenId);\n        } catch (NotLoginException ignored) {\n        }\n        return R.ok();\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/system/SysConfigController.java",
    "content": "package top.flya.web.controller.system;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.SysConfig;\nimport top.flya.system.service.ISysConfigService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.List;\n\n/**\n * 参数配置 信息操作处理\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/config\")\npublic class SysConfigController extends BaseController {\n\n    private final ISysConfigService configService;\n\n    /**\n     * 获取参数配置列表\n     */\n    @SaCheckPermission(\"system:config:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<SysConfig> list(SysConfig config, PageQuery pageQuery) {\n        return configService.selectPageConfigList(config, pageQuery);\n    }\n\n    /**\n     * 导出参数配置列表\n     */\n    @Log(title = \"参数管理\", businessType = BusinessType.EXPORT)\n    @SaCheckPermission(\"system:config:export\")\n    @PostMapping(\"/export\")\n    public void export(SysConfig config, HttpServletResponse response) {\n        List<SysConfig> list = configService.selectConfigList(config);\n        ExcelUtil.exportExcel(list, \"参数数据\", SysConfig.class, response);\n    }\n\n    /**\n     * 根据参数编号获取详细信息\n     *\n     * @param configId 参数ID\n     */\n    @SaCheckPermission(\"system:config:query\")\n    @GetMapping(value = \"/{configId}\")\n    public R<SysConfig> getInfo(@PathVariable Long configId) {\n        return R.ok(configService.selectConfigById(configId));\n    }\n\n    /**\n     * 根据参数键名查询参数值\n     *\n     * @param configKey 参数Key\n     */\n    @GetMapping(value = \"/configKey/{configKey}\")\n    public R<Void> getConfigKey(@PathVariable String configKey) {\n        return R.ok(configService.selectConfigByKey(configKey));\n    }\n\n    /**\n     * 新增参数配置\n     */\n    @SaCheckPermission(\"system:config:add\")\n    @Log(title = \"参数管理\", businessType = BusinessType.INSERT)\n    @PostMapping\n    public R<Void> add(@Validated @RequestBody SysConfig config) {\n        if (!configService.checkConfigKeyUnique(config)) {\n            return R.fail(\"新增参数'\" + config.getConfigName() + \"'失败，参数键名已存在\");\n        }\n        configService.insertConfig(config);\n        return R.ok();\n    }\n\n    /**\n     * 修改参数配置\n     */\n    @SaCheckPermission(\"system:config:edit\")\n    @Log(title = \"参数管理\", businessType = BusinessType.UPDATE)\n    @PutMapping\n    public R<Void> edit(@Validated @RequestBody SysConfig config) {\n        if (!configService.checkConfigKeyUnique(config)) {\n            return R.fail(\"修改参数'\" + config.getConfigName() + \"'失败，参数键名已存在\");\n        }\n        configService.updateConfig(config);\n        return R.ok();\n    }\n\n    /**\n     * 根据参数键名修改参数配置\n     */\n    @SaCheckPermission(\"system:config:edit\")\n    @Log(title = \"参数管理\", businessType = BusinessType.UPDATE)\n    @PutMapping(\"/updateByKey\")\n    public R<Void> updateByKey(@RequestBody SysConfig config) {\n        configService.updateConfig(config);\n        return R.ok();\n    }\n\n    /**\n     * 删除参数配置\n     *\n     * @param configIds 参数ID串\n     */\n    @SaCheckPermission(\"system:config:remove\")\n    @Log(title = \"参数管理\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{configIds}\")\n    public R<Void> remove(@PathVariable Long[] configIds) {\n        configService.deleteConfigByIds(configIds);\n        return R.ok();\n    }\n\n    /**\n     * 刷新参数缓存\n     */\n    @SaCheckPermission(\"system:config:remove\")\n    @Log(title = \"参数管理\", businessType = BusinessType.CLEAN)\n    @DeleteMapping(\"/refreshCache\")\n    public R<Void> refreshCache() {\n        configService.resetConfigCache();\n        return R.ok();\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/system/SysDeptController.java",
    "content": "package top.flya.web.controller.system;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport cn.hutool.core.convert.Convert;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.domain.entity.SysDept;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.system.service.ISysDeptService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n\n/**\n * 部门信息\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/dept\")\npublic class SysDeptController extends BaseController {\n\n    private final ISysDeptService deptService;\n\n    /**\n     * 获取部门列表\n     */\n    @SaCheckPermission(\"system:dept:list\")\n    @GetMapping(\"/list\")\n    public R<List<SysDept>> list(SysDept dept) {\n        List<SysDept> depts = deptService.selectDeptList(dept);\n        return R.ok(depts);\n    }\n\n    /**\n     * 查询部门列表（排除节点）\n     *\n     * @param deptId 部门ID\n     */\n    @SaCheckPermission(\"system:dept:list\")\n    @GetMapping(\"/list/exclude/{deptId}\")\n    public R<List<SysDept>> excludeChild(@PathVariable(value = \"deptId\", required = false) Long deptId) {\n        List<SysDept> depts = deptService.selectDeptList(new SysDept());\n        depts.removeIf(d -> d.getDeptId().equals(deptId)\n            || StringUtils.splitList(d.getAncestors()).contains(Convert.toStr(deptId)));\n        return R.ok(depts);\n    }\n\n    /**\n     * 根据部门编号获取详细信息\n     *\n     * @param deptId 部门ID\n     */\n    @SaCheckPermission(\"system:dept:query\")\n    @GetMapping(value = \"/{deptId}\")\n    public R<SysDept> getInfo(@PathVariable Long deptId) {\n        deptService.checkDeptDataScope(deptId);\n        return R.ok(deptService.selectDeptById(deptId));\n    }\n\n    /**\n     * 新增部门\n     */\n    @SaCheckPermission(\"system:dept:add\")\n    @Log(title = \"部门管理\", businessType = BusinessType.INSERT)\n    @PostMapping\n    public R<Void> add(@Validated @RequestBody SysDept dept) {\n        if (!deptService.checkDeptNameUnique(dept)) {\n            return R.fail(\"新增部门'\" + dept.getDeptName() + \"'失败，部门名称已存在\");\n        }\n        return toAjax(deptService.insertDept(dept));\n    }\n\n    /**\n     * 修改部门\n     */\n    @SaCheckPermission(\"system:dept:edit\")\n    @Log(title = \"部门管理\", businessType = BusinessType.UPDATE)\n    @PutMapping\n    public R<Void> edit(@Validated @RequestBody SysDept dept) {\n        Long deptId = dept.getDeptId();\n        deptService.checkDeptDataScope(deptId);\n        if (!deptService.checkDeptNameUnique(dept)) {\n            return R.fail(\"修改部门'\" + dept.getDeptName() + \"'失败，部门名称已存在\");\n        } else if (dept.getParentId().equals(deptId)) {\n            return R.fail(\"修改部门'\" + dept.getDeptName() + \"'失败，上级部门不能是自己\");\n        } else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())\n            && deptService.selectNormalChildrenDeptById(deptId) > 0) {\n            return R.fail(\"该部门包含未停用的子部门！\");\n        }\n        return toAjax(deptService.updateDept(dept));\n    }\n\n    /**\n     * 删除部门\n     *\n     * @param deptId 部门ID\n     */\n    @SaCheckPermission(\"system:dept:remove\")\n    @Log(title = \"部门管理\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{deptId}\")\n    public R<Void> remove(@PathVariable Long deptId) {\n        if (deptService.hasChildByDeptId(deptId)) {\n            return R.warn(\"存在下级部门,不允许删除\");\n        }\n        if (deptService.checkDeptExistUser(deptId)) {\n            return R.warn(\"部门存在用户,不允许删除\");\n        }\n        deptService.checkDeptDataScope(deptId);\n        return toAjax(deptService.deleteDeptById(deptId));\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/system/SysDictDataController.java",
    "content": "package top.flya.web.controller.system;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport cn.hutool.core.util.ObjectUtil;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.domain.entity.SysDictData;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.service.ISysDictDataService;\nimport top.flya.system.service.ISysDictTypeService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 数据字典信息\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/dict/data\")\npublic class SysDictDataController extends BaseController {\n\n    private final ISysDictDataService dictDataService;\n    private final ISysDictTypeService dictTypeService;\n\n    /**\n     * 查询字典数据列表\n     */\n    // @SaCheckPermission(\"system:dict:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<SysDictData> list(SysDictData dictData, PageQuery pageQuery) {\n        return dictDataService.selectPageDictDataList(dictData, pageQuery);\n    }\n\n    /**\n     * 导出字典数据列表\n     */\n    @Log(title = \"字典数据\", businessType = BusinessType.EXPORT)\n     @SaCheckPermission(\"system:dict:export\")\n    @PostMapping(\"/export\")\n    public void export(SysDictData dictData, HttpServletResponse response) {\n        List<SysDictData> list = dictDataService.selectDictDataList(dictData);\n        ExcelUtil.exportExcel(list, \"字典数据\", SysDictData.class, response);\n    }\n\n    /**\n     * 查询字典数据详细\n     *\n     * @param dictCode 字典code\n     */\n    // @SaCheckPermission(\"system:dict:query\")\n    @GetMapping(value = \"/{dictCode}\")\n    public R<SysDictData> getInfo(@PathVariable Long dictCode) {\n        return R.ok(dictDataService.selectDictDataById(dictCode));\n    }\n\n    /**\n     * 根据字典类型查询字典数据信息\n     *\n     * @param dictType 字典类型\n     */\n    @GetMapping(value = \"/type/{dictType}\")\n    public R<List<SysDictData>> dictType(@PathVariable String dictType) {\n        List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);\n        if (ObjectUtil.isNull(data)) {\n            data = new ArrayList<>();\n        }\n        return R.ok(data);\n    }\n\n    /**\n     * 新增字典类型\n     */\n     @SaCheckPermission(\"system:dict:add\")\n    @Log(title = \"字典数据\", businessType = BusinessType.INSERT)\n    @PostMapping\n    public R<Void> add(@Validated @RequestBody SysDictData dict) {\n        dictDataService.insertDictData(dict);\n        return R.ok();\n    }\n\n    /**\n     * 修改保存字典类型\n     */\n     @SaCheckPermission(\"system:dict:edit\")\n    @Log(title = \"字典数据\", businessType = BusinessType.UPDATE)\n    @PutMapping\n    public R<Void> edit(@Validated @RequestBody SysDictData dict) {\n        dictDataService.updateDictData(dict);\n        return R.ok();\n    }\n\n    /**\n     * 删除字典类型\n     *\n     * @param dictCodes 字典code串\n     */\n     @SaCheckPermission(\"system:dict:remove\")\n    @Log(title = \"字典类型\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{dictCodes}\")\n    public R<Void> remove(@PathVariable Long[] dictCodes) {\n        dictDataService.deleteDictDataByIds(dictCodes);\n        return R.ok();\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/system/SysDictTypeController.java",
    "content": "package top.flya.web.controller.system;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.domain.entity.SysDictType;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.service.ISysDictTypeService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.List;\n\n/**\n * 数据字典信息\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/dict/type\")\npublic class SysDictTypeController extends BaseController {\n\n    private final ISysDictTypeService dictTypeService;\n\n    /**\n     * 查询字典类型列表\n     */\n    @SaCheckPermission(\"system:dict:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<SysDictType> list(SysDictType dictType, PageQuery pageQuery) {\n        return dictTypeService.selectPageDictTypeList(dictType, pageQuery);\n    }\n\n    /**\n     * 导出字典类型列表\n     */\n    @Log(title = \"字典类型\", businessType = BusinessType.EXPORT)\n    @SaCheckPermission(\"system:dict:export\")\n    @PostMapping(\"/export\")\n    public void export(SysDictType dictType, HttpServletResponse response) {\n        List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);\n        ExcelUtil.exportExcel(list, \"字典类型\", SysDictType.class, response);\n    }\n\n    /**\n     * 查询字典类型详细\n     *\n     * @param dictId 字典ID\n     */\n    @SaCheckPermission(\"system:dict:query\")\n    @GetMapping(value = \"/{dictId}\")\n    public R<SysDictType> getInfo(@PathVariable Long dictId) {\n        return R.ok(dictTypeService.selectDictTypeById(dictId));\n    }\n\n    /**\n     * 新增字典类型\n     */\n    @SaCheckPermission(\"system:dict:add\")\n    @Log(title = \"字典类型\", businessType = BusinessType.INSERT)\n    @PostMapping\n    public R<Void> add(@Validated @RequestBody SysDictType dict) {\n        if (!dictTypeService.checkDictTypeUnique(dict)) {\n            return R.fail(\"新增字典'\" + dict.getDictName() + \"'失败，字典类型已存在\");\n        }\n        dictTypeService.insertDictType(dict);\n        return R.ok();\n    }\n\n    /**\n     * 修改字典类型\n     */\n    @SaCheckPermission(\"system:dict:edit\")\n    @Log(title = \"字典类型\", businessType = BusinessType.UPDATE)\n    @PutMapping\n    public R<Void> edit(@Validated @RequestBody SysDictType dict) {\n        if (!dictTypeService.checkDictTypeUnique(dict)) {\n            return R.fail(\"修改字典'\" + dict.getDictName() + \"'失败，字典类型已存在\");\n        }\n        dictTypeService.updateDictType(dict);\n        return R.ok();\n    }\n\n    /**\n     * 删除字典类型\n     *\n     * @param dictIds 字典ID串\n     */\n    @SaCheckPermission(\"system:dict:remove\")\n    @Log(title = \"字典类型\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{dictIds}\")\n    public R<Void> remove(@PathVariable Long[] dictIds) {\n        dictTypeService.deleteDictTypeByIds(dictIds);\n        return R.ok();\n    }\n\n    /**\n     * 刷新字典缓存\n     */\n    @SaCheckPermission(\"system:dict:remove\")\n    @Log(title = \"字典类型\", businessType = BusinessType.CLEAN)\n    @DeleteMapping(\"/refreshCache\")\n    public R<Void> refreshCache() {\n        dictTypeService.resetDictCache();\n        return R.ok();\n    }\n\n    /**\n     * 获取字典选择框列表\n     */\n    @GetMapping(\"/optionselect\")\n    public R<List<SysDictType>> optionselect() {\n        List<SysDictType> dictTypes = dictTypeService.selectDictTypeAll();\n        return R.ok(dictTypes);\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/system/SysIndexController.java",
    "content": "package top.flya.web.controller.system;\n\nimport cn.dev33.satoken.annotation.SaIgnore;\nimport top.flya.common.config.RuoYiConfig;\nimport top.flya.common.utils.StringUtils;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 首页\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@RestController\npublic class SysIndexController {\n\n    /**\n     * 系统基础配置\n     */\n    private final RuoYiConfig ruoyiConfig;\n\n    /**\n     * 访问首页，提示语\n     */\n    @SaIgnore\n    @GetMapping(\"/\")\n    public String index() {\n        return StringUtils.format(\"欢迎使用{}后台管理框架，当前版本：v{}，请通过前端地址访问。\", ruoyiConfig.getName(), ruoyiConfig.getVersion());\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/system/SysLoginController.java",
    "content": "package top.flya.web.controller.system;\n\nimport cn.dev33.satoken.annotation.SaIgnore;\nimport top.flya.common.constant.Constants;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.domain.entity.SysMenu;\nimport top.flya.common.core.domain.entity.SysUser;\nimport top.flya.common.core.domain.model.EmailLoginBody;\nimport top.flya.common.core.domain.model.LoginBody;\nimport top.flya.common.core.domain.model.LoginUser;\nimport top.flya.common.core.domain.model.SmsLoginBody;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.system.domain.bo.PzcUserBo;\nimport top.flya.system.domain.vo.RouterVo;\nimport top.flya.system.service.ISysMenuService;\nimport top.flya.system.service.ISysUserService;\nimport top.flya.system.service.SysLoginService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.validation.constraints.NotBlank;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 登录验证\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\npublic class SysLoginController {\n\n    private final SysLoginService loginService;\n    private final ISysMenuService menuService;\n    private final ISysUserService userService;\n\n    /**\n     * 登录方法\n     *\n     * @param loginBody 登录信息\n     * @return 结果\n     */\n    @SaIgnore\n    @PostMapping(\"/login\")\n    public R<Map<String, Object>> login(@Validated @RequestBody LoginBody loginBody) {\n        Map<String, Object> ajax = new HashMap<>();\n        // 生成令牌\n        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),\n            loginBody.getUuid());\n        ajax.put(Constants.TOKEN, token);\n        return R.ok(ajax);\n    }\n\n    /**\n     * 短信登录\n     *\n     * @param smsLoginBody 登录信息\n     * @return 结果\n     */\n    @SaIgnore\n    @PostMapping(\"/smsLogin\")\n    public R<Map<String, Object>> smsLogin(@Validated @RequestBody SmsLoginBody smsLoginBody) {\n        Map<String, Object> ajax = new HashMap<>();\n        // 生成令牌\n        String token = loginService.smsLogin(smsLoginBody.getPhonenumber(), smsLoginBody.getSmsCode());\n        ajax.put(Constants.TOKEN, token);\n        return R.ok(ajax);\n    }\n\n    /**\n     * 邮件登录\n     *\n     * @param body 登录信息\n     * @return 结果\n     */\n    @PostMapping(\"/emailLogin\")\n    public R<Map<String, Object>> emailLogin(@Validated @RequestBody EmailLoginBody body) {\n        Map<String, Object> ajax = new HashMap<>();\n        // 生成令牌\n        String token = loginService.emailLogin(body.getEmail(), body.getEmailCode());\n        ajax.put(Constants.TOKEN, token);\n        return R.ok(ajax);\n    }\n\n    /**\n     * @return 结果\n     */\n    @SaIgnore\n    @PostMapping(\"/xcxLogin\")\n    public R<Map<String, Object>> xcxLogin(@RequestBody PzcUserBo pzcUserBo) {\n        Map<String, Object> ajax = new HashMap<>();\n        // 生成令牌\n        String token = loginService.xcxLogin(pzcUserBo.getLoginCode());\n        ajax.put(Constants.TOKEN, token);\n        return R.ok(ajax);\n    }\n\n    /**\n     * 退出登录\n     */\n    @SaIgnore\n    @PostMapping(\"/logout\")\n    public R<Void> logout() {\n        loginService.logout();\n        return R.ok(\"退出成功\");\n    }\n\n    /**\n     * 获取用户信息\n     *\n     * @return 用户信息\n     */\n    @GetMapping(\"getInfo\")\n    public R<Map<String, Object>> getInfo() {\n        LoginUser loginUser = LoginHelper.getLoginUser();\n        SysUser user = userService.selectUserById(loginUser.getUserId());\n        Map<String, Object> ajax = new HashMap<>();\n        ajax.put(\"user\", user);\n        ajax.put(\"roles\", loginUser.getRolePermission());\n        ajax.put(\"permissions\", loginUser.getMenuPermission());\n        return R.ok(ajax);\n    }\n\n    /**\n     * 获取路由信息\n     *\n     * @return 路由信息\n     */\n    @GetMapping(\"getRouters\")\n    public R<List<RouterVo>> getRouters() {\n        Long userId = LoginHelper.getUserId();\n        List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);\n        return R.ok(menuService.buildMenus(menus));\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/system/SysMenuController.java",
    "content": "package top.flya.web.controller.system;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport cn.hutool.core.lang.tree.Tree;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.domain.entity.SysMenu;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.system.service.ISysMenuService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 菜单信息\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/menu\")\npublic class SysMenuController extends BaseController {\n\n    private final ISysMenuService menuService;\n\n    /**\n     * 获取菜单列表\n     */\n    @SaCheckPermission(\"system:menu:list\")\n    @GetMapping(\"/list\")\n    public R<List<SysMenu>> list(SysMenu menu) {\n        List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());\n        return R.ok(menus);\n    }\n\n    /**\n     * 根据菜单编号获取详细信息\n     *\n     * @param menuId 菜单ID\n     */\n    @SaCheckPermission(\"system:menu:query\")\n    @GetMapping(value = \"/{menuId}\")\n    public R<SysMenu> getInfo(@PathVariable Long menuId) {\n        return R.ok(menuService.selectMenuById(menuId));\n    }\n\n    /**\n     * 获取菜单下拉树列表\n     */\n    @GetMapping(\"/treeselect\")\n    public R<List<Tree<Long>>> treeselect(SysMenu menu) {\n        List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());\n        return R.ok(menuService.buildMenuTreeSelect(menus));\n    }\n\n    /**\n     * 加载对应角色菜单列表树\n     *\n     * @param roleId 角色ID\n     */\n    @GetMapping(value = \"/roleMenuTreeselect/{roleId}\")\n    public R<Map<String, Object>> roleMenuTreeselect(@PathVariable(\"roleId\") Long roleId) {\n        List<SysMenu> menus = menuService.selectMenuList(getUserId());\n        Map<String, Object> ajax = new HashMap<>();\n        ajax.put(\"checkedKeys\", menuService.selectMenuListByRoleId(roleId));\n        ajax.put(\"menus\", menuService.buildMenuTreeSelect(menus));\n        return R.ok(ajax);\n    }\n\n    /**\n     * 新增菜单\n     */\n    @SaCheckPermission(\"system:menu:add\")\n    @Log(title = \"菜单管理\", businessType = BusinessType.INSERT)\n    @PostMapping\n    public R<Void> add(@Validated @RequestBody SysMenu menu) {\n        if (!menuService.checkMenuNameUnique(menu)) {\n            return R.fail(\"新增菜单'\" + menu.getMenuName() + \"'失败，菜单名称已存在\");\n        } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {\n            return R.fail(\"新增菜单'\" + menu.getMenuName() + \"'失败，地址必须以http(s)://开头\");\n        }\n        return toAjax(menuService.insertMenu(menu));\n    }\n\n    /**\n     * 修改菜单\n     */\n    @SaCheckPermission(\"system:menu:edit\")\n    @Log(title = \"菜单管理\", businessType = BusinessType.UPDATE)\n    @PutMapping\n    public R<Void> edit(@Validated @RequestBody SysMenu menu) {\n        if (!menuService.checkMenuNameUnique(menu)) {\n            return R.fail(\"修改菜单'\" + menu.getMenuName() + \"'失败，菜单名称已存在\");\n        } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {\n            return R.fail(\"修改菜单'\" + menu.getMenuName() + \"'失败，地址必须以http(s)://开头\");\n        } else if (menu.getMenuId().equals(menu.getParentId())) {\n            return R.fail(\"修改菜单'\" + menu.getMenuName() + \"'失败，上级菜单不能选择自己\");\n        }\n        return toAjax(menuService.updateMenu(menu));\n    }\n\n    /**\n     * 删除菜单\n     *\n     * @param menuId 菜单ID\n     */\n    @SaCheckPermission(\"system:menu:remove\")\n    @Log(title = \"菜单管理\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{menuId}\")\n    public R<Void> remove(@PathVariable(\"menuId\") Long menuId) {\n        if (menuService.hasChildByMenuId(menuId)) {\n            return R.warn(\"存在子菜单,不允许删除\");\n        }\n        if (menuService.checkMenuExistRole(menuId)) {\n            return R.warn(\"菜单已分配,不允许删除\");\n        }\n        return toAjax(menuService.deleteMenuById(menuId));\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/system/SysNoticeController.java",
    "content": "package top.flya.web.controller.system;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.system.domain.SysNotice;\nimport top.flya.system.service.ISysNoticeService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\n/**\n * 公告 信息操作处理\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/notice\")\npublic class SysNoticeController extends BaseController {\n\n    private final ISysNoticeService noticeService;\n\n    /**\n     * 获取通知公告列表\n     */\n    @SaCheckPermission(\"system:notice:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<SysNotice> list(SysNotice notice, PageQuery pageQuery) {\n        return noticeService.selectPageNoticeList(notice, pageQuery);\n    }\n\n    /**\n     * 根据通知公告编号获取详细信息\n     *\n     * @param noticeId 公告ID\n     */\n    @SaCheckPermission(\"system:notice:query\")\n    @GetMapping(value = \"/{noticeId}\")\n    public R<SysNotice> getInfo(@PathVariable Long noticeId) {\n        return R.ok(noticeService.selectNoticeById(noticeId));\n    }\n\n    /**\n     * 新增通知公告\n     */\n    @SaCheckPermission(\"system:notice:add\")\n    @Log(title = \"通知公告\", businessType = BusinessType.INSERT)\n    @PostMapping\n    public R<Void> add(@Validated @RequestBody SysNotice notice) {\n        return toAjax(noticeService.insertNotice(notice));\n    }\n\n    /**\n     * 修改通知公告\n     */\n    @SaCheckPermission(\"system:notice:edit\")\n    @Log(title = \"通知公告\", businessType = BusinessType.UPDATE)\n    @PutMapping\n    public R<Void> edit(@Validated @RequestBody SysNotice notice) {\n        return toAjax(noticeService.updateNotice(notice));\n    }\n\n    /**\n     * 删除通知公告\n     *\n     * @param noticeIds 公告ID串\n     */\n    @SaCheckPermission(\"system:notice:remove\")\n    @Log(title = \"通知公告\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{noticeIds}\")\n    public R<Void> remove(@PathVariable Long[] noticeIds) {\n        return toAjax(noticeService.deleteNoticeByIds(noticeIds));\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/system/SysOssConfigController.java",
    "content": "package top.flya.web.controller.system;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.core.validate.QueryGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.system.domain.bo.SysOssConfigBo;\nimport top.flya.system.domain.vo.SysOssConfigVo;\nimport top.flya.system.service.ISysOssConfigService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.Arrays;\n\n/**\n * 对象存储配置\n *\n * @author Lion Li\n * @author 孤舟烟雨\n * @date 2021-08-13\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/oss/config\")\npublic class SysOssConfigController extends BaseController {\n\n    private final ISysOssConfigService iSysOssConfigService;\n\n    /**\n     * 查询对象存储配置列表\n     */\n    @SaCheckPermission(\"system:oss:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<SysOssConfigVo> list(@Validated(QueryGroup.class) SysOssConfigBo bo, PageQuery pageQuery) {\n        return iSysOssConfigService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 获取对象存储配置详细信息\n     *\n     * @param ossConfigId OSS配置ID\n     */\n    @SaCheckPermission(\"system:oss:query\")\n    @GetMapping(\"/{ossConfigId}\")\n    public R<SysOssConfigVo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable Long ossConfigId) {\n        return R.ok(iSysOssConfigService.queryById(ossConfigId));\n    }\n\n    /**\n     * 新增对象存储配置\n     */\n    @SaCheckPermission(\"system:oss:add\")\n    @Log(title = \"对象存储配置\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody SysOssConfigBo bo) {\n        return toAjax(iSysOssConfigService.insertByBo(bo));\n    }\n\n    /**\n     * 修改对象存储配置\n     */\n    @SaCheckPermission(\"system:oss:edit\")\n    @Log(title = \"对象存储配置\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysOssConfigBo bo) {\n        return toAjax(iSysOssConfigService.updateByBo(bo));\n    }\n\n    /**\n     * 删除对象存储配置\n     *\n     * @param ossConfigIds OSS配置ID串\n     */\n    @SaCheckPermission(\"system:oss:remove\")\n    @Log(title = \"对象存储配置\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{ossConfigIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Long[] ossConfigIds) {\n        return toAjax(iSysOssConfigService.deleteWithValidByIds(Arrays.asList(ossConfigIds), true));\n    }\n\n    /**\n     * 状态修改\n     */\n    @SaCheckPermission(\"system:oss:edit\")\n    @Log(title = \"对象存储状态修改\", businessType = BusinessType.UPDATE)\n    @PutMapping(\"/changeStatus\")\n    public R<Void> changeStatus(@RequestBody SysOssConfigBo bo) {\n        return toAjax(iSysOssConfigService.updateOssConfigStatus(bo));\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/system/SysOssController.java",
    "content": "package top.flya.web.controller.system;\n\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport cn.hutool.core.util.ObjectUtil;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.validate.QueryGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.system.domain.bo.SysOssBo;\nimport top.flya.system.domain.vo.SysOssVo;\nimport top.flya.system.service.ISysOssService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.http.MediaType;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.NotEmpty;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 文件上传 控制层\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/oss\")\npublic class SysOssController extends BaseController {\n\n    private final ISysOssService iSysOssService;\n\n    /**\n     * 查询OSS对象存储列表\n     */\n    @SaCheckPermission(\"system:oss:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<SysOssVo> list(@Validated(QueryGroup.class) SysOssBo bo, PageQuery pageQuery) {\n        return iSysOssService.queryPageList(bo, pageQuery);\n    }\n\n    /**\n     * 查询OSS对象基于id串\n     *\n     * @param ossIds OSS对象ID串\n     */\n    @SaCheckPermission(\"system:oss:list\")\n    @GetMapping(\"/listByIds/{ossIds}\")\n    public R<List<SysOssVo>> listByIds(@NotEmpty(message = \"主键不能为空\")\n                                       @PathVariable Long[] ossIds) {\n        List<SysOssVo> list = iSysOssService.listByIds(Arrays.asList(ossIds));\n        return R.ok(list);\n    }\n\n    /**\n     * 上传OSS对象存储\n     *\n     * @param file 文件\n     */\n//    @SaCheckPermission(\"system:oss:upload\")\n    @Log(title = \"OSS对象存储\", businessType = BusinessType.INSERT)\n    @PostMapping(value = \"/upload\", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)\n    public R<Map<String, String>> upload(@RequestPart(\"file\") MultipartFile file) {\n        if (ObjectUtil.isNull(file)) {\n            return R.fail(\"上传文件不能为空\");\n        }\n        SysOssVo oss = iSysOssService.upload(file);\n        Map<String, String> map = new HashMap<>(2);\n        map.put(\"url\", oss.getUrl());\n        map.put(\"fileName\", oss.getOriginalName());\n        map.put(\"ossId\", oss.getOssId().toString());\n        return R.ok(map);\n    }\n\n    /**\n     * 下载OSS对象\n     *\n     * @param ossId OSS对象ID\n     */\n    @SaCheckPermission(\"system:oss:download\")\n    @GetMapping(\"/download/{ossId}\")\n    public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {\n        iSysOssService.download(ossId,response);\n    }\n\n    /**\n     * 删除OSS对象存储\n     *\n     * @param ossIds OSS对象ID串\n     */\n    @SaCheckPermission(\"system:oss:remove\")\n    @Log(title = \"OSS对象存储\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{ossIds}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable Long[] ossIds) {\n        return toAjax(iSysOssService.deleteWithValidByIds(Arrays.asList(ossIds), true));\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/system/SysPostController.java",
    "content": "package top.flya.web.controller.system;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.SysPost;\nimport top.flya.system.service.ISysPostService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.List;\n\n/**\n * 岗位信息操作处理\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/post\")\npublic class SysPostController extends BaseController {\n\n    private final ISysPostService postService;\n\n    /**\n     * 获取岗位列表\n     */\n    @SaCheckPermission(\"system:post:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<SysPost> list(SysPost post, PageQuery pageQuery) {\n        return postService.selectPagePostList(post, pageQuery);\n    }\n\n    /**\n     * 导出岗位列表\n     */\n    @Log(title = \"岗位管理\", businessType = BusinessType.EXPORT)\n    @SaCheckPermission(\"system:post:export\")\n    @PostMapping(\"/export\")\n    public void export(SysPost post, HttpServletResponse response) {\n        List<SysPost> list = postService.selectPostList(post);\n        ExcelUtil.exportExcel(list, \"岗位数据\", SysPost.class, response);\n    }\n\n    /**\n     * 根据岗位编号获取详细信息\n     *\n     * @param postId 岗位ID\n     */\n    @SaCheckPermission(\"system:post:query\")\n    @GetMapping(value = \"/{postId}\")\n    public R<SysPost> getInfo(@PathVariable Long postId) {\n        return R.ok(postService.selectPostById(postId));\n    }\n\n    /**\n     * 新增岗位\n     */\n    @SaCheckPermission(\"system:post:add\")\n    @Log(title = \"岗位管理\", businessType = BusinessType.INSERT)\n    @PostMapping\n    public R<Void> add(@Validated @RequestBody SysPost post) {\n        if (!postService.checkPostNameUnique(post)) {\n            return R.fail(\"新增岗位'\" + post.getPostName() + \"'失败，岗位名称已存在\");\n        } else if (!postService.checkPostCodeUnique(post)) {\n            return R.fail(\"新增岗位'\" + post.getPostName() + \"'失败，岗位编码已存在\");\n        }\n        return toAjax(postService.insertPost(post));\n    }\n\n    /**\n     * 修改岗位\n     */\n    @SaCheckPermission(\"system:post:edit\")\n    @Log(title = \"岗位管理\", businessType = BusinessType.UPDATE)\n    @PutMapping\n    public R<Void> edit(@Validated @RequestBody SysPost post) {\n        if (!postService.checkPostNameUnique(post)) {\n            return R.fail(\"修改岗位'\" + post.getPostName() + \"'失败，岗位名称已存在\");\n        } else if (!postService.checkPostCodeUnique(post)) {\n            return R.fail(\"修改岗位'\" + post.getPostName() + \"'失败，岗位编码已存在\");\n        }\n        return toAjax(postService.updatePost(post));\n    }\n\n    /**\n     * 删除岗位\n     *\n     * @param postIds 岗位ID串\n     */\n    @SaCheckPermission(\"system:post:remove\")\n    @Log(title = \"岗位管理\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{postIds}\")\n    public R<Void> remove(@PathVariable Long[] postIds) {\n        return toAjax(postService.deletePostByIds(postIds));\n    }\n\n    /**\n     * 获取岗位选择框列表\n     */\n    @GetMapping(\"/optionselect\")\n    public R<List<SysPost>> optionselect() {\n        List<SysPost> posts = postService.selectPostAll();\n        return R.ok(posts);\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/system/SysProfileController.java",
    "content": "package top.flya.web.controller.system;\n\nimport cn.dev33.satoken.secure.BCrypt;\nimport cn.hutool.core.io.FileUtil;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.domain.entity.SysUser;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.file.MimeTypeUtils;\nimport top.flya.system.domain.SysOss;\nimport top.flya.system.domain.vo.SysOssVo;\nimport top.flya.system.service.ISysOssService;\nimport top.flya.system.service.ISysUserService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.http.MediaType;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 个人信息 业务处理\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/user/profile\")\npublic class SysProfileController extends BaseController {\n\n    private final ISysUserService userService;\n    private final ISysOssService iSysOssService;\n\n    /**\n     * 个人信息\n     */\n    @GetMapping\n    public R<Map<String, Object>> profile() {\n        SysUser user = userService.selectUserById(getUserId());\n        Map<String, Object> ajax = new HashMap<>();\n        ajax.put(\"user\", user);\n        ajax.put(\"roleGroup\", userService.selectUserRoleGroup(user.getUserName()));\n        ajax.put(\"postGroup\", userService.selectUserPostGroup(user.getUserName()));\n        return R.ok(ajax);\n    }\n\n    /**\n     * 修改用户\n     */\n    @Log(title = \"个人信息\", businessType = BusinessType.UPDATE)\n    @PutMapping\n    public R<Void> updateProfile(@RequestBody SysUser user) {\n        if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {\n            return R.fail(\"修改用户'\" + user.getUserName() + \"'失败，手机号码已存在\");\n        }\n        if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {\n            return R.fail(\"修改用户'\" + user.getUserName() + \"'失败，邮箱账号已存在\");\n        }\n        user.setUserId(getUserId());\n        user.setUserName(null);\n        user.setPassword(null);\n        user.setAvatar(null);\n        user.setDeptId(null);\n        if (userService.updateUserProfile(user) > 0) {\n            return R.ok();\n        }\n        return R.fail(\"修改个人信息异常，请联系管理员\");\n    }\n\n    /**\n     * 重置密码\n     *\n     * @param newPassword 旧密码\n     * @param oldPassword 新密码\n     */\n    @Log(title = \"个人信息\", businessType = BusinessType.UPDATE)\n    @PutMapping(\"/updatePwd\")\n    public R<Void> updatePwd(String oldPassword, String newPassword) {\n        SysUser user = userService.selectUserById(LoginHelper.getUserId());\n        String userName = user.getUserName();\n        String password = user.getPassword();\n        if (!BCrypt.checkpw(oldPassword, password)) {\n            return R.fail(\"修改密码失败，旧密码错误\");\n        }\n        if (BCrypt.checkpw(newPassword, password)) {\n            return R.fail(\"新密码不能与旧密码相同\");\n        }\n\n        if (userService.resetUserPwd(userName, BCrypt.hashpw(newPassword)) > 0) {\n            return R.ok();\n        }\n        return R.fail(\"修改密码异常，请联系管理员\");\n    }\n\n    /**\n     * 头像上传\n     *\n     * @param avatarfile 用户头像\n     */\n    @Log(title = \"用户头像\", businessType = BusinessType.UPDATE)\n    @PostMapping(value = \"/avatar\", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)\n    public R<Map<String, Object>> avatar(@RequestPart(\"avatarfile\") MultipartFile avatarfile) {\n        Map<String, Object> ajax = new HashMap<>();\n        if (!avatarfile.isEmpty()) {\n            String extension = FileUtil.extName(avatarfile.getOriginalFilename());\n            if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) {\n                return R.fail(\"文件格式不正确，请上传\" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + \"格式\");\n            }\n            SysOssVo oss = iSysOssService.upload(avatarfile);\n            String avatar = oss.getUrl();\n            if (userService.updateUserAvatar(getUsername(), avatar)) {\n                ajax.put(\"imgUrl\", avatar);\n                return R.ok(ajax);\n            }\n        }\n        return R.fail(\"上传图片异常，请联系管理员\");\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/system/SysRegisterController.java",
    "content": "package top.flya.web.controller.system;\n\nimport cn.dev33.satoken.annotation.SaIgnore;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.domain.model.RegisterBody;\nimport top.flya.system.service.ISysConfigService;\nimport top.flya.system.service.SysRegisterService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 注册验证\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\npublic class SysRegisterController extends BaseController {\n\n    private final SysRegisterService registerService;\n    private final ISysConfigService configService;\n\n    /**\n     * 用户注册\n     */\n    @SaIgnore\n    @PostMapping(\"/register\")\n    public R<Void> register(@Validated @RequestBody RegisterBody user) {\n        if (!(\"true\".equals(configService.selectConfigByKey(\"sys.account.registerUser\")))) {\n            return R.fail(\"当前系统没有开启注册功能！\");\n        }\n        registerService.register(user);\n        return R.ok();\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/system/SysRoleController.java",
    "content": "package top.flya.web.controller.system;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.domain.entity.SysDept;\nimport top.flya.common.core.domain.entity.SysRole;\nimport top.flya.common.core.domain.entity.SysUser;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.SysUserRole;\nimport top.flya.system.service.ISysDeptService;\nimport top.flya.system.service.ISysRoleService;\nimport top.flya.system.service.ISysUserService;\nimport top.flya.system.service.SysPermissionService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 角色信息\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/role\")\npublic class SysRoleController extends BaseController {\n\n    private final ISysRoleService roleService;\n    private final ISysUserService userService;\n    private final ISysDeptService deptService;\n    private final SysPermissionService permissionService;\n\n    /**\n     * 获取角色信息列表\n     */\n    @SaCheckPermission(\"system:role:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<SysRole> list(SysRole role, PageQuery pageQuery) {\n        return roleService.selectPageRoleList(role, pageQuery);\n    }\n\n    /**\n     * 导出角色信息列表\n     */\n    @Log(title = \"角色管理\", businessType = BusinessType.EXPORT)\n    @SaCheckPermission(\"system:role:export\")\n    @PostMapping(\"/export\")\n    public void export(SysRole role, HttpServletResponse response) {\n        List<SysRole> list = roleService.selectRoleList(role);\n        ExcelUtil.exportExcel(list, \"角色数据\", SysRole.class, response);\n    }\n\n    /**\n     * 根据角色编号获取详细信息\n     *\n     * @param roleId 角色ID\n     */\n    @SaCheckPermission(\"system:role:query\")\n    @GetMapping(value = \"/{roleId}\")\n    public R<SysRole> getInfo(@PathVariable Long roleId) {\n        roleService.checkRoleDataScope(roleId);\n        return R.ok(roleService.selectRoleById(roleId));\n    }\n\n    /**\n     * 新增角色\n     */\n    @SaCheckPermission(\"system:role:add\")\n    @Log(title = \"角色管理\", businessType = BusinessType.INSERT)\n    @PostMapping\n    public R<Void> add(@Validated @RequestBody SysRole role) {\n        if (!roleService.checkRoleNameUnique(role)) {\n            return R.fail(\"新增角色'\" + role.getRoleName() + \"'失败，角色名称已存在\");\n        } else if (!roleService.checkRoleKeyUnique(role)) {\n            return R.fail(\"新增角色'\" + role.getRoleName() + \"'失败，角色权限已存在\");\n        }\n        return toAjax(roleService.insertRole(role));\n\n    }\n\n    /**\n     * 修改保存角色\n     */\n    @SaCheckPermission(\"system:role:edit\")\n    @Log(title = \"角色管理\", businessType = BusinessType.UPDATE)\n    @PutMapping\n    public R<Void> edit(@Validated @RequestBody SysRole role) {\n        roleService.checkRoleAllowed(role);\n        roleService.checkRoleDataScope(role.getRoleId());\n        if (!roleService.checkRoleNameUnique(role)) {\n            return R.fail(\"修改角色'\" + role.getRoleName() + \"'失败，角色名称已存在\");\n        } else if (!roleService.checkRoleKeyUnique(role)) {\n            return R.fail(\"修改角色'\" + role.getRoleName() + \"'失败，角色权限已存在\");\n        }\n\n        if (roleService.updateRole(role) > 0) {\n            roleService.cleanOnlineUserByRole(role.getRoleId());\n            return R.ok();\n        }\n        return R.fail(\"修改角色'\" + role.getRoleName() + \"'失败，请联系管理员\");\n    }\n\n    /**\n     * 修改保存数据权限\n     */\n    @SaCheckPermission(\"system:role:edit\")\n    @Log(title = \"角色管理\", businessType = BusinessType.UPDATE)\n    @PutMapping(\"/dataScope\")\n    public R<Void> dataScope(@RequestBody SysRole role) {\n        roleService.checkRoleAllowed(role);\n        roleService.checkRoleDataScope(role.getRoleId());\n        return toAjax(roleService.authDataScope(role));\n    }\n\n    /**\n     * 状态修改\n     */\n    @SaCheckPermission(\"system:role:edit\")\n    @Log(title = \"角色管理\", businessType = BusinessType.UPDATE)\n    @PutMapping(\"/changeStatus\")\n    public R<Void> changeStatus(@RequestBody SysRole role) {\n        roleService.checkRoleAllowed(role);\n        roleService.checkRoleDataScope(role.getRoleId());\n        return toAjax(roleService.updateRoleStatus(role));\n    }\n\n    /**\n     * 删除角色\n     *\n     * @param roleIds 角色ID串\n     */\n    @SaCheckPermission(\"system:role:remove\")\n    @Log(title = \"角色管理\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{roleIds}\")\n    public R<Void> remove(@PathVariable Long[] roleIds) {\n        return toAjax(roleService.deleteRoleByIds(roleIds));\n    }\n\n    /**\n     * 获取角色选择框列表\n     */\n    @SaCheckPermission(\"system:role:query\")\n    @GetMapping(\"/optionselect\")\n    public R<List<SysRole>> optionselect() {\n        return R.ok(roleService.selectRoleAll());\n    }\n\n    /**\n     * 查询已分配用户角色列表\n     */\n    @SaCheckPermission(\"system:role:list\")\n    @GetMapping(\"/authUser/allocatedList\")\n    public TableDataInfo<SysUser> allocatedList(SysUser user, PageQuery pageQuery) {\n        return userService.selectAllocatedList(user, pageQuery);\n    }\n\n    /**\n     * 查询未分配用户角色列表\n     */\n    @SaCheckPermission(\"system:role:list\")\n    @GetMapping(\"/authUser/unallocatedList\")\n    public TableDataInfo<SysUser> unallocatedList(SysUser user, PageQuery pageQuery) {\n        return userService.selectUnallocatedList(user, pageQuery);\n    }\n\n    /**\n     * 取消授权用户\n     */\n    @SaCheckPermission(\"system:role:edit\")\n    @Log(title = \"角色管理\", businessType = BusinessType.GRANT)\n    @PutMapping(\"/authUser/cancel\")\n    public R<Void> cancelAuthUser(@RequestBody SysUserRole userRole) {\n        return toAjax(roleService.deleteAuthUser(userRole));\n    }\n\n    /**\n     * 批量取消授权用户\n     *\n     * @param roleId  角色ID\n     * @param userIds 用户ID串\n     */\n    @SaCheckPermission(\"system:role:edit\")\n    @Log(title = \"角色管理\", businessType = BusinessType.GRANT)\n    @PutMapping(\"/authUser/cancelAll\")\n    public R<Void> cancelAuthUserAll(Long roleId, Long[] userIds) {\n        return toAjax(roleService.deleteAuthUsers(roleId, userIds));\n    }\n\n    /**\n     * 批量选择用户授权\n     *\n     * @param roleId  角色ID\n     * @param userIds 用户ID串\n     */\n    @SaCheckPermission(\"system:role:edit\")\n    @Log(title = \"角色管理\", businessType = BusinessType.GRANT)\n    @PutMapping(\"/authUser/selectAll\")\n    public R<Void> selectAuthUserAll(Long roleId, Long[] userIds) {\n        roleService.checkRoleDataScope(roleId);\n        return toAjax(roleService.insertAuthUsers(roleId, userIds));\n    }\n\n    /**\n     * 获取对应角色部门树列表\n     *\n     * @param roleId 角色ID\n     */\n    @SaCheckPermission(\"system:role:list\")\n    @GetMapping(value = \"/deptTree/{roleId}\")\n    public R<Map<String, Object>> roleDeptTreeselect(@PathVariable(\"roleId\") Long roleId) {\n        Map<String, Object> ajax = new HashMap<>();\n        ajax.put(\"checkedKeys\", deptService.selectDeptListByRoleId(roleId));\n        ajax.put(\"depts\", deptService.selectDeptTreeList(new SysDept()));\n        return R.ok(ajax);\n    }\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/java/top/flya/web/controller/system/SysUserController.java",
    "content": "package top.flya.web.controller.system;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport cn.dev33.satoken.secure.BCrypt;\nimport cn.hutool.core.bean.BeanUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.domain.entity.SysDept;\nimport top.flya.common.core.domain.entity.SysRole;\nimport top.flya.common.core.domain.entity.SysUser;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.excel.ExcelResult;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.StreamUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.system.domain.vo.SysUserExportVo;\nimport top.flya.system.domain.vo.SysUserImportVo;\nimport top.flya.system.listener.SysUserImportListener;\nimport top.flya.system.service.ISysDeptService;\nimport top.flya.system.service.ISysPostService;\nimport top.flya.system.service.ISysRoleService;\nimport top.flya.system.service.ISysUserService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.http.MediaType;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 用户信息\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/system/user\")\npublic class SysUserController extends BaseController {\n\n    private final ISysUserService userService;\n    private final ISysRoleService roleService;\n    private final ISysPostService postService;\n    private final ISysDeptService deptService;\n\n    /**\n     * 获取用户列表\n     */\n    @SaCheckPermission(\"system:user:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<SysUser> list(SysUser user, PageQuery pageQuery) {\n        return userService.selectPageUserList(user, pageQuery);\n    }\n\n    /**\n     * 导出用户列表\n     */\n    @Log(title = \"用户管理\", businessType = BusinessType.EXPORT)\n    @SaCheckPermission(\"system:user:export\")\n    @PostMapping(\"/export\")\n    public void export(SysUser user, HttpServletResponse response) {\n        List<SysUser> list = userService.selectUserList(user);\n        List<SysUserExportVo> listVo = BeanUtil.copyToList(list, SysUserExportVo.class);\n        for (int i = 0; i < list.size(); i++) {\n            SysDept dept = list.get(i).getDept();\n            SysUserExportVo vo = listVo.get(i);\n            if (ObjectUtil.isNotEmpty(dept)) {\n                vo.setDeptName(dept.getDeptName());\n                vo.setLeader(dept.getLeader());\n            }\n        }\n        ExcelUtil.exportExcel(listVo, \"用户数据\", SysUserExportVo.class, response);\n    }\n\n    /**\n     * 导入数据\n     *\n     * @param file          导入文件\n     * @param updateSupport 是否更新已存在数据\n     */\n    @Log(title = \"用户管理\", businessType = BusinessType.IMPORT)\n    @SaCheckPermission(\"system:user:import\")\n    @PostMapping(value = \"/importData\", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)\n    public R<Void> importData(@RequestPart(\"file\") MultipartFile file, boolean updateSupport) throws Exception {\n        ExcelResult<SysUserImportVo> result = ExcelUtil.importExcel(file.getInputStream(), SysUserImportVo.class, new SysUserImportListener(updateSupport));\n        return R.ok(result.getAnalysis());\n    }\n\n    /**\n     * 获取导入模板\n     */\n    @PostMapping(\"/importTemplate\")\n    public void importTemplate(HttpServletResponse response) {\n        ExcelUtil.exportExcel(new ArrayList<>(), \"用户数据\", SysUserImportVo.class, response);\n    }\n\n    /**\n     * 根据用户编号获取详细信息\n     *\n     * @param userId 用户ID\n     */\n    @SaCheckPermission(\"system:user:query\")\n    @GetMapping(value = {\"/\", \"/{userId}\"})\n    public R<Map<String, Object>> getInfo(@PathVariable(value = \"userId\", required = false) Long userId) {\n        userService.checkUserDataScope(userId);\n        Map<String, Object> ajax = new HashMap<>();\n        List<SysRole> roles = roleService.selectRoleAll();\n        ajax.put(\"roles\", LoginHelper.isAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isAdmin()));\n        ajax.put(\"posts\", postService.selectPostAll());\n        if (ObjectUtil.isNotNull(userId)) {\n            SysUser sysUser = userService.selectUserById(userId);\n            ajax.put(\"user\", sysUser);\n            ajax.put(\"postIds\", postService.selectPostListByUserId(userId));\n            ajax.put(\"roleIds\", StreamUtils.toList(sysUser.getRoles(), SysRole::getRoleId));\n        }\n        return R.ok(ajax);\n    }\n\n    /**\n     * 新增用户\n     */\n    @SaCheckPermission(\"system:user:add\")\n    @Log(title = \"用户管理\", businessType = BusinessType.INSERT)\n    @PostMapping\n    public R<Void> add(@Validated @RequestBody SysUser user) {\n        if (!userService.checkUserNameUnique(user)) {\n            return R.fail(\"新增用户'\" + user.getUserName() + \"'失败，登录账号已存在\");\n        } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {\n            return R.fail(\"新增用户'\" + user.getUserName() + \"'失败，手机号码已存在\");\n        } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {\n            return R.fail(\"新增用户'\" + user.getUserName() + \"'失败，邮箱账号已存在\");\n        }\n        user.setPassword(BCrypt.hashpw(user.getPassword()));\n        return toAjax(userService.insertUser(user));\n    }\n\n    /**\n     * 修改用户\n     */\n    @SaCheckPermission(\"system:user:edit\")\n    @Log(title = \"用户管理\", businessType = BusinessType.UPDATE)\n    @PutMapping\n    public R<Void> edit(@Validated @RequestBody SysUser user) {\n        userService.checkUserAllowed(user);\n        userService.checkUserDataScope(user.getUserId());\n        if (!userService.checkUserNameUnique(user)) {\n            return R.fail(\"修改用户'\" + user.getUserName() + \"'失败，登录账号已存在\");\n        } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {\n            return R.fail(\"修改用户'\" + user.getUserName() + \"'失败，手机号码已存在\");\n        } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {\n            return R.fail(\"修改用户'\" + user.getUserName() + \"'失败，邮箱账号已存在\");\n        }\n        return toAjax(userService.updateUser(user));\n    }\n\n    /**\n     * 删除用户\n     *\n     * @param userIds 角色ID串\n     */\n    @SaCheckPermission(\"system:user:remove\")\n    @Log(title = \"用户管理\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{userIds}\")\n    public R<Void> remove(@PathVariable Long[] userIds) {\n        if (ArrayUtil.contains(userIds, getUserId())) {\n            return R.fail(\"当前用户不能删除\");\n        }\n        return toAjax(userService.deleteUserByIds(userIds));\n    }\n\n    /**\n     * 重置密码\n     */\n    @SaCheckPermission(\"system:user:resetPwd\")\n    @Log(title = \"用户管理\", businessType = BusinessType.UPDATE)\n    @PutMapping(\"/resetPwd\")\n    public R<Void> resetPwd(@RequestBody SysUser user) {\n        userService.checkUserAllowed(user);\n        userService.checkUserDataScope(user.getUserId());\n        user.setPassword(BCrypt.hashpw(user.getPassword()));\n        return toAjax(userService.resetPwd(user));\n    }\n\n    /**\n     * 状态修改\n     */\n    @SaCheckPermission(\"system:user:edit\")\n    @Log(title = \"用户管理\", businessType = BusinessType.UPDATE)\n    @PutMapping(\"/changeStatus\")\n    public R<Void> changeStatus(@RequestBody SysUser user) {\n        userService.checkUserAllowed(user);\n        userService.checkUserDataScope(user.getUserId());\n        return toAjax(userService.updateUserStatus(user));\n    }\n\n    /**\n     * 根据用户编号获取授权角色\n     *\n     * @param userId 用户ID\n     */\n    @SaCheckPermission(\"system:user:query\")\n    @GetMapping(\"/authRole/{userId}\")\n    public R<Map<String, Object>> authRole(@PathVariable Long userId) {\n        SysUser user = userService.selectUserById(userId);\n        List<SysRole> roles = roleService.selectRolesByUserId(userId);\n        Map<String, Object> ajax = new HashMap<>();\n        ajax.put(\"user\", user);\n        ajax.put(\"roles\", LoginHelper.isAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isAdmin()));\n        return R.ok(ajax);\n    }\n\n    /**\n     * 用户授权角色\n     *\n     * @param userId  用户Id\n     * @param roleIds 角色ID串\n     */\n    @SaCheckPermission(\"system:user:edit\")\n    @Log(title = \"用户管理\", businessType = BusinessType.GRANT)\n    @PutMapping(\"/authRole\")\n    public R<Void> insertAuthRole(Long userId, Long[] roleIds) {\n        userService.checkUserDataScope(userId);\n        userService.insertUserAuth(userId, roleIds);\n        return R.ok();\n    }\n\n    /**\n     * 获取部门树列表\n     */\n    @SaCheckPermission(\"system:user:list\")\n    @GetMapping(\"/deptTree\")\n    public R<List<Tree<Long>>> deptTree(SysDept dept) {\n        return R.ok(deptService.selectDeptTreeList(dept));\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-admin/src/main/resources/Dockerfile",
    "content": "\nFROM gozap/oraclejdk8\nLABEL maintainer=fengli\n\n# /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&\nRUN  echo 'Asia/Shanghai' >/etc/timezone\n\nCOPY ./ruoyi-admin/target/*.jar /app.jar\n\n\n\nEXPOSE 8080\n\n# hospital-manage\nENTRYPOINT [\"/bin/sh\",\"-c\",\"java -Dfile.encoding=utf8  -Djava.security.egd=file:/dev/./urandom -jar /app.jar \"]\n"
  },
  {
    "path": "ruoyi-admin/src/main/resources/apiclient_key.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCpUtNYFC/SVijj\nmOsW2vVIewgnOfB2YuUW8Q0LUbLtN8jfPJrv2BziCWxVciqNtMKO2ONerRtedzar\nD4poTVgDzICefGL8Mxj0PUIGH9dslc9GSsPjc3iynrW0IMFmrx1ItI//7wbWJTCf\neNWkQa9WmywFUcMQy/oVsWITq7RaoCdYLVQ6w7CrusQ1DOOJ1OoCVA8HB+zqJGKW\nX6dLTTKF14kYNABPHf7lPd8Zv2VIvxLMxqZuSDKZouQjaciXLI4LGdQje9N9j1JE\ncInKIzQnI3AfKTd0TdWR1F+j8QAQCBrHuOLO6WQEQ+N82BZX7KFn+0SVzzH0QjpL\nK+vjHSBZAgMBAAECggEBAKA6qZZC3BIdyGm/7k9dehlRm6CLGnrdEM7J4r8gW8JR\nNLvTPQbUKljX8/VTqOMZ97Z3lYmlJC4bf9cWSLJ05mIJ5niTWpQvwmB1i4ICJbgy\nd8ebvo0BW2kj+OxwxrNl6L9BZrcZOQ3yeXWfQgRCyCqbgmeyPHYroAdhKV9V78CF\nHSHwZOMVPUo/y44w6ig5Fw6pKdGXSzZwjGUyZNpaj4IY3LYWWffuigHJduMzBut2\nv4JJC2TPC9hB6MiR/sVqpKxtnM466BB8pv7hlZ9TJFzdfD5k1UZuheUgUiunajiC\nFeHOEjtSsTWKsAmTg26+Bw5r1apdL3QsLwqH58IVPaUCgYEA1k//XKqWIWG73OKc\nmOH5gjzb3Aq4c6yw5TGPhEkPn7D+7auKuwzuoUrzASeopzp0te7Ow299fNToFWIs\nwEXi4g0gezO7UwpJ6wWNr0iL/wPgYx3jglrPQ+Pvmfoaj4N0mK/pfmXHF8RdIs/M\np2kn2oQLrJgdeGb4FZM0byX7UcsCgYEAykKJotProJqDogbqsNEIJpBfFlBvxFgb\nIPAp33tBqBh9DiKIOOtISTjPZQIAkzkC8SMRK+HZbF1p2/20bjvTOsi8PKxm/sqS\nv/oPLDfni8pkmhfNU6rr0iNMagpoT9kks4qk9h2dRij1fKJ6pa4TBahJx5wQ3Yu0\nvMEmy8JdwesCgYEAoSGAg7GWMv8Cei6/QosUR4FuZGCDEiWS0p+Sogk0gAJZiWRi\naARvHkH1traUrTbcLTWhq3sVxFdnLzyjHOTukrr/4uGgQ+0GanfAcTuAVnoZqSv9\ntDKGhyrHKOPMOH7DmVEZovju2cW/qL7Hxk7fsgF5rYipD6+Lct08nRzXekUCgYEA\ns2muWYeOnhox5coo6MujZUHvdwXG/u4AsokXO6xEI24FkEJFf+gFaR5BqiHKjM2n\ntGsc0kY27Y83VfOI17etuZlSkKeFfUIIRs70Io88j53q+11dv3gAU5kIMZAl056U\nlcbIaaD/X7r5d6NRFCKDsSMEv1HLDBrfKghT967kKB0CgYBnynlp2NRtBgtLNURS\nc2xQ7NrlhSiWyLKii/h55PRfxps07w/eqsNALgAqBVDvZt8TcD1IW8/FYv+ZcvMm\nGUA4MNQonauQbLDbkwEHc8MWIpPMJnQYf9YAFlf73qknlikepc+s0mkNkYaZYQYZ\nQ3uWw4iuk4iyxAzWDWrIRPoiRA==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "ruoyi-admin/src/main/resources/application-dev.yml",
    "content": "--- # 监控中心配置\nspring.boot.admin.client:\n  # 增加客户端开关\n  enabled: true\n  url: http://localhost:9090/admin\n  instance:\n    service-host-type: IP\n  username: ruoyi\n  password: 123456\n\n\n--- # 数据源配置\nspring:\n  datasource:\n    type: com.zaxxer.hikari.HikariDataSource\n    # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content\n    dynamic:\n      # 性能分析插件(有性能损耗 不建议生产环境使用)\n      p6spy: true\n      # 设置默认的数据源或者数据源组,默认值即为 master\n      primary: master\n      # 严格模式 匹配不到数据源则报错\n      strict: true\n      datasource:\n        # 主库数据源\n        master:\n          type: ${spring.datasource.type}\n          driverClassName: com.mysql.cj.jdbc.Driver\n          # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562\n#          rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)\n          url: jdbc:mysql://localhost:3306/paizhicheng?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true\n          username: root\n          password: 123456\n      hikari:\n        # 最大连接池数量\n        maxPoolSize: 20\n        # 最小空闲线程数量\n        minIdle: 10\n        # 配置获取连接等待超时的时间\n        connectionTimeout: 30000\n        # 校验超时时间\n        validationTimeout: 5000\n        # 空闲连接存活最大时间，默认10分钟\n        idleTimeout: 600000\n        # 此属性控制池中连接的最长生命周期，值0表示无限生命周期，默认30分钟\n        maxLifetime: 1800000\n        # 连接测试query（配置检测连接是否有效）\n        connectionTestQuery: SELECT 1\n        # 多久检查一次连接的活性\n        keepaliveTime: 30000\n\n--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)\nspring:\n  redis:\n    # 地址\n    # Redis数据库索引（默认为0）\n    database: 5\n    # Redis服务器地址\n    host: localhost\n    # 端口，默认为6379\n    port: 6379\n    # 密码(如没有密码请注释掉)\n#    password:\n    # 连接超时时间\n    timeout: 10s\n    # 是否开启ssl\n    ssl: false\n\nredisson:\n  # redis key前缀\n  keyPrefix:\n  # 线程池数量\n  threads: 4\n  # Netty线程池数量\n  nettyThreads: 8\n  # 单节点配置\n  singleServerConfig:\n    # 客户端名称\n    clientName: ${ruoyi.name}\n    # 最小空闲连接数\n    connectionMinimumIdleSize: 8\n    # 连接池大小\n    connectionPoolSize: 32\n    # 连接空闲超时，单位：毫秒\n    idleConnectionTimeout: 10000\n    # 命令等待超时，单位：毫秒\n    timeout: 3000\n    # 发布和订阅连接池大小\n    subscriptionConnectionPoolSize: 50\n\n\n"
  },
  {
    "path": "ruoyi-admin/src/main/resources/application.yml",
    "content": "neteaseCloudMusicApi: http://iwenwiki.com:3000/song/url?id= # 网易云音乐API\n\nwx:\n  appId:  # 小程序AppId\n  appSecret:  # 小程序AppSecret\n  notifyUrl:   # 微信支付回调地址\n  merchantId:   # 商户号\n  merchantSerialNumber:  # 商户API证书序列号\n  api3:  # 商户API证书序\n  server:\n    port: 9394  # Websocket服务端口\n    host: localhost # Websocket服务地址\n\napi:\n  school: https://hn216.api.yesapi.cn/?s=App.Common_University.Search&return_data=0&school_name= # 高校查询API\n  apiKey: top_num=10&app_key=D5B0BA3EDE51C76C1EEC4D581589F3A9&sign=99E25CA458C5389EEA9E2E6530CB825B # API密钥\n\n# 项目相关配置\nruoyi:\n  # 名称\n  name: RuoYi-Vue-Plus\n  # 版本\n  version: ${ruoyi-vue-plus.version}\n  # 版权年份\n  copyrightYear: 2022\n  # 实例演示开关\n  demoEnabled: true\n  # 获取ip地址开关\n  addressEnabled: true\n  # 缓存懒加载\n  cacheLazy: false\n\ncaptcha:\n  # 页面 <参数设置> 可开启关闭 验证码校验\n  # 验证码类型 math 数组计算 char 字符验证\n  type: MATH\n  # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰\n  category: CIRCLE\n  # 数字验证码位数\n  numberLength: 1\n  # 字符验证码长度\n  charLength: 4\n\n# 开发环境配置\nserver:\n  # 服务器的HTTP端口，默认为8080\n  port: 9393\n  servlet:\n    # 应用的访问路径\n    context-path: /\n  # undertow 配置\n  undertow:\n    # HTTP post内容的最大大小。当值为-1时，默认值为大小是无限的\n    max-http-post-size: -1\n    # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理\n    # 每块buffer的空间大小,越小的空间被利用越充分\n    buffer-size: 512\n    # 是否分配的直接内存\n    direct-buffers: true\n    threads:\n      # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程\n      io: 8\n      # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载\n      worker: 256\n\n# 日志配置\nlogging:\n  level:\n    com.ruoyi: @logging.level@\n    org.springframework: warn\n  config: classpath:logback-plus.xml\n\n# 用户配置\nuser:\n  password:\n    # 密码最大错误次数\n    maxRetryCount: 50000\n    # 密码锁定时间（默认10分钟）\n    lockTime: 10\n\n# Spring配置\nspring:\n  application:\n    name: ${ruoyi.name}\n  # 资源信息\n  messages:\n    # 国际化资源文件路径\n    basename: i18n/messages\n  profiles:\n    active: @profiles.active@\n  # 文件上传\n  servlet:\n    multipart:\n      # 单个文件大小\n      max-file-size: 50MB\n      # 设置总上传的文件大小\n      max-request-size: 50MB\n  # 服务模块\n  devtools:\n    restart:\n      # 热部署开关\n      enabled: true\n  mvc:\n    format:\n      date-time: yyyy-MM-dd HH:mm:ss\n  jackson:\n    # 日期格式化\n    date-format: yyyy-MM-dd HH:mm:ss\n    serialization:\n      # 格式化输出\n      indent_output: false\n      # 忽略无法转换的对象\n      fail_on_empty_beans: false\n    deserialization:\n      # 允许对象忽略json中不存在的属性\n      fail_on_unknown_properties: false\n\n# Sa-Token配置\nsa-token:\n  # token名称 (同时也是cookie名称)\n  token-name: Authorization\n  # token有效期 设为一天 (必定过期) 单位: 秒   一个月过期（2592000 ） 需要重新登录  -1 为永不过期\n  timeout: 2592000\n  # token临时有效期 (指定时间无操作就过期) 单位: 秒\n  activity-timeout: -1 # 1800\n  # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)\n  is-concurrent: true\n  # 在多人登录同一账号时，是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)\n  is-share: false\n  # 是否尝试从header里读取token\n  is-read-header: true\n  # 是否尝试从cookie里读取token\n  is-read-cookie: false\n  # token前缀\n  token-prefix: \"Bearer\"\n  # jwt秘钥\n  jwt-secret-key: abcdefghiwdwdwjklmnopqrstuvwxyz\n\n# security配置\nsecurity:\n  # 排除路径\n  excludes:\n    # 静态资源\n    - /*.html\n    - /**/*.html\n    - /**/*.css\n    - /**/*.js\n    # 公共路径\n    - /favicon.ico\n    - /error\n    # swagger 文档配置\n    - /*/api-docs\n    - /*/api-docs/**\n    # actuator 监控配置\n    - /actuator\n    - /actuator/**\n    # actuator 监控配置\n    -  /system/activity/listWx  # 小程序端 活动列表\n    -  /wx/user/login  # 小程序端 登录\n    -  /system/dict/data/list # 字典数据列表\n    -  /wx/user/callback # 小程序端 回调\n    -  /system/viewPager/list # 小程序端 轮播图\n    -  /wx/user/music # 小程序端 音乐列表\n    -  /wx/user/filterKeyWords #敏感词过滤\n    -  /test/** # 测试接口\n# MyBatisPlus配置\n# https://baomidou.com/config/\nmybatis-plus:\n  # 不支持多包, 如有需要可在注解配置 或 提升扫包等级\n  # 例如 com.**.**.mapper\n  mapperPackage: top.flya.**.mapper\n  # 对应的 XML 文件位置\n  mapperLocations: classpath*:mapper/**/*Mapper.xml\n  # 实体扫描，多个package用逗号或者分号分隔\n  typeAliasesPackage: top.flya.**.domain\n  # 启动时是否检查 MyBatis XML 文件的存在，默认不检查\n  checkConfigLocation: false\n  configuration:\n    # 自动驼峰命名规则（camel case）映射\n    mapUnderscoreToCamelCase: true\n    # MyBatis 自动映射策略\n    # NONE：不启用 PARTIAL：只对非嵌套 resultMap 自动映射 FULL：对所有 resultMap 自动映射\n    autoMappingBehavior: PARTIAL\n    # MyBatis 自动映射时未知列或未知属性处理策\n    # NONE：不做处理 WARNING：打印相关警告 FAILING：抛出异常和详细信息\n    autoMappingUnknownColumnBehavior: NONE\n    # 更详细的日志输出 会有性能损耗 org.apache.ibatis.logging.stdout.StdOutImpl\n    # 关闭日志记录 (可单纯使用 p6spy 分析) org.apache.ibatis.logging.nologging.NoLoggingImpl\n    # 默认日志输出 org.apache.ibatis.logging.slf4j.Slf4jImpl\n    logImpl: org.apache.ibatis.logging.nologging.NoLoggingImpl\n  global-config:\n    # 是否打印 Logo banner\n    banner: true\n    dbConfig:\n      # 主键类型\n      # AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID\n      idType: AUTO\n      # 逻辑已删除值\n      logicDeleteValue: 2\n      # 逻辑未删除值\n      logicNotDeleteValue: 0\n      # 字段验证策略之 insert,在 insert 的时候的字段验证策略\n      # IGNORED 忽略 NOT_NULL 非NULL NOT_EMPTY 非空 DEFAULT 默认 NEVER 不加入 SQL\n      insertStrategy: NOT_NULL\n      # 字段验证策略之 update,在 update 的时候的字段验证策略\n      updateStrategy: NOT_NULL\n      # 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件\n      where-strategy: NOT_NULL\n\n# 数据加密\nmybatis-encryptor:\n  # 是否开启加密\n  enable: false\n  # 默认加密算法\n  algorithm: BASE64\n  # 编码方式 BASE64/HEX。默认BASE64\n  encode: BASE64\n  # 安全秘钥 对称算法的秘钥 如：AES，SM4\n  password:\n  # 公私钥 非对称算法的公私钥 如：SM2，RSA\n  publicKey:\n  privateKey:\n\n# Swagger配置\nswagger:\n  info:\n    # 标题\n    title: '标题：${ruoyi.name}后台管理系统_接口文档'\n    # 描述\n    description: '描述：用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'\n    # 版本\n    version: '版本号: ${ruoyi-vue-plus.version}'\n    # 作者信息\n    contact:\n      name: Lion Li\n      email: crazylionli@163.com\n      url: https://gitee.com/dromara/RuoYi-Vue-Plus\n  components:\n    # 鉴权方式配置\n    security-schemes:\n      apiKey:\n        type: APIKEY\n        in: HEADER\n        name: ${sa-token.token-name}\n\nspringdoc:\n  api-docs:\n    # 是否开启接口文档\n    enabled: true\n  swagger-ui:\n    # 持久化认证数据\n    persistAuthorization: true\n  #这里定义了两个分组，可定义多个，也可以不定义\n  group-configs:\n    - group: 1.演示模块\n      packages-to-scan: top.flya.demo\n    - group: 2.系统模块\n      packages-to-scan: top.flya.web\n    - group: 3.代码生成模块\n      packages-to-scan: top.flya.generator\n\n# 防止XSS攻击\nxss:\n  # 过滤开关\n  enabled: false\n  # 排除链接（多个用逗号分隔）\n  excludes: /system/notice\n  # 匹配链接\n  urlPatterns: /system/*,/monitor/*,/tool/*\n\n# 全局线程池相关配置\nthread-pool:\n  # 是否开启线程池\n  enabled: false\n  # 队列最大长度\n  queueCapacity: 128\n  # 线程池维护线程所允许的空闲时间\n  keepAliveSeconds: 300\nxxl:\n  job:\n    admin:\n      url:\n        update: ${xxl.job.admin-addresses}/jobinfo/update\n        add: ${xxl.job.admin-addresses}/jobinfo/add\n        start: ${xxl.job.admin-addresses}/jobinfo/start\n        stop: ${xxl.job.admin-addresses}/jobinfo/stop\n        pageList: ${xxl.job.admin-addresses}/jobinfo/pageList\n\n      address: http://111.229.24.163:9100/xxl-job-admin\n      username: admin\n      password: admin\n    enabled: true\n    admin-addresses: http://111.229.24.163:9100/xxl-job-admin\n    access-token: xdsl3ewi3al1oehxmo68pqxer\n    executor:\n      appname: pai-zhi-cheng\n      ip:\n      port: 9101\n      logpath: ./logs/xxl-job\n      logretentiondays: 30\n--- # 分布式锁 lock4j 全局配置\nlock4j:\n  # 获取分布式锁超时时间，默认为 3000 毫秒\n  acquire-timeout: 3000\n  # 分布式锁的超时时间，默认为 30 秒\n  expire: 30000\n\n--- # Actuator 监控端点的配置项\n#management:\n#  endpoints:\n#    web:\n#      exposure:\n#        include: '*'\n#    enabled-by-default: off\n#  endpoint:\n#    health:\n#      show-details: ALWAYS\n#    logfile:\n#      external-file: ./logs/sys-console.log\n"
  },
  {
    "path": "ruoyi-admin/src/main/resources/banner.txt",
    "content": "Application Version: ${ruoyi-vue-plus.version}\nSpring Boot Version: ${spring-boot.version}\n__________            _____.___.__         ____   ____                     __________.__\n\\______   \\__ __  ____\\__  |   |__|        \\   \\ /   /_ __   ____          \\______   \\  |  __ __  ______\n |       _/  |  \\/  _ \\/   |   |  |  ______ \\   Y   /  |  \\_/ __ \\   ______ |     ___/  | |  |  \\/  ___/\n |    |   \\  |  (  <_> )____   |  | /_____/  \\     /|  |  /\\  ___/  /_____/ |    |   |  |_|  |  /\\___ \\\n |____|_  /____/ \\____// ______|__|           \\___/ |____/  \\___  >         |____|   |____/____//____  >\n        \\/             \\/                                       \\/                                   \\/\n"
  },
  {
    "path": "ruoyi-admin/src/main/resources/i18n/messages.properties",
    "content": "#错误消息\nnot.null=* 必须填写\nuser.jcaptcha.error=验证码错误\nuser.jcaptcha.expire=验证码已失效\nuser.not.exists=对不起, 您的账号：{0} 不存在.\nuser.password.not.match=用户不存在/密码错误\nuser.password.retry.limit.count=密码输入错误{0}次\nuser.password.retry.limit.exceed=密码输入错误{0}次，帐户锁定{1}分钟\nuser.password.delete=对不起，您的账号：{0} 已被删除\nuser.blocked=对不起，您的账号：{0} 已禁用，请联系管理员\nrole.blocked=角色已封禁，请联系管理员\nuser.logout.success=退出成功\nlength.not.valid=长度必须在{min}到{max}个字符之间\nuser.username.not.blank=用户名不能为空\nuser.username.not.valid=* 2到20个汉字、字母、数字或下划线组成，且必须以非数字开头\nuser.username.length.valid=账户长度必须在{min}到{max}个字符之间\nuser.password.not.blank=用户密码不能为空\nuser.password.length.valid=用户密码长度必须在{min}到{max}个字符之间\nuser.password.not.valid=* 5-50个字符\nuser.email.not.valid=邮箱格式错误\nuser.email.not.blank=邮箱不能为空\nuser.phonenumber.not.blank=用户手机号不能为空\nuser.mobile.phone.number.not.valid=手机号格式错误\nuser.login.success=登录成功\nuser.register.success=注册成功\nuser.register.save.error=保存用户 {0} 失败，注册账号已存在\nuser.register.error=注册失败，请联系系统管理人员\nuser.notfound=请重新登录\nuser.forcelogout=管理员强制退出，请重新登录\nuser.unknown.error=未知错误，请重新登录\n##文件上传消息\nupload.exceed.maxSize=上传的文件大小超出限制的文件大小！<br/>允许的文件最大大小是：{0}MB！\nupload.filename.exceed.length=上传的文件名最长{0}个字符\n##权限\nno.permission=您没有数据的权限，请联系管理员添加权限 [{0}]\nno.create.permission=您没有创建数据的权限，请联系管理员添加权限 [{0}]\nno.update.permission=您没有修改数据的权限，请联系管理员添加权限 [{0}]\nno.delete.permission=您没有删除数据的权限，请联系管理员添加权限 [{0}]\nno.export.permission=您没有导出数据的权限，请联系管理员添加权限 [{0}]\nno.view.permission=您没有查看数据的权限，请联系管理员添加权限 [{0}]\nrepeat.submit.message=不允许重复提交，请稍候再试\nrate.limiter.message=访问过于频繁，请稍候再试\nsms.code.not.blank=短信验证码不能为空\nsms.code.retry.limit.count=短信验证码输入错误{0}次\nsms.code.retry.limit.exceed=短信验证码输入错误{0}次，帐户锁定{1}分钟\nemail.code.not.blank=邮箱验证码不能为空\nemail.code.retry.limit.count=邮箱验证码输入错误{0}次\nemail.code.retry.limit.exceed=邮箱验证码输入错误{0}次，帐户锁定{1}分钟\nxcx.code.not.blank=小程序code不能为空\n"
  },
  {
    "path": "ruoyi-admin/src/main/resources/i18n/messages_en_US.properties",
    "content": "#错误消息\nnot.null=* Required fill in\nuser.jcaptcha.error=Captcha error\nuser.jcaptcha.expire=Captcha invalid\nuser.not.exists=Sorry, your account: {0} does not exist\nuser.password.not.match=User does not exist/Password error\nuser.password.retry.limit.count=Password input error {0} times\nuser.password.retry.limit.exceed=Password input error {0} times, account locked for {1} minutes\nuser.password.delete=Sorry, your account：{0} has been deleted\nuser.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator\nrole.blocked=Role disabled，please contact administrators\nuser.logout.success=Exit successful\nlength.not.valid=The length must be between {min} and {max} characters\nuser.username.not.blank=Username cannot be blank\nuser.username.not.valid=* 2 to 20 chinese characters, letters, numbers or underscores, and must start with a non number\nuser.username.length.valid=Account length must be between {min} and {max} characters\nuser.password.not.blank=Password cannot be empty\nuser.password.length.valid=Password length must be between {min} and {max} characters\nuser.password.not.valid=* 5-50 characters\nuser.email.not.valid=Mailbox format error\nuser.email.not.blank=Mailbox cannot be blank\nuser.phonenumber.not.blank=Phone number cannot be blank\nuser.mobile.phone.number.not.valid=Phone number format error\nuser.login.success=Login successful\nuser.register.success=Register successful\nuser.register.save.error=Failed to save user {0}, The registered account already exists\nuser.register.error=Register failed, please contact system administrator\nuser.notfound=Please login again\nuser.forcelogout=The administrator is forced to exit，please login again\nuser.unknown.error=Unknown error, please login again\n##文件上传消息\nupload.exceed.maxSize=The uploaded file size exceeds the limit file size！<br/>the maximum allowed file size is：{0}MB！\nupload.filename.exceed.length=The maximum length of uploaded file name is {0} characters\n##权限\nno.permission=You do not have permission to the data，please contact your administrator to add permissions [{0}]\nno.create.permission=You do not have permission to create data，please contact your administrator to add permissions [{0}]\nno.update.permission=You do not have permission to modify data，please contact your administrator to add permissions [{0}]\nno.delete.permission=You do not have permission to delete data，please contact your administrator to add permissions [{0}]\nno.export.permission=You do not have permission to export data，please contact your administrator to add permissions [{0}]\nno.view.permission=You do not have permission to view data，please contact your administrator to add permissions [{0}]\nrepeat.submit.message=Repeat submit is not allowed, please try again later\nrate.limiter.message=Visit too frequently, please try again later\nsms.code.not.blank=Sms code cannot be blank\nsms.code.retry.limit.count=Sms code input error {0} times\nsms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes\nemail.code.not.blank=Email code cannot be blank\nemail.code.retry.limit.count=Email code input error {0} times\nemail.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes\nxcx.code.not.blank=Mini program code cannot be blank\n"
  },
  {
    "path": "ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties",
    "content": "#错误消息\nnot.null=* 必须填写\nuser.jcaptcha.error=验证码错误\nuser.jcaptcha.expire=验证码已失效\nuser.not.exists=对不起, 您的账号：{0} 不存在.\nuser.password.not.match=用户不存在/密码错误\nuser.password.retry.limit.count=密码输入错误{0}次\nuser.password.retry.limit.exceed=密码输入错误{0}次，帐户锁定{1}分钟\nuser.password.delete=对不起，您的账号：{0} 已被删除\nuser.blocked=对不起，您的账号：{0} 已禁用，请联系管理员\nrole.blocked=角色已封禁，请联系管理员\nuser.logout.success=退出成功\nlength.not.valid=长度必须在{min}到{max}个字符之间\nuser.username.not.blank=用户名不能为空\nuser.username.not.valid=* 2到20个汉字、字母、数字或下划线组成，且必须以非数字开头\nuser.username.length.valid=账户长度必须在{min}到{max}个字符之间\nuser.password.not.blank=用户密码不能为空\nuser.password.length.valid=用户密码长度必须在{min}到{max}个字符之间\nuser.password.not.valid=* 5-50个字符\nuser.email.not.valid=邮箱格式错误\nuser.email.not.blank=邮箱不能为空\nuser.phonenumber.not.blank=用户手机号不能为空\nuser.mobile.phone.number.not.valid=手机号格式错误\nuser.login.success=登录成功\nuser.register.success=注册成功\nuser.register.save.error=保存用户 {0} 失败，注册账号已存在\nuser.register.error=注册失败，请联系系统管理人员\nuser.notfound=请重新登录\nuser.forcelogout=管理员强制退出，请重新登录\nuser.unknown.error=未知错误，请重新登录\n##文件上传消息\nupload.exceed.maxSize=上传的文件大小超出限制的文件大小！<br/>允许的文件最大大小是：{0}MB！\nupload.filename.exceed.length=上传的文件名最长{0}个字符\n##权限\nno.permission=您没有数据的权限，请联系管理员添加权限 [{0}]\nno.create.permission=您没有创建数据的权限，请联系管理员添加权限 [{0}]\nno.update.permission=您没有修改数据的权限，请联系管理员添加权限 [{0}]\nno.delete.permission=您没有删除数据的权限，请联系管理员添加权限 [{0}]\nno.export.permission=您没有导出数据的权限，请联系管理员添加权限 [{0}]\nno.view.permission=您没有查看数据的权限，请联系管理员添加权限 [{0}]\nrepeat.submit.message=不允许重复提交，请稍候再试\nrate.limiter.message=访问过于频繁，请稍候再试\nsms.code.not.blank=短信验证码不能为空\nsms.code.retry.limit.count=短信验证码输入错误{0}次\nsms.code.retry.limit.exceed=短信验证码输入错误{0}次，帐户锁定{1}分钟\nemail.code.not.blank=邮箱验证码不能为空\nemail.code.retry.limit.count=邮箱验证码输入错误{0}次\nemail.code.retry.limit.exceed=邮箱验证码输入错误{0}次，帐户锁定{1}分钟\nxcx.code.not.blank=小程序code不能为空\n"
  },
  {
    "path": "ruoyi-admin/src/main/resources/logback-plus.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n    <property name=\"log.path\" value=\"./logs\"/>\n    <property name=\"console.log.pattern\"\n              value=\"%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n\"/>\n    <property name=\"log.pattern\" value=\"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"/>\n\n    <!-- 控制台输出 -->\n    <appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>${console.log.pattern}</pattern>\n            <charset>utf-8</charset>\n        </encoder>\n    </appender>\n\n    <!-- 控制台输出 -->\n    <appender name=\"file_console\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${log.path}/sys-console.log</file>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n            <!-- 日志文件名格式 -->\n            <fileNamePattern>${log.path}/sys-console.%d{yyyy-MM-dd}.log</fileNamePattern>\n            <!-- 日志最大 1天 -->\n            <maxHistory>1</maxHistory>\n        </rollingPolicy>\n        <encoder>\n            <pattern>${log.pattern}</pattern>\n            <charset>utf-8</charset>\n        </encoder>\n        <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\n            <!-- 过滤的级别 -->\n            <level>INFO</level>\n        </filter>\n    </appender>\n\n    <!-- 系统日志输出 -->\n    <appender name=\"file_info\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${log.path}/sys-info.log</file>\n        <!-- 循环政策：基于时间创建日志文件 -->\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n            <!-- 日志文件名格式 -->\n            <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>\n            <!-- 日志最大的历史 60天 -->\n            <maxHistory>60</maxHistory>\n        </rollingPolicy>\n        <encoder>\n            <pattern>${log.pattern}</pattern>\n        </encoder>\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <!-- 过滤的级别 -->\n            <level>INFO</level>\n<!--            <level></level>-->\n            <!-- 匹配时的操作：接收（记录） -->\n            <onMatch>ACCEPT</onMatch>\n            <!-- 不匹配时的操作：拒绝（不记录） -->\n            <onMismatch>DENY</onMismatch>\n        </filter>\n    </appender>\n\n    <appender name=\"file_error\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${log.path}/sys-error.log</file>\n        <!-- 循环政策：基于时间创建日志文件 -->\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n            <!-- 日志文件名格式 -->\n            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>\n            <!-- 日志最大的历史 60天 -->\n            <maxHistory>60</maxHistory>\n        </rollingPolicy>\n        <encoder>\n            <pattern>${log.pattern}</pattern>\n        </encoder>\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <!-- 过滤的级别 -->\n            <level>ERROR</level>\n            <!-- 匹配时的操作：接收（记录） -->\n            <onMatch>ACCEPT</onMatch>\n            <!-- 不匹配时的操作：拒绝（不记录） -->\n            <onMismatch>DENY</onMismatch>\n        </filter>\n    </appender>\n\n    <!-- info异步输出 -->\n    <appender name=\"async_info\" class=\"ch.qos.logback.classic.AsyncAppender\">\n        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->\n        <discardingThreshold>0</discardingThreshold>\n        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->\n        <queueSize>512</queueSize>\n        <!-- 添加附加的appender,最多只能添加一个 -->\n        <appender-ref ref=\"file_info\"/>\n    </appender>\n\n    <!-- error异步输出 -->\n    <appender name=\"async_error\" class=\"ch.qos.logback.classic.AsyncAppender\">\n        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->\n        <discardingThreshold>0</discardingThreshold>\n        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->\n        <queueSize>512</queueSize>\n        <!-- 添加附加的appender,最多只能添加一个 -->\n        <appender-ref ref=\"file_error\"/>\n    </appender>\n\n    <!-- 整合 skywalking 控制台输出 tid -->\n<!--    <appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">-->\n<!--        <encoder class=\"ch.qos.logback.core.encoder.LayoutWrappingEncoder\">-->\n<!--            <layout class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout\">-->\n<!--                <pattern>[%tid] ${console.log.pattern}</pattern>-->\n<!--            </layout>-->\n<!--            <charset>utf-8</charset>-->\n<!--        </encoder>-->\n<!--    </appender>-->\n\n    <!-- 整合 skywalking 推送采集日志 -->\n<!--    <appender name=\"sky_log\" class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender\">-->\n<!--        <encoder class=\"ch.qos.logback.core.encoder.LayoutWrappingEncoder\">-->\n<!--            <layout class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout\">-->\n<!--                <pattern>[%tid] ${console.log.pattern}</pattern>-->\n<!--            </layout>-->\n<!--            <charset>utf-8</charset>-->\n<!--        </encoder>-->\n<!--    </appender>-->\n\n    <!--系统操作日志-->\n    <root level=\"info\">\n        <appender-ref ref=\"console\" />\n        <appender-ref ref=\"async_info\" />\n        <appender-ref ref=\"async_error\" />\n        <appender-ref ref=\"file_console\" />\n<!--        <appender-ref ref=\"sky_log\"/>-->\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "ruoyi-admin/src/main/resources/spy.properties",
    "content": "# p6spy 性能分析插件配置文件\nmodulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory\n# 自定义日志打印\nlogMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger\n#日志输出到控制台\nappender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger\n# 使用日志系统记录 sql\n#appender=com.p6spy.engine.spy.appender.Slf4JLogger\n# 设置 p6spy driver 代理\n#deregisterdrivers=true\n# 取消JDBC URL前缀\nuseprefix=true\n# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.\nexcludecategories=info,debug,result,commit,resultset\n# 日期格式\ndateformat=yyyy-MM-dd HH:mm:ss\n# SQL语句打印时间格式\ndatabaseDialectTimestampFormat=yyyy-MM-dd HH:mm:ss\n# 实际驱动可多个\n#driverlist=org.h2.Driver\n# 是否开启慢SQL记录\noutagedetection=true\n# 慢SQL记录标准 2 秒\noutagedetectioninterval=2\n# 是否过滤 Log\nfilter=true\n# 过滤 Log 时所排除的 sql 关键字，以逗号分隔\nexclude=SELECT 1\n"
  },
  {
    "path": "ruoyi-common/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>ruoyi-vue-plus</artifactId>\n        <groupId>com.ruoyi</groupId>\n        <version>4.7.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ruoyi-common</artifactId>\n\n    <description>\n        common通用工具\n    </description>\n\n    <dependencies>\n\n        <!-- Spring框架基本的核心工具 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context-support</artifactId>\n        </dependency>\n\n        <!-- SpringWeb模块 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n        </dependency>\n\n        <!-- Sa-Token 权限认证, 在线文档：http://sa-token.dev33.cn/ -->\n        <dependency>\n            <groupId>cn.dev33</groupId>\n            <artifactId>sa-token-spring-boot-starter</artifactId>\n        </dependency>\n        <!-- Sa-Token 整合 jwt -->\n        <dependency>\n            <groupId>cn.dev33</groupId>\n            <artifactId>sa-token-jwt</artifactId>\n        </dependency>\n\n        <!-- 自定义验证注解 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n        </dependency>\n\n        <!--常用工具类 -->\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n\n        <!-- JSON工具类 -->\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-databind</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>easyexcel</artifactId>\n        </dependency>\n\n        <!-- yml解析器 -->\n        <dependency>\n            <groupId>org.yaml</groupId>\n            <artifactId>snakeyaml</artifactId>\n        </dependency>\n\n        <!-- servlet包 -->\n        <dependency>\n            <groupId>javax.servlet</groupId>\n            <artifactId>javax.servlet-api</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus-boot-starter</artifactId>\n        </dependency>\n\n        <!-- dynamic-datasource 多数据源-->\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.hutool</groupId>\n            <artifactId>hutool-core</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.hutool</groupId>\n            <artifactId>hutool-http</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.hutool</groupId>\n            <artifactId>hutool-captcha</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.hutool</groupId>\n            <artifactId>hutool-jwt</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.hutool</groupId>\n            <artifactId>hutool-extra</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.sun.mail</groupId>\n            <artifactId>jakarta.mail</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springdoc</groupId>\n            <artifactId>springdoc-openapi-webmvc-core</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springdoc</groupId>\n            <artifactId>springdoc-openapi-javadoc</artifactId>\n        </dependency>\n\n        <!--  自动生成YML配置关联JSON文件  -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-configuration-processor</artifactId>\n        </dependency>\n\n        <!--redisson-->\n        <dependency>\n            <groupId>org.redisson</groupId>\n            <artifactId>redisson-spring-boot-starter</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.redisson</groupId>\n            <artifactId>redisson-spring-data-27</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>lock4j-redisson-spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 加密包引入 -->\n        <dependency>\n            <groupId>org.bouncycastle</groupId>\n            <artifactId>bcprov-jdk15to18</artifactId>\n        </dependency>\n\n        <!-- 离线IP地址定位库 -->\n        <dependency>\n            <groupId>org.lionsoul</groupId>\n            <artifactId>ip2region</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/annotation/CellMerge.java",
    "content": "package top.flya.common.annotation;\n\nimport top.flya.common.excel.CellMergeStrategy;\n\nimport java.lang.annotation.*;\n\n/**\n * excel 列单元格合并(合并列相同项)\n *\n * 需搭配 {@link CellMergeStrategy} 策略使用\n *\n * @author Lion Li\n */\n@Target(ElementType.FIELD)\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\npublic @interface CellMerge {\n\n\t/**\n\t * col index\n\t */\n\tint index() default -1;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/annotation/DataColumn.java",
    "content": "package top.flya.common.annotation;\n\nimport java.lang.annotation.*;\n\n/**\n * 数据权限\n *\n * 一个注解只能对应一个模板\n *\n * @author Lion Li\n * @version 3.5.0\n */\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface DataColumn {\n\n    /**\n     * 占位符关键字\n     */\n    String[] key() default \"deptName\";\n\n    /**\n     * 占位符替换值\n     */\n    String[] value() default \"dept_id\";\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/annotation/DataPermission.java",
    "content": "package top.flya.common.annotation;\n\nimport java.lang.annotation.*;\n\n/**\n * 数据权限组\n *\n * @author Lion Li\n * @version 3.5.0\n */\n@Target({ElementType.METHOD, ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface DataPermission {\n\n    DataColumn[] value();\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/annotation/DictDataMapper.java",
    "content": "package top.flya.common.annotation;\n\nimport com.fasterxml.jackson.annotation.JacksonAnnotationsInside;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport top.flya.common.jackson.DictDataJsonSerializer;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 字典数据映射注解\n *\n * @author itino\n * @deprecated 建议使用通用翻译注解\n */\n@Deprecated\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.FIELD, ElementType.METHOD})\n@JacksonAnnotationsInside\n@JsonSerialize(using = DictDataJsonSerializer.class)\npublic @interface DictDataMapper {\n\n    /**\n     * 设置字典的type值 (如: sys_user_sex)\n     */\n    String dictType() default \"\";\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/annotation/EncryptField.java",
    "content": "package top.flya.common.annotation;\n\nimport top.flya.common.enums.AlgorithmType;\nimport top.flya.common.enums.EncodeType;\n\nimport java.lang.annotation.*;\n\n/**\n * 字段加密注解\n *\n * @author 老马\n */\n@Documented\n@Inherited\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface EncryptField {\n\n    /**\n     * 加密算法\n     */\n    AlgorithmType algorithm() default AlgorithmType.DEFAULT;\n\n    /**\n     * 秘钥。AES、SM4需要\n     */\n    String password() default \"\";\n\n    /**\n     * 公钥。RSA、SM2需要\n     */\n    String publicKey() default \"\";\n\n    /**\n     * 公钥。RSA、SM2需要\n     */\n    String privateKey() default \"\";\n\n    /**\n     * 编码方式。对加密算法为BASE64的不起作用\n     */\n    EncodeType encode() default EncodeType.DEFAULT;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/annotation/ExcelDictFormat.java",
    "content": "package top.flya.common.annotation;\n\nimport top.flya.common.utils.StringUtils;\n\nimport java.lang.annotation.*;\n\n/**\n * 字典格式化\n *\n * @author Lion Li\n */\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\npublic @interface ExcelDictFormat {\n\n    /**\n     * 如果是字典类型，请设置字典的type值 (如: sys_user_sex)\n     */\n    String dictType() default \"\";\n\n    /**\n     * 读取内容转表达式 (如: 0=男,1=女,2=未知)\n     */\n    String readConverterExp() default \"\";\n\n    /**\n     * 分隔符，读取字符串组内容\n     */\n    String separator() default StringUtils.SEPARATOR;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/annotation/ExcelEnumFormat.java",
    "content": "package top.flya.common.annotation;\n\nimport java.lang.annotation.*;\n\n/**\n * 枚举格式化\n *\n * @author Liang\n */\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\npublic @interface ExcelEnumFormat {\n\n    /**\n     * 字典枚举类型\n     */\n    Class<? extends Enum<?>> enumClass();\n\n    /**\n     * 字典枚举类中对应的code属性名称，默认为code\n     */\n    String codeField() default \"code\";\n\n    /**\n     * 字典枚举类中对应的text属性名称，默认为text\n     */\n    String textField() default \"text\";\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/annotation/Log.java",
    "content": "package top.flya.common.annotation;\n\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.enums.OperatorType;\n\nimport java.lang.annotation.*;\n\n/**\n * 自定义操作日志记录注解\n *\n * @author ruoyi\n */\n@Target({ElementType.PARAMETER, ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Log {\n    /**\n     * 模块\n     */\n    String title() default \"\";\n\n    /**\n     * 功能\n     */\n    BusinessType businessType() default BusinessType.OTHER;\n\n    /**\n     * 操作人类别\n     */\n    OperatorType operatorType() default OperatorType.MANAGE;\n\n    /**\n     * 是否保存请求的参数\n     */\n    boolean isSaveRequestData() default true;\n\n    /**\n     * 是否保存响应的参数\n     */\n    boolean isSaveResponseData() default true;\n\n    /**\n     * 排除指定的请求参数\n     */\n    String[] excludeParamNames() default {};\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/annotation/RateLimiter.java",
    "content": "package top.flya.common.annotation;\n\nimport top.flya.common.enums.LimitType;\n\nimport java.lang.annotation.*;\n\n/**\n * 限流注解\n *\n * @author Lion Li\n */\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface RateLimiter {\n    /**\n     * 限流key,支持使用Spring el表达式来动态获取方法上的参数值\n     * 格式类似于  #code.id #{#code}\n     */\n    String key() default \"\";\n\n    /**\n     * 限流时间,单位秒\n     */\n    int time() default 60;\n\n    /**\n     * 限流次数\n     */\n    int count() default 100;\n\n    /**\n     * 限流类型\n     */\n    LimitType limitType() default LimitType.DEFAULT;\n\n    /**\n     * 提示消息 支持国际化 格式为 {code}\n     */\n    String message() default \"{rate.limiter.message}\";\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/annotation/RepeatSubmit.java",
    "content": "package top.flya.common.annotation;\n\nimport java.lang.annotation.*;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 自定义注解防止表单重复提交\n *\n * @author Lion Li\n */\n@Inherited\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface RepeatSubmit {\n\n    /**\n     * 间隔时间(ms)，小于此时间视为重复提交\n     */\n    int interval() default 5000;\n\n    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;\n\n    /**\n     * 提示消息 支持国际化 格式为 {code}\n     */\n    String message() default \"{repeat.submit.message}\";\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/annotation/Sensitive.java",
    "content": "package top.flya.common.annotation;\n\nimport com.fasterxml.jackson.annotation.JacksonAnnotationsInside;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport top.flya.common.enums.SensitiveStrategy;\nimport top.flya.common.jackson.SensitiveJsonSerializer;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 数据脱敏注解\n *\n * @author zhujie\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.FIELD)\n@JacksonAnnotationsInside\n@JsonSerialize(using = SensitiveJsonSerializer.class)\npublic @interface Sensitive {\n    SensitiveStrategy strategy();\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/annotation/Translation.java",
    "content": "package top.flya.common.annotation;\n\nimport com.fasterxml.jackson.annotation.JacksonAnnotationsInside;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport top.flya.common.translation.handler.TranslationHandler;\n\nimport java.lang.annotation.*;\n\n/**\n * 通用翻译注解\n *\n * @author Lion Li\n */\n@Inherited\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.FIELD, ElementType.METHOD})\n@Documented\n@JacksonAnnotationsInside\n@JsonSerialize(using = TranslationHandler.class)\npublic @interface Translation {\n\n    /**\n     * 类型 (需与实现类上的 {@link TranslationType} 注解type对应)\n     * <p>\n     * 默认取当前字段的值 如果设置了 @{@link Translation#mapper()} 则取映射字段的值\n     */\n    String type();\n\n    /**\n     * 映射字段 (如果不为空则取此字段的值)\n     */\n    String mapper() default \"\";\n\n    /**\n     * 其他条件 例如: 字典type(sys_user_sex)\n     */\n    String other() default \"\";\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/annotation/TranslationType.java",
    "content": "package top.flya.common.annotation;\n\nimport top.flya.common.translation.TranslationInterface;\n\nimport java.lang.annotation.*;\n\n/**\n * 翻译类型注解 (标注到{@link TranslationInterface} 的实现类)\n *\n * @author Lion Li\n */\n@Inherited\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.TYPE})\n@Documented\npublic @interface TranslationType {\n\n    /**\n     * 类型\n     */\n    String type();\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/captcha/UnsignedMathGenerator.java",
    "content": "package top.flya.common.captcha;\n\nimport cn.hutool.captcha.generator.CodeGenerator;\nimport cn.hutool.core.math.Calculator;\nimport cn.hutool.core.util.CharUtil;\nimport cn.hutool.core.util.RandomUtil;\nimport top.flya.common.utils.StringUtils;\n\n/**\n * 无符号计算生成器\n *\n * @author Lion Li\n */\npublic class UnsignedMathGenerator implements CodeGenerator {\n\n    private static final long serialVersionUID = -5514819971774091076L;\n\n    private static final String OPERATORS = \"+-*\";\n\n    /**\n     * 参与计算数字最大长度\n     */\n    private final int numberLength;\n\n    /**\n     * 构造\n     */\n    public UnsignedMathGenerator() {\n        this(2);\n    }\n\n    /**\n     * 构造\n     *\n     * @param numberLength 参与计算最大数字位数\n     */\n    public UnsignedMathGenerator(int numberLength) {\n        this.numberLength = numberLength;\n    }\n\n    @Override\n    public String generate() {\n        final int limit = getLimit();\n        int a = RandomUtil.randomInt(limit);\n        int b = RandomUtil.randomInt(limit);\n        String max = Integer.toString(Math.max(a,b));\n        String min = Integer.toString(Math.min(a,b));\n        max = StringUtils.rightPad(max, this.numberLength, CharUtil.SPACE);\n        min = StringUtils.rightPad(min, this.numberLength, CharUtil.SPACE);\n\n        return max + RandomUtil.randomChar(OPERATORS) + min + '=';\n    }\n\n    @Override\n    public boolean verify(String code, String userInputCode) {\n        int result;\n        try {\n            result = Integer.parseInt(userInputCode);\n        } catch (NumberFormatException e) {\n            // 用户输入非数字\n            return false;\n        }\n\n        final int calculateResult = (int) Calculator.conversion(code);\n        return result == calculateResult;\n    }\n\n    /**\n     * 获取验证码长度\n     *\n     * @return 验证码长度\n     */\n    public int getLength() {\n        return this.numberLength * 2 + 2;\n    }\n\n    /**\n     * 根据长度获取参与计算数字最大值\n     *\n     * @return 最大值\n     */\n    private int getLimit() {\n        return Integer.parseInt(\"1\" + StringUtils.repeat('0', this.numberLength));\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/config/RuoYiConfig.java",
    "content": "package top.flya.common.config;\n\nimport lombok.Data;\nimport lombok.Getter;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n/**\n * 读取项目相关配置\n *\n * @author Lion Li\n */\n\n@Data\n@Component\n@ConfigurationProperties(prefix = \"ruoyi\")\npublic class RuoYiConfig {\n\n    /**\n     * 项目名称\n     */\n    private String name;\n\n    /**\n     * 版本\n     */\n    private String version;\n\n    /**\n     * 版权年份\n     */\n    private String copyrightYear;\n\n    /**\n     * 实例演示开关\n     */\n    private boolean demoEnabled;\n\n    /**\n     * 缓存懒加载\n     */\n    private boolean cacheLazy;\n\n    /**\n     * 获取地址开关\n     */\n    @Getter\n    private static boolean addressEnabled;\n\n    public void setAddressEnabled(boolean addressEnabled) {\n        RuoYiConfig.addressEnabled = addressEnabled;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/constant/CacheConstants.java",
    "content": "package top.flya.common.constant;\n\n/**\n * 缓存的key 常量\n *\n * @author ruoyi\n */\npublic interface CacheConstants {\n\n    /**\n     * 在线用户 redis key\n     */\n    String ONLINE_TOKEN_KEY = \"online_tokens:\";\n\n    /**\n     * 验证码 redis key\n     */\n    String CAPTCHA_CODE_KEY = \"captcha_codes:\";\n\n    /**\n     * 参数管理 cache key\n     */\n    String SYS_CONFIG_KEY = \"sys_config:\";\n\n    /**\n     * 字典管理 cache key\n     */\n    String SYS_DICT_KEY = \"sys_dict:\";\n\n    /**\n     * 防重提交 redis key\n     */\n    String REPEAT_SUBMIT_KEY = \"repeat_submit:\";\n\n    /**\n     * 限流 redis key\n     */\n    String RATE_LIMIT_KEY = \"rate_limit:\";\n\n    /**\n     * 登录账户密码错误次数 redis key\n     */\n    String PWD_ERR_CNT_KEY = \"pwd_err_cnt:\";\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/constant/CacheNames.java",
    "content": "package top.flya.common.constant;\n\n/**\n * 缓存组名称常量\n * <p>\n * key 格式为 cacheNames#ttl#maxIdleTime#maxSize\n * <p>\n * ttl 过期时间 如果设置为0则不过期 默认为0\n * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0\n * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0\n * <p>\n * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500\n *\n * @author Lion Li\n */\npublic interface CacheNames {\n\n    /**\n     * 演示案例\n     */\n    String DEMO_CACHE = \"demo:cache#60s#10m#20\";\n\n    /**\n     * 系统配置\n     */\n    String SYS_CONFIG = \"sys_config\";\n\n    /**\n     * 数据字典\n     */\n    String SYS_DICT = \"sys_dict\";\n\n    /**\n     * 用户账户\n     */\n    String SYS_USER_NAME = \"sys_user_name#30d\";\n\n    /**\n     * 部门\n     */\n    String SYS_DEPT = \"sys_dept#30d\";\n\n    /**\n     * OSS内容\n     */\n    String SYS_OSS = \"sys_oss#30d\";\n\n    /**\n     * OSS配置\n     */\n    String SYS_OSS_CONFIG = \"sys_oss_config\";\n\n    /**\n     * 在线用户\n     */\n    String ONLINE_TOKEN = \"online_tokens\";\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/constant/Constants.java",
    "content": "package top.flya.common.constant;\n\n/**\n * 通用常量信息\n *\n * @author ruoyi\n */\npublic interface Constants {\n\n    /**\n     * UTF-8 字符集\n     */\n    String UTF8 = \"UTF-8\";\n\n    /**\n     * GBK 字符集\n     */\n    String GBK = \"GBK\";\n\n    /**\n     * www主域\n     */\n    String WWW = \"www.\";\n\n    /**\n     * http请求\n     */\n    String HTTP = \"http://\";\n\n    /**\n     * https请求\n     */\n    String HTTPS = \"https://\";\n\n    /**\n     * 通用成功标识\n     */\n    String SUCCESS = \"0\";\n\n    /**\n     * 通用失败标识\n     */\n    String FAIL = \"1\";\n\n    /**\n     * 登录成功\n     */\n    String LOGIN_SUCCESS = \"Success\";\n\n    /**\n     * 注销\n     */\n    String LOGOUT = \"Logout\";\n\n    /**\n     * 注册\n     */\n    String REGISTER = \"Register\";\n\n    /**\n     * 登录失败\n     */\n    String LOGIN_FAIL = \"Error\";\n\n    /**\n     * 验证码有效期（分钟）\n     */\n    Integer CAPTCHA_EXPIRATION = 2;\n\n    /**\n     * 令牌\n     */\n    String TOKEN = \"token\";\n\n}\n\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/constant/GenConstants.java",
    "content": "package top.flya.common.constant;\n\n/**\n * 代码生成通用常量\n *\n * @author ruoyi\n */\npublic interface GenConstants {\n    /**\n     * 单表（增删改查）\n     */\n    String TPL_CRUD = \"crud\";\n\n    /**\n     * 树表（增删改查）\n     */\n    String TPL_TREE = \"tree\";\n\n    /**\n     * 主子表（增删改查）\n     */\n    String TPL_SUB = \"sub\";\n\n    /**\n     * 树编码字段\n     */\n    String TREE_CODE = \"treeCode\";\n\n    /**\n     * 树父编码字段\n     */\n    String TREE_PARENT_CODE = \"treeParentCode\";\n\n    /**\n     * 树名称字段\n     */\n    String TREE_NAME = \"treeName\";\n\n    /**\n     * 上级菜单ID字段\n     */\n    String PARENT_MENU_ID = \"parentMenuId\";\n\n    /**\n     * 上级菜单名称字段\n     */\n    String PARENT_MENU_NAME = \"parentMenuName\";\n\n    /**\n     * 数据库字符串类型\n     */\n    String[] COLUMNTYPE_STR = {\"char\", \"varchar\", \"nvarchar\", \"varchar2\"};\n\n    /**\n     * 数据库文本类型\n     */\n    String[] COLUMNTYPE_TEXT = {\"tinytext\", \"text\", \"mediumtext\", \"longtext\"};\n\n    /**\n     * 数据库时间类型\n     */\n    String[] COLUMNTYPE_TIME = {\"datetime\", \"time\", \"date\", \"timestamp\"};\n\n    /**\n     * 数据库数字类型\n     */\n    String[] COLUMNTYPE_NUMBER = {\"tinyint\", \"smallint\", \"mediumint\", \"int\", \"number\", \"integer\",\n        \"bit\", \"bigint\", \"float\", \"double\", \"decimal\"};\n\n    /**\n     * BO对象 不需要添加字段\n     */\n    String[] COLUMNNAME_NOT_ADD = {\"create_by\", \"create_time\", \"del_flag\", \"update_by\",\n        \"update_time\", \"version\"};\n\n    /**\n     * BO对象 不需要编辑字段\n     */\n    String[] COLUMNNAME_NOT_EDIT = {\"create_by\", \"create_time\", \"del_flag\", \"update_by\",\n        \"update_time\", \"version\"};\n\n    /**\n     * VO对象 不需要返回字段\n     */\n    String[] COLUMNNAME_NOT_LIST = {\"create_by\", \"create_time\", \"del_flag\", \"update_by\",\n        \"update_time\", \"version\"};\n\n    /**\n     * BO对象 不需要查询字段\n     */\n    String[] COLUMNNAME_NOT_QUERY = {\"id\", \"create_by\", \"create_time\", \"del_flag\", \"update_by\",\n        \"update_time\", \"remark\", \"version\"};\n\n    /**\n     * Entity基类字段\n     */\n    String[] BASE_ENTITY = {\"createBy\", \"createTime\", \"updateBy\", \"updateTime\"};\n\n    /**\n     * Tree基类字段\n     */\n    String[] TREE_ENTITY = {\"parentName\", \"parentId\", \"children\"};\n\n    /**\n     * 文本框\n     */\n    String HTML_INPUT = \"input\";\n\n    /**\n     * 文本域\n     */\n    String HTML_TEXTAREA = \"textarea\";\n\n    /**\n     * 下拉框\n     */\n    String HTML_SELECT = \"select\";\n\n    /**\n     * 单选框\n     */\n    String HTML_RADIO = \"radio\";\n\n    /**\n     * 复选框\n     */\n    String HTML_CHECKBOX = \"checkbox\";\n\n    /**\n     * 日期控件\n     */\n    String HTML_DATETIME = \"datetime\";\n\n    /**\n     * 图片上传控件\n     */\n    String HTML_IMAGE_UPLOAD = \"imageUpload\";\n\n    /**\n     * 文件上传控件\n     */\n    String HTML_FILE_UPLOAD = \"fileUpload\";\n\n    /**\n     * 富文本控件\n     */\n    String HTML_EDITOR = \"editor\";\n\n    /**\n     * 字符串类型\n     */\n    String TYPE_STRING = \"String\";\n\n    /**\n     * 整型\n     */\n    String TYPE_INTEGER = \"Integer\";\n\n    /**\n     * 长整型\n     */\n    String TYPE_LONG = \"Long\";\n\n    /**\n     * 浮点型\n     */\n    String TYPE_DOUBLE = \"Double\";\n\n    /**\n     * 高精度计算类型\n     */\n    String TYPE_BIGDECIMAL = \"BigDecimal\";\n\n    /**\n     * 时间类型\n     */\n    String TYPE_DATE = \"Date\";\n\n    /**\n     * 模糊查询\n     */\n    String QUERY_LIKE = \"LIKE\";\n\n    /**\n     * 相等查询\n     */\n    String QUERY_EQ = \"EQ\";\n\n    /**\n     * 需要\n     */\n    String REQUIRE = \"1\";\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/constant/HttpStatus.java",
    "content": "package top.flya.common.constant;\n\n/**\n * 返回状态码\n *\n * @author Lion Li\n */\npublic interface HttpStatus {\n    /**\n     * 操作成功\n     */\n    int SUCCESS = 200;\n\n    /**\n     * 对象创建成功\n     */\n    int CREATED = 201;\n\n    /**\n     * 请求已经被接受\n     */\n    int ACCEPTED = 202;\n\n    /**\n     * 操作已经执行成功，但是没有返回数据\n     */\n    int NO_CONTENT = 204;\n\n    /**\n     * 资源已被移除\n     */\n    int MOVED_PERM = 301;\n\n    /**\n     * 重定向\n     */\n    int SEE_OTHER = 303;\n\n    /**\n     * 资源没有被修改\n     */\n    int NOT_MODIFIED = 304;\n\n    /**\n     * 参数列表错误（缺少，格式不匹配）\n     */\n    int BAD_REQUEST = 400;\n\n    /**\n     * 未授权\n     */\n    int UNAUTHORIZED = 401;\n\n    /**\n     * 访问受限，授权过期\n     */\n    int FORBIDDEN = 403;\n\n    /**\n     * 资源，服务未找到\n     */\n    int NOT_FOUND = 404;\n\n    /**\n     * 不允许的http方法\n     */\n    int BAD_METHOD = 405;\n\n    /**\n     * 资源冲突，或者资源被锁\n     */\n    int CONFLICT = 409;\n\n    /**\n     * 不支持的数据，媒体类型\n     */\n    int UNSUPPORTED_TYPE = 415;\n\n    /**\n     * 系统内部错误\n     */\n    int ERROR = 500;\n\n    /**\n     * 接口未实现\n     */\n    int NOT_IMPLEMENTED = 501;\n\n    /**\n     * 系统警告消息\n     */\n    int WARN = 601;\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/constant/TransConstant.java",
    "content": "package top.flya.common.constant;\n\n/**\n * 翻译常量\n *\n * @author Lion Li\n */\npublic interface TransConstant {\n\n    /**\n     * 用户id转账号\n     */\n    String USER_ID_TO_NAME = \"user_id_to_name\";\n\n    /**\n     * 部门id转名称\n     */\n    String DEPT_ID_TO_NAME = \"dept_id_to_name\";\n\n    /**\n     * 字典type转label\n     */\n    String DICT_TYPE_TO_LABEL = \"dict_type_to_label\";\n\n    /**\n     * ossId转url\n     */\n    String OSS_ID_TO_URL = \"oss_id_to_url\";\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/constant/UserConstants.java",
    "content": "package top.flya.common.constant;\n\n/**\n * 用户常量信息\n *\n * @author ruoyi\n */\npublic interface UserConstants {\n\n    /**\n     * 平台内系统用户的唯一标志\n     */\n    String SYS_USER = \"SYS_USER\";\n\n    /**\n     * 正常状态\n     */\n    String NORMAL = \"0\";\n\n    /**\n     * 异常状态\n     */\n    String EXCEPTION = \"1\";\n\n    /**\n     * 用户正常状态\n     */\n    String USER_NORMAL = \"0\";\n\n    /**\n     * 用户封禁状态\n     */\n    String USER_DISABLE = \"1\";\n\n    /**\n     * 角色正常状态\n     */\n    String ROLE_NORMAL = \"0\";\n\n    /**\n     * 角色封禁状态\n     */\n    String ROLE_DISABLE = \"1\";\n\n    /**\n     * 部门正常状态\n     */\n    String DEPT_NORMAL = \"0\";\n\n    /**\n     * 部门停用状态\n     */\n    String DEPT_DISABLE = \"1\";\n\n    /**\n     * 字典正常状态\n     */\n    String DICT_NORMAL = \"0\";\n\n    /**\n     * 是否为系统默认（是）\n     */\n    String YES = \"Y\";\n\n    /**\n     * 是否菜单外链（是）\n     */\n    String YES_FRAME = \"0\";\n\n    /**\n     * 是否菜单外链（否）\n     */\n    String NO_FRAME = \"1\";\n\n    /**\n     * 菜单正常状态\n     */\n    String MENU_NORMAL = \"0\";\n\n    /**\n     * 菜单停用状态\n     */\n    String MENU_DISABLE = \"1\";\n\n    /**\n     * 菜单类型（目录）\n     */\n    String TYPE_DIR = \"M\";\n\n    /**\n     * 菜单类型（菜单）\n     */\n    String TYPE_MENU = \"C\";\n\n    /**\n     * 菜单类型（按钮）\n     */\n    String TYPE_BUTTON = \"F\";\n\n    /**\n     * Layout组件标识\n     */\n    String LAYOUT = \"Layout\";\n\n    /**\n     * ParentView组件标识\n     */\n    String PARENT_VIEW = \"ParentView\";\n\n    /**\n     * InnerLink组件标识\n     */\n    String INNER_LINK = \"InnerLink\";\n\n    /**\n     * 用户名长度限制\n     */\n    int USERNAME_MIN_LENGTH = 2;\n    int USERNAME_MAX_LENGTH = 20;\n\n    /**\n     * 密码长度限制\n     */\n    int PASSWORD_MIN_LENGTH = 5;\n    int PASSWORD_MAX_LENGTH = 20;\n\n    /**\n     * 管理员ID\n     */\n    Long ADMIN_ID = 1L;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/convert/ExcelBigNumberConvert.java",
    "content": "package top.flya.common.convert;\n\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.alibaba.excel.converters.Converter;\nimport com.alibaba.excel.enums.CellDataTypeEnum;\nimport com.alibaba.excel.metadata.GlobalConfiguration;\nimport com.alibaba.excel.metadata.data.ReadCellData;\nimport com.alibaba.excel.metadata.data.WriteCellData;\nimport com.alibaba.excel.metadata.property.ExcelContentProperty;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.math.BigDecimal;\n\n/**\n * 大数值转换\n * Excel 数值长度位15位 大于15位的数值转换位字符串\n *\n * @author Lion Li\n */\n@Slf4j\npublic class ExcelBigNumberConvert implements Converter<Long> {\n\n    @Override\n    public Class<Long> supportJavaTypeKey() {\n        return Long.class;\n    }\n\n    @Override\n    public CellDataTypeEnum supportExcelTypeKey() {\n        return CellDataTypeEnum.STRING;\n    }\n\n    @Override\n    public Long convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {\n        return Convert.toLong(cellData.getData());\n    }\n\n    @Override\n    public WriteCellData<Object> convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {\n        if (ObjectUtil.isNotNull(object)) {\n            String str = Convert.toStr(object);\n            if (str.length() > 15) {\n                return new WriteCellData<>(str);\n            }\n        }\n        WriteCellData<Object> cellData = new WriteCellData<>(new BigDecimal(object));\n        cellData.setType(CellDataTypeEnum.NUMBER);\n        return cellData;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/convert/ExcelDictConvert.java",
    "content": "package top.flya.common.convert;\n\nimport cn.hutool.core.annotation.AnnotationUtil;\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.alibaba.excel.converters.Converter;\nimport com.alibaba.excel.enums.CellDataTypeEnum;\nimport com.alibaba.excel.metadata.GlobalConfiguration;\nimport com.alibaba.excel.metadata.data.ReadCellData;\nimport com.alibaba.excel.metadata.data.WriteCellData;\nimport com.alibaba.excel.metadata.property.ExcelContentProperty;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.core.service.DictService;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.lang.reflect.Field;\n\n/**\n * 字典格式化转换处理\n *\n * @author Lion Li\n */\n@Slf4j\npublic class ExcelDictConvert implements Converter<Object> {\n\n    @Override\n    public Class<Object> supportJavaTypeKey() {\n        return Object.class;\n    }\n\n    @Override\n    public CellDataTypeEnum supportExcelTypeKey() {\n        return null;\n    }\n\n    @Override\n    public Object convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {\n        ExcelDictFormat anno = getAnnotation(contentProperty.getField());\n        String type = anno.dictType();\n        String label = cellData.getStringValue();\n        String value;\n        if (StringUtils.isBlank(type)) {\n            value = ExcelUtil.reverseByExp(label, anno.readConverterExp(), anno.separator());\n        } else {\n            value = SpringUtils.getBean(DictService.class).getDictValue(type, label, anno.separator());\n        }\n        return Convert.convert(contentProperty.getField().getType(), value);\n    }\n\n    @Override\n    public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {\n        if (ObjectUtil.isNull(object)) {\n            return new WriteCellData<>(\"\");\n        }\n        ExcelDictFormat anno = getAnnotation(contentProperty.getField());\n        String type = anno.dictType();\n        String value = Convert.toStr(object);\n        String label;\n        if (StringUtils.isBlank(type)) {\n            label = ExcelUtil.convertByExp(value, anno.readConverterExp(), anno.separator());\n        } else {\n            label = SpringUtils.getBean(DictService.class).getDictLabel(type, value, anno.separator());\n        }\n        return new WriteCellData<>(label);\n    }\n\n    private ExcelDictFormat getAnnotation(Field field) {\n        return AnnotationUtil.getAnnotation(field, ExcelDictFormat.class);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/convert/ExcelEnumConvert.java",
    "content": "package top.flya.common.convert;\n\nimport cn.hutool.core.annotation.AnnotationUtil;\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.alibaba.excel.converters.Converter;\nimport com.alibaba.excel.enums.CellDataTypeEnum;\nimport com.alibaba.excel.metadata.GlobalConfiguration;\nimport com.alibaba.excel.metadata.data.ReadCellData;\nimport com.alibaba.excel.metadata.data.WriteCellData;\nimport com.alibaba.excel.metadata.property.ExcelContentProperty;\nimport top.flya.common.annotation.ExcelEnumFormat;\nimport top.flya.common.utils.reflect.ReflectUtils;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.lang.reflect.Field;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 枚举格式化转换处理\n *\n * @author Liang\n */\n@Slf4j\npublic class ExcelEnumConvert implements Converter<Object> {\n\n    @Override\n    public Class<Object> supportJavaTypeKey() {\n        return Object.class;\n    }\n\n    @Override\n    public CellDataTypeEnum supportExcelTypeKey() {\n        return null;\n    }\n\n    @Override\n    public Object convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {\n        Object codeValue = cellData.getData();\n        // 如果是空值\n        if (ObjectUtil.isNull(codeValue)) {\n            return null;\n        }\n        Map<Object, String> enumValueMap = beforeConvert(contentProperty);\n        String textValue = enumValueMap.get(codeValue);\n        return Convert.convert(contentProperty.getField().getType(), textValue);\n    }\n\n    @Override\n    public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {\n        if (ObjectUtil.isNull(object)) {\n            return new WriteCellData<>(\"\");\n        }\n        Map<Object, String> enumValueMap = beforeConvert(contentProperty);\n        String value = Convert.toStr(enumValueMap.get(object), \"\");\n        return new WriteCellData<>(value);\n    }\n\n    private Map<Object, String> beforeConvert(ExcelContentProperty contentProperty) {\n        ExcelEnumFormat anno = getAnnotation(contentProperty.getField());\n        Map<Object, String> enumValueMap = new HashMap<>();\n        Enum<?>[] enumConstants = anno.enumClass().getEnumConstants();\n        for (Enum<?> enumConstant : enumConstants) {\n            Object codeValue = ReflectUtils.invokeGetter(enumConstant, anno.codeField());\n            String textValue = ReflectUtils.invokeGetter(enumConstant, anno.textField());\n            enumValueMap.put(codeValue, textValue);\n        }\n        return enumValueMap;\n    }\n\n    private ExcelEnumFormat getAnnotation(Field field) {\n        return AnnotationUtil.getAnnotation(field, ExcelEnumFormat.class);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/controller/BaseController.java",
    "content": "package top.flya.common.core.controller;\n\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.domain.model.LoginUser;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.StringUtils;\n\n/**\n * web层通用数据处理\n *\n * @author Lion Li\n */\npublic class BaseController {\n\n    /**\n     * 响应返回结果\n     *\n     * @param rows 影响行数\n     * @return 操作结果\n     */\n    protected R<Void> toAjax(int rows) {\n        return rows > 0 ? R.ok() : R.fail();\n    }\n\n    /**\n     * 响应返回结果\n     *\n     * @param result 结果\n     * @return 操作结果\n     */\n    protected R<Void> toAjax(boolean result) {\n        return result ? R.ok() : R.fail();\n    }\n\n    /**\n     * 页面跳转\n     */\n    public String redirect(String url) {\n        return StringUtils.format(\"redirect:{}\", url);\n    }\n\n    /**\n     * 获取用户缓存信息\n     */\n    public LoginUser getLoginUser() {\n        return LoginHelper.getLoginUser();\n    }\n\n    /**\n     * 获取登录用户id\n     */\n    public Long getUserId() {\n        return LoginHelper.getUserId();\n    }\n\n    /**\n     * 获取登录部门id\n     */\n    public Long getDeptId() {\n        return LoginHelper.getDeptId();\n    }\n\n    /**\n     * 获取登录用户名\n     */\n    public String getUsername() {\n        return LoginHelper.getUsername();\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/BaseEntity.java",
    "content": "package top.flya.common.core.domain;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Entity基类\n *\n * @author Lion Li\n */\n\n@Data\npublic class BaseEntity implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 搜索值\n     */\n    @JsonIgnore\n    @TableField(exist = false)\n    private String searchValue;\n\n//    /**\n//     * 创建者\n//     */\n//    @TableField(fill = FieldFill.INSERT)\n//    private String createBy;\n\n    /**\n     * 创建时间\n     */\n    @TableField(fill = FieldFill.INSERT)\n    private Date createTime;\n\n//    /**\n//     * 更新者\n//     */\n//    @TableField(fill = FieldFill.INSERT_UPDATE)\n//    private String updateBy;\n\n    /**\n     * 更新时间\n     */\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private Date updateTime;\n\n    /**\n     * 请求参数\n     */\n    @JsonInclude(JsonInclude.Include.NON_EMPTY)\n    @TableField(exist = false)\n    private Map<String, Object> params = new HashMap<>();\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/PageQuery.java",
    "content": "package top.flya.common.core.domain;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.sql.SqlUtil;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 分页查询实体类\n *\n * @author Lion Li\n */\n\n@Data\npublic class PageQuery implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 分页大小\n     */\n    private Integer pageSize;\n\n    /**\n     * 当前页数\n     */\n    private Integer pageNum;\n\n    /**\n     * 排序列\n     */\n    private String orderByColumn;\n\n    /**\n     * 排序的方向desc或者asc\n     */\n    private String isAsc;\n\n    /**\n     * 当前记录起始索引 默认值\n     */\n    public static final int DEFAULT_PAGE_NUM = 1;\n\n    /**\n     * 每页显示记录数 默认值 默认查全部\n     */\n    public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;\n\n    public <T> Page<T> build() {\n        Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM);\n        Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE);\n\n        if (pageNum <= 0) {\n            pageNum = DEFAULT_PAGE_NUM;\n        }\n        Page<T> page = new Page<>(pageNum, pageSize);\n        List<OrderItem> orderItems = buildOrderItem();\n        if (CollUtil.isNotEmpty(orderItems)) {\n            page.addOrder(orderItems);\n        }\n        return page;\n    }\n\n    /**\n     * 构建排序\n     *\n     * 支持的用法如下:\n     * {isAsc:\"asc\",orderByColumn:\"id\"} order by id asc\n     * {isAsc:\"asc\",orderByColumn:\"id,createTime\"} order by id asc,create_time asc\n     * {isAsc:\"desc\",orderByColumn:\"id,createTime\"} order by id desc,create_time desc\n     * {isAsc:\"asc,desc\",orderByColumn:\"id,createTime\"} order by id asc,create_time desc\n     */\n    private List<OrderItem> buildOrderItem() {\n        if (StringUtils.isBlank(orderByColumn) || StringUtils.isBlank(isAsc)) {\n//            orderByColumn=\"create_time\";\n//            isAsc=\"desc\";\n            return null;\n        }\n        String orderBy = SqlUtil.escapeOrderBySql(orderByColumn);\n        orderBy = StringUtils.toUnderScoreCase(orderBy);\n\n        // 兼容前端排序类型\n        isAsc = StringUtils.replaceEach(isAsc, new String[]{\"ascending\", \"descending\"}, new String[]{\"asc\", \"desc\"});\n\n        String[] orderByArr = orderBy.split(StringUtils.SEPARATOR);\n        String[] isAscArr = isAsc.split(StringUtils.SEPARATOR);\n        if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) {\n            throw new ServiceException(\"排序参数有误\");\n        }\n\n        List<OrderItem> list = new ArrayList<>();\n        // 每个字段各自排序\n        for (int i = 0; i < orderByArr.length; i++) {\n            String orderByStr = orderByArr[i];\n            String isAscStr = isAscArr.length == 1 ? isAscArr[0] : isAscArr[i];\n            if (\"asc\".equals(isAscStr)) {\n                list.add(OrderItem.asc(orderByStr));\n            } else if (\"desc\".equals(isAscStr)) {\n                list.add(OrderItem.desc(orderByStr));\n            } else {\n                throw new ServiceException(\"排序参数有误\");\n            }\n        }\n        return list;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/R.java",
    "content": "package top.flya.common.core.domain;\n\nimport top.flya.common.constant.HttpStatus;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\n\n/**\n * 响应信息主体\n *\n * @author Lion Li\n */\n@Data\n@NoArgsConstructor\npublic class R<T> implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 成功\n     */\n    public static final int SUCCESS = 200;\n\n    /**\n     * 失败\n     */\n    public static final int FAIL = 500;\n\n    private int code;\n\n    private String msg;\n\n    private T data;\n\n    public static <T> R<T> ok() {\n        return restResult(null, SUCCESS, \"操作成功\");\n    }\n\n    public static <T> R<T> ok(T data) {\n        return restResult(data, SUCCESS, \"操作成功\");\n    }\n\n    public static <T> R<T> ok(String msg) {\n        return restResult(null, SUCCESS, msg);\n    }\n\n    public static <T> R<T> ok(String msg, T data) {\n        return restResult(data, SUCCESS, msg);\n    }\n\n    public static <T> R<T> fail() {\n        return restResult(null, FAIL, \"操作失败\");\n    }\n\n    public static <T> R<T> fail(String msg) {\n        return restResult(null, FAIL, msg);\n    }\n\n    public static <T> R<T> fail(T data) {\n        return restResult(data, FAIL, \"操作失败\");\n    }\n\n    public static <T> R<T> fail(String msg, T data) {\n        return restResult(data, FAIL, msg);\n    }\n\n    public static <T> R<T> fail(int code, String msg) {\n        return restResult(null, code, msg);\n    }\n\n    /**\n     * 返回警告消息\n     *\n     * @param msg 返回内容\n     * @return 警告消息\n     */\n    public static <T> R<T> warn(String msg) {\n        return restResult(null, HttpStatus.WARN, msg);\n    }\n\n    /**\n     * 返回警告消息\n     *\n     * @param msg 返回内容\n     * @param data 数据对象\n     * @return 警告消息\n     */\n    public static <T> R<T> warn(String msg, T data) {\n        return restResult(data, HttpStatus.WARN, msg);\n    }\n\n    private static <T> R<T> restResult(T data, int code, String msg) {\n        R<T> r = new R<>();\n        r.setCode(code);\n        r.setData(data);\n        r.setMsg(msg);\n        return r;\n    }\n\n    public static <T> Boolean isError(R<T> ret) {\n        return !isSuccess(ret);\n    }\n\n    public static <T> Boolean isSuccess(R<T> ret) {\n        return R.SUCCESS == ret.getCode();\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/TreeEntity.java",
    "content": "package top.flya.common.core.domain;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Tree基类\n *\n * @author Lion Li\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class TreeEntity<T> extends BaseEntity {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 父菜单名称\n     */\n    @TableField(exist = false)\n    private String parentName;\n\n    /**\n     * 父菜单ID\n     */\n    private Long parentId;\n\n    /**\n     * 子部门\n     */\n    @TableField(exist = false)\n    private List<T> children = new ArrayList<>();\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/dto/RoleDTO.java",
    "content": "package top.flya.common.core.domain.dto;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\n\n/**\n * 角色\n *\n * @author Lion Li\n */\n\n@Data\n@NoArgsConstructor\npublic class RoleDTO implements Serializable {\n\n    /**\n     * 角色ID\n     */\n    private Long roleId;\n\n    /**\n     * 角色名称\n     */\n    private String roleName;\n\n    /**\n     * 角色权限\n     */\n    private String roleKey;\n\n    /**\n     * 数据范围（1：所有数据权限；2：自定义数据权限；3：本部门数据权限；4：本部门及以下数据权限；5：仅本人数据权限）\n     */\n    private String dataScope;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/dto/UserOnlineDTO.java",
    "content": "package top.flya.common.core.domain.dto;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\n\n/**\n * 当前在线会话\n *\n * @author ruoyi\n */\n\n@Data\n@NoArgsConstructor\npublic class UserOnlineDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 会话编号\n     */\n    private String tokenId;\n\n    /**\n     * 部门名称\n     */\n    private String deptName;\n\n    /**\n     * 用户名称\n     */\n    private String userName;\n\n    /**\n     * 登录IP地址\n     */\n    private String ipaddr;\n\n    /**\n     * 登录地址\n     */\n    private String loginLocation;\n\n    /**\n     * 浏览器类型\n     */\n    private String browser;\n\n    /**\n     * 操作系统\n     */\n    private String os;\n\n    /**\n     * 登录时间\n     */\n    private Long loginTime;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/entity/SysDept.java",
    "content": "package top.flya.common.core.domain.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport top.flya.common.core.domain.TreeEntity;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.Email;\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\n\n/**\n * 部门表 sys_dept\n *\n * @author Lion Li\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"sys_dept\")\npublic class SysDept extends TreeEntity<SysDept> {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 部门ID\n     */\n    @TableId(value = \"dept_id\")\n    private Long deptId;\n\n    /**\n     * 部门名称\n     */\n    @NotBlank(message = \"部门名称不能为空\")\n    @Size(min = 0, max = 30, message = \"部门名称长度不能超过{max}个字符\")\n    private String deptName;\n\n    /**\n     * 显示顺序\n     */\n    @NotNull(message = \"显示顺序不能为空\")\n    private Integer orderNum;\n\n    /**\n     * 负责人\n     */\n    private String leader;\n\n    /**\n     * 联系电话\n     */\n    @Size(min = 0, max = 11, message = \"联系电话长度不能超过{max}个字符\")\n    private String phone;\n\n    /**\n     * 邮箱\n     */\n    @Email(message = \"邮箱格式不正确\")\n    @Size(min = 0, max = 50, message = \"邮箱长度不能超过{max}个字符\")\n    private String email;\n\n    /**\n     * 部门状态:0正常,1停用\n     */\n    private String status;\n\n    /**\n     * 删除标志（0代表存在 2代表删除）\n     */\n    @TableLogic\n    private String delFlag;\n\n    /**\n     * 祖级列表\n     */\n    private String ancestors;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/entity/SysDictData.java",
    "content": "package top.flya.common.core.domain.entity;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.convert.ExcelDictConvert;\nimport top.flya.common.core.domain.BaseEntity;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.Size;\n\n/**\n * 字典数据表 sys_dict_data\n *\n * @author Lion Li\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"sys_dict_data\")\n@ExcelIgnoreUnannotated\npublic class SysDictData extends BaseEntity {\n\n    /**\n     * 字典编码\n     */\n    @ExcelProperty(value = \"字典编码\")\n    @TableId(value = \"dict_code\", type = IdType.AUTO)\n    private Long dictCode;\n\n    /**\n     * 字典排序\n     */\n    @ExcelProperty(value = \"字典排序\")\n    private Integer dictSort;\n\n    /**\n     * 字典标签\n     */\n    @ExcelProperty(value = \"字典标签\")\n    @NotBlank(message = \"字典标签不能为空\")\n    @Size(min = 0, max = 100, message = \"字典标签长度不能超过{max}个字符\")\n    private String dictLabel;\n\n    /**\n     * 字典键值\n     */\n    @ExcelProperty(value = \"字典键值\")\n    @NotBlank(message = \"字典键值不能为空\")\n    @Size(min = 0, max = 100, message = \"字典键值长度不能超过{max}个字符\")\n    private String dictValue;\n\n    /**\n     * 字典类型\n     */\n    @ExcelProperty(value = \"字典类型\")\n    @NotBlank(message = \"字典类型不能为空\")\n    @Size(min = 0, max = 100, message = \"字典类型长度不能超过{max}个字符\")\n    private String dictType;\n\n    /**\n     * 样式属性（其他样式扩展）\n     */\n    @Size(min = 0, max = 100, message = \"样式属性长度不能超过{max}个字符\")\n    private String cssClass;\n\n    /**\n     * 表格字典样式\n     */\n    private String listClass;\n\n    /**\n     * 是否默认（Y是 N否）\n     */\n    @ExcelProperty(value = \"是否默认\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"sys_yes_no\")\n    private String isDefault;\n\n    /**\n     * 状态（0正常 1停用）\n     */\n    @ExcelProperty(value = \"状态\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"sys_normal_disable\")\n    private String status;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    public boolean getDefault() {\n        return UserConstants.YES.equals(this.isDefault);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/entity/SysDictType.java",
    "content": "package top.flya.common.core.domain.entity;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport top.flya.common.core.domain.BaseEntity;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.Pattern;\nimport javax.validation.constraints.Size;\n\n/**\n * 字典类型表 sys_dict_type\n *\n * @author Lion Li\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"sys_dict_type\")\n@ExcelIgnoreUnannotated\npublic class SysDictType extends BaseEntity {\n\n    /**\n     * 字典主键\n     */\n    @ExcelProperty(value = \"字典主键\")\n    @TableId(value = \"dict_id\", type = IdType.AUTO)\n    private Long dictId;\n\n    /**\n     * 字典名称\n     */\n    @ExcelProperty(value = \"字典名称\")\n    @NotBlank(message = \"字典名称不能为空\")\n    @Size(min = 0, max = 100, message = \"字典类型名称长度不能超过{max}个字符\")\n    private String dictName;\n\n    /**\n     * 字典类型\n     */\n    @ExcelProperty(value = \"字典类型\")\n    @NotBlank(message = \"字典类型不能为空\")\n    @Size(min = 0, max = 100, message = \"字典类型类型长度不能超过{max}个字符\")\n    @Pattern(regexp = \"^[a-z][a-z0-9_]*$\", message = \"字典类型必须以字母开头，且只能为（小写字母，数字，下滑线）\")\n    private String dictType;\n\n    /**\n     * 状态（0正常 1停用）\n     */\n    @ExcelProperty(value = \"状态\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"sys_normal_disable\")\n    private String status;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/entity/SysMenu.java",
    "content": "package top.flya.common.core.domain.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport top.flya.common.core.domain.TreeEntity;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\n\n/**\n * 菜单权限表 sys_menu\n *\n * @author Lion Li\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"sys_menu\")\npublic class SysMenu extends TreeEntity<SysMenu> {\n\n    /**\n     * 菜单ID\n     */\n    @TableId(value = \"menu_id\", type = IdType.AUTO)\n    private Long menuId;\n\n    /**\n     * 菜单名称\n     */\n    @NotBlank(message = \"菜单名称不能为空\")\n    @Size(min = 0, max = 50, message = \"菜单名称长度不能超过{max}个字符\")\n    private String menuName;\n\n    /**\n     * 显示顺序\n     */\n    @NotNull(message = \"显示顺序不能为空\")\n    private Integer orderNum;\n\n    /**\n     * 路由地址\n     */\n    @Size(min = 0, max = 200, message = \"路由地址不能超过{max}个字符\")\n    private String path;\n\n    /**\n     * 组件路径\n     */\n    @Size(min = 0, max = 200, message = \"组件路径不能超过{max}个字符\")\n    private String component;\n\n    /**\n     * 路由参数\n     */\n    private String queryParam;\n\n    /**\n     * 是否为外链（0是 1否）\n     */\n    private String isFrame;\n\n    /**\n     * 是否缓存（0缓存 1不缓存）\n     */\n    private String isCache;\n\n    /**\n     * 类型（M目录 C菜单 F按钮）\n     */\n    @NotBlank(message = \"菜单类型不能为空\")\n    private String menuType;\n\n    /**\n     * 显示状态（0显示 1隐藏）\n     */\n    private String visible;\n\n    /**\n     * 菜单状态（0正常 1停用）\n     */\n    private String status;\n\n    /**\n     * 权限字符串\n     */\n    @JsonInclude(JsonInclude.Include.NON_NULL)\n    @Size(min = 0, max = 100, message = \"权限标识长度不能超过{max}个字符\")\n    private String perms;\n\n    /**\n     * 菜单图标\n     */\n    private String icon;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/entity/SysRole.java",
    "content": "package top.flya.common.core.domain.entity;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport com.baomidou.mybatisplus.annotation.*;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.convert.ExcelDictConvert;\nimport top.flya.common.core.domain.BaseEntity;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\n\n/**\n * 角色表 sys_role\n *\n * @author Lion Li\n */\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"sys_role\")\n@ExcelIgnoreUnannotated\npublic class SysRole extends BaseEntity {\n\n    /**\n     * 角色ID\n     */\n    @ExcelProperty(value = \"角色序号\")\n    @TableId(value = \"role_id\",type = IdType.AUTO)\n    private Long roleId;\n\n    /**\n     * 角色名称\n     */\n    @ExcelProperty(value = \"角色名称\")\n    @NotBlank(message = \"角色名称不能为空\")\n    @Size(min = 0, max = 30, message = \"角色名称长度不能超过{max}个字符\")\n    private String roleName;\n\n    /**\n     * 角色权限\n     */\n    @ExcelProperty(value = \"角色权限\")\n    @NotBlank(message = \"权限字符不能为空\")\n    @Size(min = 0, max = 100, message = \"权限字符长度不能超过{max}个字符\")\n    private String roleKey;\n\n    /**\n     * 角色排序\n     */\n    @ExcelProperty(value = \"角色排序\")\n    @NotNull(message = \"显示顺序不能为空\")\n    private Integer roleSort;\n\n    /**\n     * 数据范围（1：所有数据权限；2：自定义数据权限；3：本部门数据权限；4：本部门及以下数据权限；5：仅本人数据权限）\n     */\n    @ExcelProperty(value = \"数据范围\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(readConverterExp = \"1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限\")\n    private String dataScope;\n\n    /**\n     * 菜单树选择项是否关联显示（ 0：父子不互相关联显示 1：父子互相关联显示）\n     */\n    private Boolean menuCheckStrictly;\n\n    /**\n     * 部门树选择项是否关联显示（0：父子不互相关联显示 1：父子互相关联显示 ）\n     */\n    private Boolean deptCheckStrictly;\n\n    /**\n     * 角色状态（0正常 1停用）\n     */\n    @ExcelProperty(value = \"角色状态\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"sys_normal_disable\")\n    private String status;\n\n    /**\n     * 删除标志（0代表存在 2代表删除）\n     */\n    @TableLogic\n    private String delFlag;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 用户是否存在此角色标识 默认不存在\n     */\n    @TableField(exist = false)\n    private boolean flag = false;\n\n    /**\n     * 菜单组\n     */\n    @TableField(exist = false)\n    private Long[] menuIds;\n\n    /**\n     * 部门组（数据权限）\n     */\n    @TableField(exist = false)\n    private Long[] deptIds;\n\n    public SysRole(Long roleId) {\n        this.roleId = roleId;\n    }\n\n    public boolean isAdmin() {\n        return UserConstants.ADMIN_ID.equals(this.roleId);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/entity/SysUser.java",
    "content": "package top.flya.common.core.domain.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport top.flya.common.annotation.Sensitive;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.enums.SensitiveStrategy;\nimport top.flya.common.xss.Xss;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.Email;\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.Size;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * 用户对象 sys_user\n *\n * @author Lion Li\n */\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"sys_user\")\npublic class SysUser extends BaseEntity {\n\n    /**\n     * 用户ID\n     */\n    @TableId(value = \"user_id\",type = IdType.AUTO)\n    private Long userId;\n\n    /**\n     * 部门ID\n     */\n    private Long deptId;\n\n    /**\n     * 用户账号\n     */\n    @Xss(message = \"用户账号不能包含脚本字符\")\n    @NotBlank(message = \"用户账号不能为空\")\n    @Size(min = 0, max = 30, message = \"用户账号长度不能超过{max}个字符\")\n    private String userName;\n\n    /**\n     * 用户昵称\n     */\n    @Xss(message = \"用户昵称不能包含脚本字符\")\n    @Size(min = 0, max = 30, message = \"用户昵称长度不能超过{max}个字符\")\n    private String nickName;\n\n    /**\n     * 用户类型（sys_user系统用户）\n     */\n    private String userType;\n\n    /**\n     * 用户邮箱\n     */\n    @Sensitive(strategy = SensitiveStrategy.EMAIL)\n    @Email(message = \"邮箱格式不正确\")\n    @Size(min = 0, max = 50, message = \"邮箱长度不能超过{max}个字符\")\n    private String email;\n\n    /**\n     * 手机号码\n     */\n    @Sensitive(strategy = SensitiveStrategy.PHONE)\n    private String phonenumber;\n\n    /**\n     * 用户性别\n     */\n    private String sex;\n\n    /**\n     * 用户头像\n     */\n    private String avatar;\n\n    /**\n     * 密码\n     */\n    @TableField(\n        insertStrategy = FieldStrategy.NOT_EMPTY,\n        updateStrategy = FieldStrategy.NOT_EMPTY,\n        whereStrategy = FieldStrategy.NOT_EMPTY\n    )\n    private String password;\n\n    @JsonIgnore\n    @JsonProperty\n    public String getPassword() {\n        return password;\n    }\n\n    /**\n     * 帐号状态（0正常 1停用）\n     */\n    private String status;\n\n    /**\n     * 删除标志（0代表存在 2代表删除）\n     */\n    @TableLogic\n    private String delFlag;\n\n    /**\n     * 最后登录IP\n     */\n    private String loginIp;\n\n    /**\n     * 最后登录时间\n     */\n    private Date loginDate;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 部门对象\n     */\n    @TableField(exist = false)\n    private SysDept dept;\n\n    /**\n     * 角色对象\n     */\n    @TableField(exist = false)\n    private List<SysRole> roles;\n\n    /**\n     * 角色组\n     */\n    @TableField(exist = false)\n    private Long[] roleIds;\n\n    /**\n     * 岗位组\n     */\n    @TableField(exist = false)\n    private Long[] postIds;\n\n    /**\n     * 数据权限 当前角色ID\n     */\n    @TableField(exist = false)\n    private Long roleId;\n\n    public SysUser(Long userId) {\n        this.userId = userId;\n    }\n\n    public boolean isAdmin() {\n        return UserConstants.ADMIN_ID.equals(this.userId);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/event/LogininforEvent.java",
    "content": "package top.flya.common.core.domain.event;\n\nimport lombok.Data;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.io.Serializable;\n\n/**\n * 登录事件\n *\n * @author Lion Li\n */\n\n@Data\npublic class LogininforEvent implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 用户账号\n     */\n    private String username;\n\n    /**\n     * 登录状态 0成功 1失败\n     */\n    private String status;\n\n    /**\n     * 提示消息\n     */\n    private String message;\n\n    /**\n     * 请求体\n     */\n    private HttpServletRequest request;\n\n    /**\n     * 其他参数\n     */\n    private Object[] args;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/event/OperLogEvent.java",
    "content": "package top.flya.common.core.domain.event;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 操作日志事件\n *\n * @author Lion Li\n */\n\n@Data\npublic class OperLogEvent implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 日志主键\n     */\n    private Long operId;\n\n    /**\n     * 操作模块\n     */\n    private String title;\n\n    /**\n     * 业务类型（0其它 1新增 2修改 3删除）\n     */\n    private Integer businessType;\n\n    /**\n     * 业务类型数组\n     */\n    private Integer[] businessTypes;\n\n    /**\n     * 请求方法\n     */\n    private String method;\n\n    /**\n     * 请求方式\n     */\n    private String requestMethod;\n\n    /**\n     * 操作类别（0其它 1后台用户 2手机端用户）\n     */\n    private Integer operatorType;\n\n    /**\n     * 操作人员\n     */\n    private String operName;\n\n    /**\n     * 部门名称\n     */\n    private String deptName;\n\n    /**\n     * 请求url\n     */\n    private String operUrl;\n\n    /**\n     * 操作地址\n     */\n    private String operIp;\n\n    /**\n     * 操作地点\n     */\n    private String operLocation;\n\n    /**\n     * 请求参数\n     */\n    private String operParam;\n\n    /**\n     * 返回参数\n     */\n    private String jsonResult;\n\n    /**\n     * 操作状态（0正常 1异常）\n     */\n    private Integer status;\n\n    /**\n     * 错误消息\n     */\n    private String errorMsg;\n\n    /**\n     * 操作时间\n     */\n    private Date operTime;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/model/EmailLoginBody.java",
    "content": "package top.flya.common.core.domain.model;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.Email;\nimport javax.validation.constraints.NotBlank;\n\n/**\n * 短信登录对象\n *\n * @author Lion Li\n */\n\n@Data\npublic class EmailLoginBody {\n\n    /**\n     * 邮箱\n     */\n    @NotBlank(message = \"{user.email.not.blank}\")\n    @Email(message = \"{user.email.not.valid}\")\n    private String email;\n\n    /**\n     * 邮箱code\n     */\n    @NotBlank(message = \"{email.code.not.blank}\")\n    private String emailCode;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/model/LoginBody.java",
    "content": "package top.flya.common.core.domain.model;\n\nimport top.flya.common.constant.UserConstants;\nimport lombok.Data;\nimport org.hibernate.validator.constraints.Length;\n\nimport javax.validation.constraints.NotBlank;\n\n/**\n * 用户登录对象\n *\n * @author Lion Li\n */\n\n@Data\npublic class LoginBody {\n\n    /**\n     * 用户名\n     */\n    @NotBlank(message = \"{user.username.not.blank}\")\n    @Length(min = UserConstants.USERNAME_MIN_LENGTH, max = UserConstants.USERNAME_MAX_LENGTH, message = \"{user.username.length.valid}\")\n    private String username;\n\n    /**\n     * 用户密码\n     */\n    @NotBlank(message = \"{user.password.not.blank}\")\n    @Length(min = UserConstants.PASSWORD_MIN_LENGTH, max = UserConstants.PASSWORD_MAX_LENGTH, message = \"{user.password.length.valid}\")\n    private String password;\n\n    /**\n     * 验证码\n     */\n    private String code;\n\n    /**\n     * 唯一标识\n     */\n    private String uuid;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/model/LoginUser.java",
    "content": "package top.flya.common.core.domain.model;\n\nimport top.flya.common.core.domain.dto.RoleDTO;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * 登录用户身份权限\n *\n * @author Lion Li\n */\n\n@Data\n@NoArgsConstructor\npublic class LoginUser implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 用户ID\n     */\n    private Long userId;\n\n    /**\n     * 部门ID\n     */\n    private Long deptId;\n\n    /**\n     * 部门名\n     */\n    private String deptName;\n\n    /**\n     * 用户唯一标识\n     */\n    private String token;\n\n    /**\n     * 用户类型\n     */\n    private String userType;\n\n    /**\n     * 登录时间\n     */\n    private Long loginTime;\n\n    /**\n     * 过期时间\n     */\n    private Long expireTime;\n\n    /**\n     * 登录IP地址\n     */\n    private String ipaddr;\n\n    /**\n     * 登录地点\n     */\n    private String loginLocation;\n\n    /**\n     * 浏览器类型\n     */\n    private String browser;\n\n    /**\n     * 操作系统\n     */\n    private String os;\n\n    /**\n     * 菜单权限\n     */\n    private Set<String> menuPermission;\n\n    /**\n     * 角色权限\n     */\n    private Set<String> rolePermission;\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    /**\n     * 角色对象\n     */\n    private List<RoleDTO> roles;\n\n    /**\n     * 数据权限 当前角色ID\n     */\n    private Long roleId;\n\n    /**\n     * 获取登录id\n     */\n    public String getLoginId() {\n        if (userType == null) {\n            throw new IllegalArgumentException(\"用户类型不能为空\");\n        }\n        if (userId == null) {\n            throw new IllegalArgumentException(\"用户ID不能为空\");\n        }\n        return userType + \":\" + userId;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/model/RegisterBody.java",
    "content": "package top.flya.common.core.domain.model;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n/**\n * 用户注册对象\n *\n * @author Lion Li\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class RegisterBody extends LoginBody {\n\n    private String userType;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/model/SmsLoginBody.java",
    "content": "package top.flya.common.core.domain.model;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\n\n/**\n * 短信登录对象\n *\n * @author Lion Li\n */\n\n@Data\npublic class SmsLoginBody {\n\n    /**\n     * 手机号\n     */\n    @NotBlank(message = \"{user.phonenumber.not.blank}\")\n    private String phonenumber;\n\n    /**\n     * 短信code\n     */\n    @NotBlank(message = \"{sms.code.not.blank}\")\n    private String smsCode;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/domain/model/XcxLoginUser.java",
    "content": "package top.flya.common.core.domain.model;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n/**\n * 小程序登录用户身份权限\n *\n * @author Lion Li\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@NoArgsConstructor\npublic class XcxLoginUser extends LoginUser {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * openid\n     */\n    private String openid;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/mapper/BaseMapperPlus.java",
    "content": "package top.flya.common.core.mapper;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.toolkit.*;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.extension.toolkit.Db;\nimport top.flya.common.utils.BeanCopyUtils;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 自定义 Mapper 接口, 实现 自定义扩展\n *\n * @param <M> mapper 泛型\n * @param <T> table 泛型\n * @param <V> vo 泛型\n * @author Lion Li\n * @since 2021-05-13\n */\n@SuppressWarnings(\"unchecked\")\npublic interface BaseMapperPlus<M, T, V> extends BaseMapper<T> {\n\n    Log log = LogFactory.getLog(BaseMapperPlus.class);\n\n    default Class<V> currentVoClass() {\n        return (Class<V>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 2);\n    }\n\n    default Class<T> currentModelClass() {\n        return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 1);\n    }\n\n    default Class<M> currentMapperClass() {\n        return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 0);\n    }\n\n    default List<T> selectList() {\n        return this.selectList(new QueryWrapper<>());\n    }\n\n    /**\n     * 批量插入\n     */\n    default boolean insertBatch(Collection<T> entityList) {\n        return Db.saveBatch(entityList);\n    }\n\n    /**\n     * 批量更新\n     */\n    default boolean updateBatchById(Collection<T> entityList) {\n        return Db.updateBatchById(entityList);\n    }\n\n    /**\n     * 批量插入或更新\n     */\n    default boolean insertOrUpdateBatch(Collection<T> entityList) {\n        return Db.saveOrUpdateBatch(entityList);\n    }\n\n    /**\n     * 批量插入(包含限制条数)\n     */\n    default boolean insertBatch(Collection<T> entityList, int batchSize) {\n        return Db.saveBatch(entityList, batchSize);\n    }\n\n    /**\n     * 批量更新(包含限制条数)\n     */\n    default boolean updateBatchById(Collection<T> entityList, int batchSize) {\n        return Db.updateBatchById(entityList, batchSize);\n    }\n\n    /**\n     * 批量插入或更新(包含限制条数)\n     */\n    default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) {\n        return Db.saveOrUpdateBatch(entityList, batchSize);\n    }\n\n    /**\n     * 插入或更新(包含限制条数)\n     */\n    default boolean insertOrUpdate(T entity) {\n        return Db.saveOrUpdate(entity);\n    }\n\n    default V selectVoById(Serializable id) {\n        return selectVoById(id, this.currentVoClass());\n    }\n\n    /**\n     * 根据 ID 查询\n     */\n    default <C> C selectVoById(Serializable id, Class<C> voClass) {\n        T obj = this.selectById(id);\n        if (ObjectUtil.isNull(obj)) {\n            return null;\n        }\n        return BeanCopyUtils.copy(obj, voClass);\n    }\n\n    default List<V> selectVoBatchIds(Collection<? extends Serializable> idList) {\n        return selectVoBatchIds(idList, this.currentVoClass());\n    }\n\n    /**\n     * 查询（根据ID 批量查询）\n     */\n    default <C> List<C> selectVoBatchIds(Collection<? extends Serializable> idList, Class<C> voClass) {\n        List<T> list = this.selectBatchIds(idList);\n        if (CollUtil.isEmpty(list)) {\n            return CollUtil.newArrayList();\n        }\n        return BeanCopyUtils.copyList(list, voClass);\n    }\n\n    default List<V> selectVoByMap(Map<String, Object> map) {\n        return selectVoByMap(map, this.currentVoClass());\n    }\n\n    /**\n     * 查询（根据 columnMap 条件）\n     */\n    default <C> List<C> selectVoByMap(Map<String, Object> map, Class<C> voClass) {\n        List<T> list = this.selectByMap(map);\n        if (CollUtil.isEmpty(list)) {\n            return CollUtil.newArrayList();\n        }\n        return BeanCopyUtils.copyList(list, voClass);\n    }\n\n    default V selectVoOne(Wrapper<T> wrapper) {\n        return selectVoOne(wrapper, this.currentVoClass());\n    }\n\n    /**\n     * 根据 entity 条件，查询一条记录\n     */\n    default <C> C selectVoOne(Wrapper<T> wrapper, Class<C> voClass) {\n        T obj = this.selectOne(wrapper);\n        if (ObjectUtil.isNull(obj)) {\n            return null;\n        }\n        return BeanCopyUtils.copy(obj, voClass);\n    }\n\n    default List<V> selectVoList(Wrapper<T> wrapper) {\n        return selectVoList(wrapper, this.currentVoClass());\n    }\n\n    /**\n     * 根据 entity 条件，查询全部记录\n     */\n    default <C> List<C> selectVoList(Wrapper<T> wrapper, Class<C> voClass) {\n        List<T> list = this.selectList(wrapper);\n        if (CollUtil.isEmpty(list)) {\n            return CollUtil.newArrayList();\n        }\n        return BeanCopyUtils.copyList(list, voClass);\n    }\n\n    default <P extends IPage<V>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper) {\n        return selectVoPage(page, wrapper, this.currentVoClass());\n    }\n\n    /**\n     * 分页查询VO\n     */\n    default <C, P extends IPage<C>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper, Class<C> voClass) {\n        IPage<T> pageData = this.selectPage(page, wrapper);\n        IPage<C> voPage = new Page<>(pageData.getCurrent(), pageData.getSize(), pageData.getTotal());\n        if (CollUtil.isEmpty(pageData.getRecords())) {\n            return (P) voPage;\n        }\n        voPage.setRecords(BeanCopyUtils.copyList(pageData.getRecords(), voClass));\n        return (P) voPage;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/page/TableDataInfo.java",
    "content": "package top.flya.common.core.page;\n\nimport cn.hutool.http.HttpStatus;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 表格分页数据对象\n *\n * @author Lion Li\n */\n\n@Data\n@NoArgsConstructor\npublic class TableDataInfo<T> implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 总记录数\n     */\n    private long total;\n\n    /**\n     * 列表数据\n     */\n    private List<T> rows;\n\n    /**\n     * 消息状态码\n     */\n    private int code;\n\n    /**\n     * 消息内容\n     */\n    private String msg;\n\n    /**\n     * 分页\n     *\n     * @param list  列表数据\n     * @param total 总记录数\n     */\n    public TableDataInfo(List<T> list, long total) {\n        this.rows = list;\n        this.total = total;\n    }\n\n    public static <T> TableDataInfo<T> build(IPage<T> page) {\n        TableDataInfo<T> rspData = new TableDataInfo<>();\n        rspData.setCode(HttpStatus.HTTP_OK);\n        rspData.setMsg(\"查询成功\");\n        rspData.setRows(page.getRecords());\n        rspData.setTotal(page.getTotal());\n        return rspData;\n    }\n\n    public static <T> TableDataInfo<T> build(List<T> list) {\n        TableDataInfo<T> rspData = new TableDataInfo<>();\n        rspData.setCode(HttpStatus.HTTP_OK);\n        rspData.setMsg(\"查询成功\");\n        rspData.setRows(list);\n        rspData.setTotal(list.size());\n        return rspData;\n    }\n\n    public static <T> TableDataInfo<T> build() {\n        TableDataInfo<T> rspData = new TableDataInfo<>();\n        rspData.setCode(HttpStatus.HTTP_OK);\n        rspData.setMsg(\"查询成功\");\n        return rspData;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/service/ConfigService.java",
    "content": "package top.flya.common.core.service;\n\n/**\n * 通用 参数配置服务\n *\n * @author Lion Li\n */\npublic interface ConfigService {\n\n    /**\n     * 根据参数 key 获取参数值\n     *\n     * @param configKey 参数 key\n     * @return 参数值\n     */\n    String getConfigValue(String configKey);\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/service/DeptService.java",
    "content": "package top.flya.common.core.service;\n\n/**\n * 通用 部门服务\n *\n * @author Lion Li\n */\npublic interface DeptService {\n\n    /**\n     * 通过部门ID查询部门名称\n     *\n     * @param deptIds 部门ID串逗号分隔\n     * @return 部门名称串逗号分隔\n     */\n    String selectDeptNameByIds(String deptIds);\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/service/DictService.java",
    "content": "package top.flya.common.core.service;\n\n/**\n * 通用 字典服务\n *\n * @author Lion Li\n */\npublic interface DictService {\n\n    /**\n     * 分隔符\n     */\n    String SEPARATOR = \",\";\n\n    /**\n     * 根据字典类型和字典值获取字典标签\n     *\n     * @param dictType  字典类型\n     * @param dictValue 字典值\n     * @return 字典标签\n     */\n    default String getDictLabel(String dictType, String dictValue) {\n        return getDictLabel(dictType, dictValue, SEPARATOR);\n    }\n\n    /**\n     * 根据字典类型和字典标签获取字典值\n     *\n     * @param dictType  字典类型\n     * @param dictLabel 字典标签\n     * @return 字典值\n     */\n    default String getDictValue(String dictType, String dictLabel) {\n        return getDictValue(dictType, dictLabel, SEPARATOR);\n    }\n\n    /**\n     * 根据字典类型和字典值获取字典标签\n     *\n     * @param dictType  字典类型\n     * @param dictValue 字典值\n     * @param separator 分隔符\n     * @return 字典标签\n     */\n    String getDictLabel(String dictType, String dictValue, String separator);\n\n    /**\n     * 根据字典类型和字典标签获取字典值\n     *\n     * @param dictType  字典类型\n     * @param dictLabel 字典标签\n     * @param separator 分隔符\n     * @return 字典值\n     */\n    String getDictValue(String dictType, String dictLabel, String separator);\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/service/OssService.java",
    "content": "package top.flya.common.core.service;\n\n/**\n * 通用 OSS服务\n *\n * @author Lion Li\n */\npublic interface OssService {\n\n    /**\n     * 通过ossId查询对应的url\n     *\n     * @param ossIds ossId串逗号分隔\n     * @return url串逗号分隔\n     */\n    String selectUrlByIds(String ossIds);\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/service/SensitiveService.java",
    "content": "package top.flya.common.core.service;\n\n/**\n * 脱敏服务\n * 默认管理员不过滤\n * 需自行根据业务重写实现\n *\n * @author Lion Li\n * @version 3.6.0\n */\npublic interface SensitiveService {\n\n    /**\n     * 是否脱敏\n     */\n    boolean isSensitive();\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/service/UserService.java",
    "content": "package top.flya.common.core.service;\n\n/**\n * 通用 用户服务\n *\n * @author Lion Li\n */\npublic interface UserService {\n\n    /**\n     * 通过用户ID查询用户账户\n     *\n     * @param userId 用户ID\n     * @return 用户账户\n     */\n    String selectUserNameById(Long userId);\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/validate/AddGroup.java",
    "content": "package top.flya.common.core.validate;\n\n/**\n * 校验分组 add\n *\n * @author Lion Li\n */\npublic interface AddGroup {\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/validate/EditGroup.java",
    "content": "package top.flya.common.core.validate;\n\n/**\n * 校验分组 edit\n *\n * @author Lion Li\n */\npublic interface EditGroup {\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/core/validate/QueryGroup.java",
    "content": "package top.flya.common.core.validate;\n\n/**\n * 校验分组 query\n *\n * @author Lion Li\n */\npublic interface QueryGroup {\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/encrypt/EncryptContext.java",
    "content": "package top.flya.common.encrypt;\n\nimport top.flya.common.enums.AlgorithmType;\nimport top.flya.common.enums.EncodeType;\nimport lombok.Data;\n\n/**\n * 加密上下文 用于encryptor传递必要的参数。\n *\n * @author 老马\n * @version 4.6.0\n */\n@Data\npublic class EncryptContext {\n\n    /**\n     * 默认算法\n     */\n    private AlgorithmType algorithm;\n\n    /**\n     * 安全秘钥\n     */\n    private String password;\n\n    /**\n     * 公钥\n     */\n    private String publicKey;\n\n    /**\n     * 私钥\n     */\n    private String privateKey;\n\n    /**\n     * 编码方式，base64/hex\n     */\n    private EncodeType encode;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/encrypt/IEncryptor.java",
    "content": "package top.flya.common.encrypt;\n\nimport top.flya.common.enums.AlgorithmType;\nimport top.flya.common.enums.EncodeType;\n\n/**\n * 加解者\n *\n * @author 老马\n * @version 4.6.0\n */\npublic interface IEncryptor {\n\n    /**\n     * 获得当前算法\n     */\n    AlgorithmType algorithm();\n\n    /**\n     * 加密\n     *\n     * @param value      待加密字符串\n     * @param encodeType 加密后的编码格式\n     * @return 加密后的字符串\n     */\n    String encrypt(String value, EncodeType encodeType);\n\n    /**\n     * 解密\n     *\n     * @param value      待加密字符串\n     * @return 解密后的字符串\n     */\n    String decrypt(String value);\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/encrypt/encryptor/AbstractEncryptor.java",
    "content": "package top.flya.common.encrypt.encryptor;\n\nimport top.flya.common.encrypt.EncryptContext;\nimport top.flya.common.encrypt.IEncryptor;\n\n/**\n * 所有加密执行者的基类\n *\n * @author 老马\n * @version 4.6.0\n */\npublic abstract class AbstractEncryptor implements IEncryptor {\n\n    public AbstractEncryptor(EncryptContext context) {\n        // 用户配置校验与配置注入\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/encrypt/encryptor/AesEncryptor.java",
    "content": "package top.flya.common.encrypt.encryptor;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.crypto.SecureUtil;\nimport cn.hutool.crypto.symmetric.AES;\nimport top.flya.common.encrypt.EncryptContext;\nimport top.flya.common.enums.AlgorithmType;\nimport top.flya.common.enums.EncodeType;\n\nimport java.nio.charset.StandardCharsets;\n\n/**\n * AES算法实现\n *\n * @author 老马\n * @version 4.6.0\n */\npublic class AesEncryptor extends AbstractEncryptor {\n\n    private final AES aes;\n\n    public AesEncryptor(EncryptContext context) {\n        super(context);\n        String password = context.getPassword();\n        if (StrUtil.isBlank(password)) {\n            throw new IllegalArgumentException(\"AES没有获得秘钥信息\");\n        }\n        // aes算法的秘钥要求是16位、24位、32位\n        int[] array = {16, 24, 32};\n        if (!ArrayUtil.contains(array, password.length())) {\n            throw new IllegalArgumentException(\"AES秘钥长度应该为16位、24位、32位，实际为\" + password.length() + \"位\");\n        }\n        aes = SecureUtil.aes(context.getPassword().getBytes(StandardCharsets.UTF_8));\n    }\n\n    /**\n     * 获得当前算法\n     */\n    @Override\n    public AlgorithmType algorithm() {\n        return AlgorithmType.AES;\n    }\n\n    /**\n     * 加密\n     *\n     * @param value      待加密字符串\n     * @param encodeType 加密后的编码格式\n     */\n    @Override\n    public String encrypt(String value, EncodeType encodeType) {\n        if (encodeType == EncodeType.HEX) {\n            return aes.encryptHex(value);\n        } else {\n            return aes.encryptBase64(value);\n        }\n    }\n\n    /**\n     * 解密\n     *\n     * @param value      待加密字符串\n     */\n    @Override\n    public String decrypt(String value) {\n        return this.aes.decryptStr(value);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/encrypt/encryptor/Base64Encryptor.java",
    "content": "package top.flya.common.encrypt.encryptor;\n\nimport cn.hutool.core.codec.Base64;\nimport top.flya.common.encrypt.EncryptContext;\nimport top.flya.common.enums.AlgorithmType;\nimport top.flya.common.enums.EncodeType;\n\n/**\n * Base64算法实现\n *\n * @author 老马\n * @version 4.6.0\n */\npublic class Base64Encryptor extends AbstractEncryptor {\n\n    public Base64Encryptor(EncryptContext context) {\n        super(context);\n    }\n\n    /**\n     * 获得当前算法\n     */\n    @Override\n    public AlgorithmType algorithm() {\n        return AlgorithmType.BASE64;\n    }\n\n    /**\n     * 加密\n     *\n     * @param value      待加密字符串\n     * @param encodeType 加密后的编码格式\n     */\n    @Override\n    public String encrypt(String value, EncodeType encodeType) {\n        return Base64.encode(value);\n    }\n\n    /**\n     * 解密\n     *\n     * @param value      待加密字符串\n     */\n    @Override\n    public String decrypt(String value) {\n        return Base64.decodeStr(value);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/encrypt/encryptor/RsaEncryptor.java",
    "content": "package top.flya.common.encrypt.encryptor;\n\nimport cn.hutool.core.codec.Base64;\nimport cn.hutool.crypto.SecureUtil;\nimport cn.hutool.crypto.asymmetric.KeyType;\nimport cn.hutool.crypto.asymmetric.RSA;\nimport top.flya.common.encrypt.EncryptContext;\nimport top.flya.common.enums.AlgorithmType;\nimport top.flya.common.enums.EncodeType;\nimport top.flya.common.utils.StringUtils;\n\n\n/**\n * RSA算法实现\n *\n * @author 老马\n * @version 4.6.0\n */\npublic class RsaEncryptor extends AbstractEncryptor {\n\n    private final RSA rsa;\n\n    public RsaEncryptor(EncryptContext context) {\n        super(context);\n        String privateKey = context.getPrivateKey();\n        String publicKey = context.getPublicKey();\n        if (StringUtils.isAnyEmpty(privateKey, publicKey)) {\n            throw new IllegalArgumentException(\"RSA公私钥均需要提供，公钥加密，私钥解密。\");\n        }\n        this.rsa = SecureUtil.rsa(Base64.decode(privateKey), Base64.decode(publicKey));\n    }\n\n    /**\n     * 获得当前算法\n     */\n    @Override\n    public AlgorithmType algorithm() {\n        return AlgorithmType.RSA;\n    }\n\n    /**\n     * 加密\n     *\n     * @param value      待加密字符串\n     * @param encodeType 加密后的编码格式\n     */\n    @Override\n    public String encrypt(String value, EncodeType encodeType) {\n        if (encodeType == EncodeType.HEX) {\n            return rsa.encryptHex(value, KeyType.PublicKey);\n        } else {\n            return rsa.encryptBase64(value, KeyType.PublicKey);\n        }\n    }\n\n    /**\n     * 解密\n     *\n     * @param value      待加密字符串\n     */\n    @Override\n    public String decrypt(String value) {\n        return this.rsa.decryptStr(value, KeyType.PrivateKey);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/encrypt/encryptor/Sm2Encryptor.java",
    "content": "package top.flya.common.encrypt.encryptor;\n\n\nimport cn.hutool.core.codec.Base64;\nimport cn.hutool.crypto.SmUtil;\nimport cn.hutool.crypto.asymmetric.KeyType;\nimport cn.hutool.crypto.asymmetric.SM2;\nimport top.flya.common.encrypt.EncryptContext;\nimport top.flya.common.enums.AlgorithmType;\nimport top.flya.common.enums.EncodeType;\nimport top.flya.common.utils.StringUtils;\n\n/**\n * sm2算法实现\n *\n * @author 老马\n * @version 4.6.0\n */\npublic class Sm2Encryptor extends AbstractEncryptor {\n\n    private final SM2 sm2;\n\n    public Sm2Encryptor(EncryptContext context) {\n        super(context);\n        String privateKey = context.getPrivateKey();\n        String publicKey = context.getPublicKey();\n        if (StringUtils.isAnyEmpty(privateKey, publicKey)) {\n            throw new IllegalArgumentException(\"SM2公私钥均需要提供，公钥加密，私钥解密。\");\n        }\n        this.sm2 = SmUtil.sm2(Base64.decode(privateKey), Base64.decode(publicKey));\n    }\n\n    /**\n     * 获得当前算法\n     */\n    @Override\n    public AlgorithmType algorithm() {\n        return AlgorithmType.SM2;\n    }\n\n    /**\n     * 加密\n     *\n     * @param value      待加密字符串\n     * @param encodeType 加密后的编码格式\n     */\n    @Override\n    public String encrypt(String value, EncodeType encodeType) {\n        if (encodeType == EncodeType.HEX) {\n            return sm2.encryptHex(value, KeyType.PublicKey);\n        } else {\n            return sm2.encryptBase64(value, KeyType.PublicKey);\n        }\n    }\n\n    /**\n     * 解密\n     *\n     * @param value      待加密字符串\n     */\n    @Override\n    public String decrypt(String value) {\n        return this.sm2.decryptStr(value, KeyType.PrivateKey);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/encrypt/encryptor/Sm4Encryptor.java",
    "content": "package top.flya.common.encrypt.encryptor;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.crypto.SmUtil;\nimport cn.hutool.crypto.symmetric.SM4;\nimport top.flya.common.encrypt.EncryptContext;\nimport top.flya.common.enums.AlgorithmType;\nimport top.flya.common.enums.EncodeType;\n\nimport java.nio.charset.StandardCharsets;\n\n/**\n * sm4算法实现\n *\n * @author 老马\n * @version 4.6.0\n */\npublic class Sm4Encryptor extends AbstractEncryptor {\n\n    private final SM4 sm4;\n\n    public Sm4Encryptor(EncryptContext context) {\n        super(context);\n        String password = context.getPassword();\n        if (StrUtil.isBlank(password)) {\n            throw new IllegalArgumentException(\"SM4没有获得秘钥信息\");\n        }\n        // sm4算法的秘钥要求是16位长度\n        if (16 != password.length()) {\n            throw new IllegalArgumentException(\"SM4秘钥长度应该为16位，实际为\" + password.length() + \"位\");\n        }\n        this.sm4 = SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8));\n    }\n\n    /**\n     * 获得当前算法\n     */\n    @Override\n    public AlgorithmType algorithm() {\n        return AlgorithmType.SM4;\n    }\n\n    /**\n     * 加密\n     *\n     * @param value      待加密字符串\n     * @param encodeType 加密后的编码格式\n     */\n    @Override\n    public String encrypt(String value, EncodeType encodeType) {\n        if (encodeType == EncodeType.HEX) {\n            return sm4.encryptHex(value);\n        } else {\n            return sm4.encryptBase64(value);\n        }\n    }\n\n    /**\n     * 解密\n     *\n     * @param value      待加密字符串\n     */\n    @Override\n    public String decrypt(String value) {\n        return this.sm4.decryptStr(value);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/AlgorithmType.java",
    "content": "package top.flya.common.enums;\n\nimport top.flya.common.encrypt.encryptor.*;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport top.flya.common.encrypt.encryptor.*;\n\n/**\n * 算法名称\n *\n * @author 老马\n * @version 4.6.0\n */\n@Getter\n@AllArgsConstructor\npublic enum AlgorithmType {\n\n    /**\n     * 默认走yml配置\n     */\n    DEFAULT(null),\n\n    /**\n     * base64\n     */\n    BASE64(Base64Encryptor.class),\n\n    /**\n     * aes\n     */\n    AES(AesEncryptor.class),\n\n    /**\n     * rsa\n     */\n    RSA(RsaEncryptor.class),\n\n    /**\n     * sm2\n     */\n    SM2(Sm2Encryptor.class),\n\n    /**\n     * sm4\n     */\n    SM4(Sm4Encryptor.class);\n\n    private final Class<? extends AbstractEncryptor> clazz;\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/BusinessStatus.java",
    "content": "package top.flya.common.enums;\n\n/**\n * 操作状态\n *\n * @author ruoyi\n */\npublic enum BusinessStatus {\n    /**\n     * 成功\n     */\n    SUCCESS,\n\n    /**\n     * 失败\n     */\n    FAIL,\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/BusinessType.java",
    "content": "package top.flya.common.enums;\n\n/**\n * 业务操作类型\n *\n * @author ruoyi\n */\npublic enum BusinessType {\n    /**\n     * 其它\n     */\n    OTHER,\n\n    /**\n     * 新增\n     */\n    INSERT,\n\n    /**\n     * 修改\n     */\n    UPDATE,\n\n    /**\n     * 删除\n     */\n    DELETE,\n\n    /**\n     * 授权\n     */\n    GRANT,\n\n    /**\n     * 导出\n     */\n    EXPORT,\n\n    /**\n     * 导入\n     */\n    IMPORT,\n\n    /**\n     * 强退\n     */\n    FORCE,\n\n    /**\n     * 生成代码\n     */\n    GENCODE,\n\n    /**\n     * 清空数据\n     */\n    CLEAN,\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/CaptchaCategory.java",
    "content": "package top.flya.common.enums;\n\nimport cn.hutool.captcha.AbstractCaptcha;\nimport cn.hutool.captcha.CircleCaptcha;\nimport cn.hutool.captcha.LineCaptcha;\nimport cn.hutool.captcha.ShearCaptcha;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 验证码类别\n *\n * @author Lion Li\n */\n@Getter\n@AllArgsConstructor\npublic enum CaptchaCategory {\n\n    /**\n     * 线段干扰\n     */\n    LINE(LineCaptcha.class),\n\n    /**\n     * 圆圈干扰\n     */\n    CIRCLE(CircleCaptcha.class),\n\n    /**\n     * 扭曲干扰\n     */\n    SHEAR(ShearCaptcha.class);\n\n    private final Class<? extends AbstractCaptcha> clazz;\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/CaptchaType.java",
    "content": "package top.flya.common.enums;\n\nimport cn.hutool.captcha.generator.CodeGenerator;\nimport cn.hutool.captcha.generator.RandomGenerator;\nimport top.flya.common.captcha.UnsignedMathGenerator;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 验证码类型\n *\n * @author Lion Li\n */\n@Getter\n@AllArgsConstructor\npublic enum CaptchaType {\n\n    /**\n     * 数字\n     */\n    MATH(UnsignedMathGenerator.class),\n\n    /**\n     * 字符\n     */\n    CHAR(RandomGenerator.class);\n\n    private final Class<? extends CodeGenerator> clazz;\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/DataBaseType.java",
    "content": "package top.flya.common.enums;\n\nimport top.flya.common.utils.StringUtils;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 数据库类型\n *\n * @author Lion Li\n */\n@Getter\n@AllArgsConstructor\npublic enum DataBaseType {\n\n    /**\n     * MySQL\n     */\n    MY_SQL(\"MySQL\"),\n\n    /**\n     * Oracle\n     */\n    ORACLE(\"Oracle\"),\n\n    /**\n     * PostgreSQL\n     */\n    POSTGRE_SQL(\"PostgreSQL\"),\n\n    /**\n     * SQL Server\n     */\n    SQL_SERVER(\"Microsoft SQL Server\");\n\n    private final String type;\n\n    public static DataBaseType find(String databaseProductName) {\n        if (StringUtils.isBlank(databaseProductName)) {\n            return null;\n        }\n        for (DataBaseType type : values()) {\n            if (type.getType().equals(databaseProductName)) {\n                return type;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/DataScopeType.java",
    "content": "package top.flya.common.enums;\n\nimport top.flya.common.utils.StringUtils;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport top.flya.common.helper.DataPermissionHelper;\n\n/**\n * 数据权限类型\n * <p>\n * 语法支持 spel 模板表达式\n * <p>\n * 内置数据 user 当前用户 内容参考 LoginUser\n * 如需扩展数据 可使用 {@link DataPermissionHelper} 操作\n * 内置服务 sdss 系统数据权限服务 内容参考 SysDataScopeService\n * 如需扩展更多自定义服务 可以参考 sdss 自行编写\n *\n * @author Lion Li\n * @version 3.5.0\n */\n@Getter\n@AllArgsConstructor\npublic enum DataScopeType {\n\n    /**\n     * 全部数据权限\n     */\n    ALL(\"1\", \"\", \"\"),\n\n    /**\n     * 自定数据权限\n     */\n    CUSTOM(\"2\", \" #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) \", \"\"),\n\n    /**\n     * 部门数据权限\n     */\n    DEPT(\"3\", \" #{#deptName} = #{#user.deptId} \", \"\"),\n\n    /**\n     * 部门及以下数据权限\n     */\n    DEPT_AND_CHILD(\"4\", \" #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )\", \"\"),\n\n    /**\n     * 仅本人数据权限\n     */\n    SELF(\"5\", \" #{#userName} = #{#user.userId} \", \" 1 = 0 \");\n\n    private final String code;\n\n    /**\n     * 语法 采用 spel 模板表达式\n     */\n    private final String sqlTemplate;\n\n    /**\n     * 不满足 sqlTemplate 则填充\n     */\n    private final String elseSql;\n\n    public static DataScopeType findCode(String code) {\n        if (StringUtils.isBlank(code)) {\n            return null;\n        }\n        for (DataScopeType type : values()) {\n            if (type.getCode().equals(code)) {\n                return type;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/DeviceType.java",
    "content": "package top.flya.common.enums;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 设备类型\n * 针对一套 用户体系\n *\n * @author Lion Li\n */\n@Getter\n@AllArgsConstructor\npublic enum DeviceType {\n\n    /**\n     * pc端\n     */\n    PC(\"pc\"),\n\n    /**\n     * app端\n     */\n    APP(\"app\"),\n\n    /**\n     * 小程序端\n     */\n    XCX(\"xcx\");\n\n    private final String device;\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/EncodeType.java",
    "content": "package top.flya.common.enums;\n\n/**\n * 编码类型\n *\n * @author 老马\n * @version 4.6.0\n */\npublic enum EncodeType {\n\n    /**\n     * 默认使用yml配置\n     */\n    DEFAULT,\n\n    /**\n     * base64编码\n     */\n    BASE64,\n\n    /**\n     * 16进制编码\n     */\n    HEX;\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/HttpMethod.java",
    "content": "package top.flya.common.enums;\n\nimport org.springframework.lang.Nullable;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 请求方式\n *\n * @author ruoyi\n */\npublic enum HttpMethod {\n    GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;\n\n    private static final Map<String, HttpMethod> mappings = new HashMap<>(16);\n\n    static {\n        for (HttpMethod httpMethod : values()) {\n            mappings.put(httpMethod.name(), httpMethod);\n        }\n    }\n\n    @Nullable\n    public static HttpMethod resolve(@Nullable String method) {\n        return (method != null ? mappings.get(method) : null);\n    }\n\n    public boolean matches(String method) {\n        return (this == resolve(method));\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/LimitType.java",
    "content": "package top.flya.common.enums;\n\n/**\n * 限流类型\n *\n * @author ruoyi\n */\n\npublic enum LimitType {\n    /**\n     * 默认策略全局限流\n     */\n    DEFAULT,\n\n    /**\n     * 根据请求者IP进行限流\n     */\n    IP,\n\n    /**\n     * 实例限流(集群多后端实例)\n     */\n    CLUSTER\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/LoginType.java",
    "content": "package top.flya.common.enums;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 登录类型\n *\n * @author Lion Li\n */\n@Getter\n@AllArgsConstructor\npublic enum LoginType {\n\n    /**\n     * 密码登录\n     */\n    PASSWORD(\"user.password.retry.limit.exceed\", \"user.password.retry.limit.count\"),\n\n    /**\n     * 短信登录\n     */\n    SMS(\"sms.code.retry.limit.exceed\", \"sms.code.retry.limit.count\"),\n\n    /**\n     * 邮箱登录\n     */\n    EMAIL(\"email.code.retry.limit.exceed\", \"email.code.retry.limit.count\"),\n\n    /**\n     * 小程序登录\n     */\n    XCX(\"\", \"\");\n\n    /**\n     * 登录重试超出限制提示\n     */\n    final String retryLimitExceed;\n\n    /**\n     * 登录重试限制计数提示\n     */\n    final String retryLimitCount;\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/OperatorType.java",
    "content": "package top.flya.common.enums;\n\n/**\n * 操作人类别\n *\n * @author ruoyi\n */\npublic enum OperatorType {\n    /**\n     * 其它\n     */\n    OTHER,\n\n    /**\n     * 后台用户\n     */\n    MANAGE,\n\n    /**\n     * 手机端用户\n     */\n    MOBILE\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/SensitiveStrategy.java",
    "content": "package top.flya.common.enums;\n\nimport cn.hutool.core.util.DesensitizedUtil;\nimport lombok.AllArgsConstructor;\n\nimport java.util.function.Function;\n\n/**\n * 脱敏策略\n *\n * @author Yjoioooo\n * @version 3.6.0\n */\n@AllArgsConstructor\npublic enum SensitiveStrategy {\n\n    /**\n     * 身份证脱敏\n     */\n    ID_CARD(s -> DesensitizedUtil.idCardNum(s, 3, 4)),\n\n    /**\n     * 手机号脱敏\n     */\n    PHONE(DesensitizedUtil::mobilePhone),\n\n    /**\n     * 地址脱敏\n     */\n    ADDRESS(s -> DesensitizedUtil.address(s, 8)),\n\n    /**\n     * 邮箱脱敏\n     */\n    EMAIL(DesensitizedUtil::email),\n\n    /**\n     * 银行卡\n     */\n    BANK_CARD(DesensitizedUtil::bankCard);\n\n    //可自行添加其他脱敏策略\n\n    private final Function<String, String> desensitizer;\n\n    public Function<String, String> desensitizer() {\n        return desensitizer;\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/UserStatus.java",
    "content": "package top.flya.common.enums;\n\n/**\n * 用户状态\n *\n * @author ruoyi\n */\npublic enum UserStatus {\n    OK(\"0\", \"正常\"), DISABLE(\"1\", \"停用\"), DELETED(\"2\", \"删除\");\n\n    private final String code;\n    private final String info;\n\n    UserStatus(String code, String info) {\n        this.code = code;\n        this.info = info;\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/enums/UserType.java",
    "content": "package top.flya.common.enums;\n\nimport top.flya.common.utils.StringUtils;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 设备类型\n * 针对多套 用户体系\n *\n * @author Lion Li\n */\n@Getter\n@AllArgsConstructor\npublic enum UserType {\n\n    /**\n     * pc端\n     */\n    SYS_USER(\"sys_user\"),\n\n    /**\n     * app端\n     */\n    APP_USER(\"app_user\"),\n\n    WX_USER(\"微信小程序用户\");\n\n    private final String userType;\n\n    public static UserType getUserType(String str) {\n        for (UserType value : values()) {\n            if (StringUtils.contains(str, value.getUserType())) {\n                return value;\n            }\n        }\n        throw new RuntimeException(\"'UserType' not found By \" + str);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/excel/CellMergeStrategy.java",
    "content": "package top.flya.common.excel;\n\nimport com.alibaba.excel.metadata.Head;\nimport com.alibaba.excel.write.merge.AbstractMergeStrategy;\nimport top.flya.common.annotation.CellMerge;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.poi.ss.usermodel.Cell;\nimport org.apache.poi.ss.usermodel.Sheet;\nimport org.apache.poi.ss.util.CellRangeAddress;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 列值重复合并策略\n *\n * @author Lion Li\n */\n@AllArgsConstructor\n@Slf4j\npublic class CellMergeStrategy extends AbstractMergeStrategy {\n\n\tprivate List<?> list;\n\tprivate boolean hasTitle;\n\n\t@Override\n\tprotected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {\n\t\tList<CellRangeAddress> cellList = handle(list, hasTitle);\n\t\t// judge the list is not null\n\t\tif (CollectionUtils.isNotEmpty(cellList)) {\n\t\t\t// the judge is necessary\n\t\t\tif (cell.getRowIndex() == 1 && cell.getColumnIndex() == 0) {\n\t\t\t\tfor (CellRangeAddress item : cellList) {\n\t\t\t\t\tsheet.addMergedRegion(item);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@SneakyThrows\n\tprivate static List<CellRangeAddress> handle(List<?> list, boolean hasTitle) {\n\t\tList<CellRangeAddress> cellList = new ArrayList<>();\n\t\tif (CollectionUtils.isEmpty(list)) {\n\t\t\treturn cellList;\n\t\t}\n\t\tClass<?> clazz = list.get(0).getClass();\n\t\tField[] fields = clazz.getDeclaredFields();\n\t\t// 有注解的字段\n\t\tList<Field> mergeFields = new ArrayList<>();\n\t\tList<Integer> mergeFieldsIndex = new ArrayList<>();\n\t\tfor (int i = 0; i < fields.length; i++) {\n\t\t\tField field = fields[i];\n\t\t\tif (field.isAnnotationPresent(CellMerge.class)) {\n\t\t\t\tCellMerge cm = field.getAnnotation(CellMerge.class);\n\t\t\t\tmergeFields.add(field);\n\t\t\t\tmergeFieldsIndex.add(cm.index() == -1 ? i : cm.index());\n\t\t\t}\n\t\t}\n\t\t// 行合并开始下标\n\t\tint rowIndex = hasTitle ? 1 : 0;\n\t\tMap<Field, RepeatCell> map = new HashMap<>();\n\t\t// 生成两两合并单元格\n\t\tfor (int i = 0; i < list.size(); i++) {\n\t\t\tfor (int j = 0; j < mergeFields.size(); j++) {\n\t\t\t\tField field = mergeFields.get(j);\n\t\t\t\tString name = field.getName();\n\t\t\t\tString methodName = \"get\" + name.substring(0, 1).toUpperCase() + name.substring(1);\n\t\t\t\tMethod readMethod = clazz.getMethod(methodName);\n\t\t\t\tObject val = readMethod.invoke(list.get(i));\n\n\t\t\t\tint colNum = mergeFieldsIndex.get(j);\n\t\t\t\tif (!map.containsKey(field)) {\n\t\t\t\t\tmap.put(field, new RepeatCell(val, i));\n\t\t\t\t} else {\n\t\t\t\t\tRepeatCell repeatCell = map.get(field);\n\t\t\t\t\tObject cellValue = repeatCell.getValue();\n\t\t\t\t\tif (cellValue == null || \"\".equals(cellValue)) {\n\t\t\t\t\t\t// 空值跳过不合并\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (!cellValue.equals(val)) {\n\t\t\t\t\t\tif (i - repeatCell.getCurrent() > 1) {\n\t\t\t\t\t\t\tcellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmap.put(field, new RepeatCell(val, i));\n\t\t\t\t\t} else if (i == list.size() - 1) {\n\t\t\t\t\t\tif (i > repeatCell.getCurrent()) {\n\t\t\t\t\t\t\tcellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn cellList;\n\t}\n\n\t@Data\n\t@AllArgsConstructor\n\tstatic class RepeatCell {\n\n\t\tprivate Object value;\n\n\t\tprivate int current;\n\n\t}\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/excel/DefaultExcelListener.java",
    "content": "package top.flya.common.excel;\n\nimport cn.hutool.core.util.StrUtil;\nimport com.alibaba.excel.context.AnalysisContext;\nimport com.alibaba.excel.event.AnalysisEventListener;\nimport com.alibaba.excel.exception.ExcelAnalysisException;\nimport com.alibaba.excel.exception.ExcelDataConvertException;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.common.utils.StreamUtils;\nimport top.flya.common.utils.ValidatorUtils;\nimport lombok.NoArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\n\nimport javax.validation.ConstraintViolation;\nimport javax.validation.ConstraintViolationException;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * Excel 导入监听\n *\n * @author Yjoioooo\n * @author Lion Li\n */\n@Slf4j\n@NoArgsConstructor\npublic class DefaultExcelListener<T> extends AnalysisEventListener<T> implements ExcelListener<T> {\n\n    /**\n     * 是否Validator检验，默认为是\n     */\n    private Boolean isValidate = Boolean.TRUE;\n\n    /**\n     * excel 表头数据\n     */\n    private Map<Integer, String> headMap;\n\n    /**\n     * 导入回执\n     */\n    private ExcelResult<T> excelResult;\n\n    public DefaultExcelListener(boolean isValidate) {\n        this.excelResult = new DefaultExcelResult<>();\n        this.isValidate = isValidate;\n    }\n\n    /**\n     * 处理异常\n     *\n     * @param exception ExcelDataConvertException\n     * @param context   Excel 上下文\n     */\n    @Override\n    public void onException(Exception exception, AnalysisContext context) throws Exception {\n        String errMsg = null;\n        if (exception instanceof ExcelDataConvertException) {\n            // 如果是某一个单元格的转换异常 能获取到具体行号\n            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;\n            Integer rowIndex = excelDataConvertException.getRowIndex();\n            Integer columnIndex = excelDataConvertException.getColumnIndex();\n            errMsg = StrUtil.format(\"第{}行-第{}列-表头{}: 解析异常<br/>\",\n                rowIndex + 1, columnIndex + 1, headMap.get(columnIndex));\n            if (log.isDebugEnabled()) {\n                log.error(errMsg);\n            }\n        }\n        if (exception instanceof ConstraintViolationException) {\n            ConstraintViolationException constraintViolationException = (ConstraintViolationException) exception;\n            Set<ConstraintViolation<?>> constraintViolations = constraintViolationException.getConstraintViolations();\n            String constraintViolationsMsg = StreamUtils.join(constraintViolations, ConstraintViolation::getMessage, \", \");\n            errMsg = StrUtil.format(\"第{}行数据校验异常: {}\", context.readRowHolder().getRowIndex() + 1, constraintViolationsMsg);\n            if (log.isDebugEnabled()) {\n                log.error(errMsg);\n            }\n        }\n        excelResult.getErrorList().add(errMsg);\n        throw new ExcelAnalysisException(errMsg);\n    }\n\n    @Override\n    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {\n        this.headMap = headMap;\n        log.debug(\"解析到一条表头数据: {}\", JsonUtils.toJsonString(headMap));\n    }\n\n    @Override\n    public void invoke(T data, AnalysisContext context) {\n        if (isValidate) {\n            ValidatorUtils.validate(data);\n        }\n        excelResult.getList().add(data);\n    }\n\n    @Override\n    public void doAfterAllAnalysed(AnalysisContext context) {\n        log.debug(\"所有数据解析完成！\");\n    }\n\n    @Override\n    public ExcelResult<T> getExcelResult() {\n        return excelResult;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/excel/DefaultExcelResult.java",
    "content": "package top.flya.common.excel;\n\nimport cn.hutool.core.util.StrUtil;\nimport lombok.Setter;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 默认excel返回对象\n *\n * @author Yjoioooo\n * @author Lion Li\n */\npublic class DefaultExcelResult<T> implements ExcelResult<T> {\n\n    /**\n     * 数据对象list\n     */\n    @Setter\n    private List<T> list;\n\n    /**\n     * 错误信息列表\n     */\n    @Setter\n    private List<String> errorList;\n\n    public DefaultExcelResult() {\n        this.list = new ArrayList<>();\n        this.errorList = new ArrayList<>();\n    }\n\n    public DefaultExcelResult(List<T> list, List<String> errorList) {\n        this.list = list;\n        this.errorList = errorList;\n    }\n\n    public DefaultExcelResult(ExcelResult<T> excelResult) {\n        this.list = excelResult.getList();\n        this.errorList = excelResult.getErrorList();\n    }\n\n    @Override\n    public List<T> getList() {\n        return list;\n    }\n\n    @Override\n    public List<String> getErrorList() {\n        return errorList;\n    }\n\n    /**\n     * 获取导入回执\n     *\n     * @return 导入回执\n     */\n    @Override\n    public String getAnalysis() {\n        int successCount = list.size();\n        int errorCount = errorList.size();\n        if (successCount == 0) {\n            return \"读取失败，未解析到数据\";\n        } else {\n            if (errorCount == 0) {\n                return StrUtil.format(\"恭喜您，全部读取成功！共{}条\", successCount);\n            } else {\n                return \"\";\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/excel/ExcelListener.java",
    "content": "package top.flya.common.excel;\n\nimport com.alibaba.excel.read.listener.ReadListener;\n\n/**\n * Excel 导入监听\n *\n * @author Lion Li\n */\npublic interface ExcelListener<T> extends ReadListener<T> {\n\n    ExcelResult<T> getExcelResult();\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/excel/ExcelResult.java",
    "content": "package top.flya.common.excel;\n\nimport java.util.List;\n\n/**\n * excel返回对象\n *\n * @author Lion Li\n */\npublic interface ExcelResult<T> {\n\n    /**\n     * 对象列表\n     */\n    List<T> getList();\n\n    /**\n     * 错误列表\n     */\n    List<String> getErrorList();\n\n    /**\n     * 导入回执\n     */\n    String getAnalysis();\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/exception/DemoModeException.java",
    "content": "package top.flya.common.exception;\n\n/**\n * 演示模式异常\n *\n * @author ruoyi\n */\npublic class DemoModeException extends RuntimeException {\n    private static final long serialVersionUID = 1L;\n\n    public DemoModeException() {\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/exception/GlobalException.java",
    "content": "package top.flya.common.exception;\n\n/**\n * 全局异常\n *\n * @author ruoyi\n */\npublic class GlobalException extends RuntimeException {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 错误提示\n     */\n    private String message;\n\n    /**\n     * 错误明细，内部调试错误\n     * <p>\n     * 和 {@link CommonResult#getDetailMessage()} 一致的设计\n     */\n    private String detailMessage;\n\n    /**\n     * 空构造方法，避免反序列化问题\n     */\n    public GlobalException() {\n    }\n\n    public GlobalException(String message) {\n        this.message = message;\n    }\n\n    public String getDetailMessage() {\n        return detailMessage;\n    }\n\n    public GlobalException setDetailMessage(String detailMessage) {\n        this.detailMessage = detailMessage;\n        return this;\n    }\n\n    @Override\n    public String getMessage() {\n        return message;\n    }\n\n    public GlobalException setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/exception/ServiceException.java",
    "content": "package top.flya.common.exception;\n\n/**\n * 业务异常\n *\n * @author ruoyi\n */\npublic final class ServiceException extends RuntimeException {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 错误码\n     */\n    private Integer code;\n\n    /**\n     * 错误提示\n     */\n    private String message;\n\n    /**\n     * 错误明细，内部调试错误\n     * <p>\n     * 和 {@link CommonResult#getDetailMessage()} 一致的设计\n     */\n    private String detailMessage;\n\n    /**\n     * 空构造方法，避免反序列化问题\n     */\n    public ServiceException() {\n    }\n\n    public ServiceException(String message) {\n        this.message = message;\n    }\n\n    public ServiceException(String message, Integer code) {\n        this.message = message;\n        this.code = code;\n    }\n\n    public String getDetailMessage() {\n        return detailMessage;\n    }\n\n    @Override\n    public String getMessage() {\n        return message;\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public ServiceException setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n\n    public ServiceException setDetailMessage(String detailMessage) {\n        this.detailMessage = detailMessage;\n        return this;\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/exception/UtilException.java",
    "content": "package top.flya.common.exception;\n\n/**\n * 工具类异常\n *\n * @author ruoyi\n */\npublic class UtilException extends RuntimeException {\n    private static final long serialVersionUID = 8247610319171014183L;\n\n    public UtilException(Throwable e) {\n        super(e.getMessage(), e);\n    }\n\n    public UtilException(String message) {\n        super(message);\n    }\n\n    public UtilException(String message, Throwable throwable) {\n        super(message, throwable);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/exception/base/BaseException.java",
    "content": "package top.flya.common.exception.base;\n\nimport top.flya.common.utils.MessageUtils;\nimport top.flya.common.utils.StringUtils;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n/**\n * 基础异常\n *\n * @author ruoyi\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@NoArgsConstructor\npublic class BaseException extends RuntimeException {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 所属模块\n     */\n    private String module;\n\n    /**\n     * 错误码\n     */\n    private String code;\n\n    /**\n     * 错误码对应的参数\n     */\n    private Object[] args;\n\n    /**\n     * 错误消息\n     */\n    private String defaultMessage;\n\n    public BaseException(String module, String code, Object[] args, String defaultMessage) {\n        this.module = module;\n        this.code = code;\n        this.args = args;\n        this.defaultMessage = defaultMessage;\n    }\n\n    public BaseException(String module, String code, Object[] args) {\n        this(module, code, args, null);\n    }\n\n    public BaseException(String module, String defaultMessage) {\n        this(module, null, null, defaultMessage);\n    }\n\n    public BaseException(String code, Object[] args) {\n        this(null, code, args, null);\n    }\n\n    public BaseException(String defaultMessage) {\n        this(null, null, null, defaultMessage);\n    }\n\n    @Override\n    public String getMessage() {\n        String message = null;\n        if (!StringUtils.isEmpty(code)) {\n            message = MessageUtils.message(code, args);\n        }\n        if (message == null) {\n            message = defaultMessage;\n        }\n        return message;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/exception/file/FileException.java",
    "content": "package top.flya.common.exception.file;\n\nimport top.flya.common.exception.base.BaseException;\n\n/**\n * 文件信息异常类\n *\n * @author ruoyi\n */\npublic class FileException extends BaseException {\n    private static final long serialVersionUID = 1L;\n\n    public FileException(String code, Object[] args) {\n        super(\"file\", code, args, null);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/exception/file/FileNameLengthLimitExceededException.java",
    "content": "package top.flya.common.exception.file;\n\n/**\n * 文件名称超长限制异常类\n *\n * @author ruoyi\n */\npublic class FileNameLengthLimitExceededException extends FileException {\n    private static final long serialVersionUID = 1L;\n\n    public FileNameLengthLimitExceededException(int defaultFileNameLength) {\n        super(\"upload.filename.exceed.length\", new Object[]{defaultFileNameLength});\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/exception/file/FileSizeLimitExceededException.java",
    "content": "package top.flya.common.exception.file;\n\n/**\n * 文件名大小限制异常类\n *\n * @author ruoyi\n */\npublic class FileSizeLimitExceededException extends FileException {\n    private static final long serialVersionUID = 1L;\n\n    public FileSizeLimitExceededException(long defaultMaxSize) {\n        super(\"upload.exceed.maxSize\", new Object[]{defaultMaxSize});\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/exception/user/CaptchaException.java",
    "content": "package top.flya.common.exception.user;\n\n/**\n * 验证码错误异常类\n *\n * @author ruoyi\n */\npublic class CaptchaException extends UserException {\n    private static final long serialVersionUID = 1L;\n\n    public CaptchaException() {\n        super(\"user.jcaptcha.error\");\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/exception/user/CaptchaExpireException.java",
    "content": "package top.flya.common.exception.user;\n\n/**\n * 验证码失效异常类\n *\n * @author ruoyi\n */\npublic class CaptchaExpireException extends UserException {\n    private static final long serialVersionUID = 1L;\n\n    public CaptchaExpireException() {\n        super(\"user.jcaptcha.expire\");\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/exception/user/UserException.java",
    "content": "package top.flya.common.exception.user;\n\nimport top.flya.common.exception.base.BaseException;\n\n/**\n * 用户信息异常类\n *\n * @author ruoyi\n */\npublic class UserException extends BaseException {\n    private static final long serialVersionUID = 1L;\n\n    public UserException(String code, Object... args) {\n        super(\"user\", code, args, null);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/exception/user/UserPasswordNotMatchException.java",
    "content": "package top.flya.common.exception.user;\n\n/**\n * 用户密码不正确或不符合规范异常类\n *\n * @author ruoyi\n */\npublic class UserPasswordNotMatchException extends UserException {\n    private static final long serialVersionUID = 1L;\n\n    public UserPasswordNotMatchException() {\n        super(\"user.password.not.match\");\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/exception/user/UserPasswordRetryLimitExceedException.java",
    "content": "package top.flya.common.exception.user;\n\n/**\n * 用户错误最大次数异常类\n *\n * @author ruoyi\n */\npublic class UserPasswordRetryLimitExceedException extends UserException {\n\n    private static final long serialVersionUID = 1L;\n\n    public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime) {\n        super(\"user.password.retry.limit.exceed\", retryLimitCount, lockTime);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/filter/RepeatableFilter.java",
    "content": "package top.flya.common.filter;\n\nimport top.flya.common.utils.StringUtils;\nimport org.springframework.http.MediaType;\n\nimport javax.servlet.*;\nimport javax.servlet.http.HttpServletRequest;\nimport java.io.IOException;\n\n/**\n * Repeatable 过滤器\n *\n * @author ruoyi\n */\npublic class RepeatableFilter implements Filter {\n    @Override\n    public void init(FilterConfig filterConfig) throws ServletException {\n\n    }\n\n    @Override\n    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n        throws IOException, ServletException {\n        ServletRequest requestWrapper = null;\n        if (request instanceof HttpServletRequest\n            && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) {\n            requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);\n        }\n        if (null == requestWrapper) {\n            chain.doFilter(request, response);\n        } else {\n            chain.doFilter(requestWrapper, response);\n        }\n    }\n\n    @Override\n    public void destroy() {\n\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/filter/RepeatedlyRequestWrapper.java",
    "content": "package top.flya.common.filter;\n\nimport cn.hutool.core.io.IoUtil;\nimport top.flya.common.constant.Constants;\n\nimport javax.servlet.ReadListener;\nimport javax.servlet.ServletInputStream;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletRequestWrapper;\nimport java.io.BufferedReader;\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\n\n/**\n * 构建可重复读取inputStream的request\n *\n * @author ruoyi\n */\npublic class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {\n    private final byte[] body;\n\n    public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException {\n        super(request);\n        request.setCharacterEncoding(Constants.UTF8);\n        response.setCharacterEncoding(Constants.UTF8);\n\n        body = IoUtil.readBytes(request.getInputStream(), false);\n    }\n\n    @Override\n    public BufferedReader getReader() throws IOException {\n        return new BufferedReader(new InputStreamReader(getInputStream()));\n    }\n\n    @Override\n    public ServletInputStream getInputStream() throws IOException {\n        final ByteArrayInputStream bais = new ByteArrayInputStream(body);\n        return new ServletInputStream() {\n            @Override\n            public int read() throws IOException {\n                return bais.read();\n            }\n\n            @Override\n            public int available() throws IOException {\n                return body.length;\n            }\n\n            @Override\n            public boolean isFinished() {\n                return false;\n            }\n\n            @Override\n            public boolean isReady() {\n                return false;\n            }\n\n            @Override\n            public void setReadListener(ReadListener readListener) {\n\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/filter/XssFilter.java",
    "content": "package top.flya.common.filter;\n\nimport top.flya.common.enums.HttpMethod;\nimport top.flya.common.utils.StringUtils;\n\nimport javax.servlet.*;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 防止XSS攻击的过滤器\n *\n * @author ruoyi\n */\npublic class XssFilter implements Filter {\n    /**\n     * 排除链接\n     */\n    public List<String> excludes = new ArrayList<>();\n\n    @Override\n    public void init(FilterConfig filterConfig) throws ServletException {\n        String tempExcludes = filterConfig.getInitParameter(\"excludes\");\n        if (StringUtils.isNotEmpty(tempExcludes)) {\n            String[] url = tempExcludes.split(StringUtils.SEPARATOR);\n            for (int i = 0; url != null && i < url.length; i++) {\n                excludes.add(url[i]);\n            }\n        }\n    }\n\n    @Override\n    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n        throws IOException, ServletException {\n        HttpServletRequest req = (HttpServletRequest) request;\n        HttpServletResponse resp = (HttpServletResponse) response;\n        if (handleExcludeURL(req, resp)) {\n            chain.doFilter(request, response);\n            return;\n        }\n        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);\n        chain.doFilter(xssRequest, response);\n    }\n\n    private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) {\n        String url = request.getServletPath();\n        String method = request.getMethod();\n        // GET DELETE 不过滤\n        if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) {\n            return true;\n        }\n        return StringUtils.matches(url, excludes);\n    }\n\n    @Override\n    public void destroy() {\n\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/filter/XssHttpServletRequestWrapper.java",
    "content": "package top.flya.common.filter;\n\nimport cn.hutool.core.io.IoUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.http.HtmlUtil;\nimport top.flya.common.utils.StringUtils;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\n\nimport javax.servlet.ReadListener;\nimport javax.servlet.ServletInputStream;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletRequestWrapper;\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * XSS过滤处理\n *\n * @author ruoyi\n */\npublic class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {\n    /**\n     * @param request\n     */\n    public XssHttpServletRequestWrapper(HttpServletRequest request) {\n        super(request);\n    }\n\n    @Override\n    public String[] getParameterValues(String name) {\n        String[] values = super.getParameterValues(name);\n        if (values != null) {\n            int length = values.length;\n            String[] escapesValues = new String[length];\n            for (int i = 0; i < length; i++) {\n                // 防xss攻击和过滤前后空格\n                escapesValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim();\n            }\n            return escapesValues;\n        }\n        return super.getParameterValues(name);\n    }\n\n    @Override\n    public ServletInputStream getInputStream() throws IOException {\n        // 非json类型，直接返回\n        if (!isJsonRequest()) {\n            return super.getInputStream();\n        }\n\n        // 为空，直接返回\n        String json = StrUtil.str(IoUtil.readBytes(super.getInputStream(), false), StandardCharsets.UTF_8);\n        if (StringUtils.isEmpty(json)) {\n            return super.getInputStream();\n        }\n\n        // xss过滤\n        json = HtmlUtil.cleanHtmlTag(json).trim();\n        byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8);\n        final ByteArrayInputStream bis = IoUtil.toStream(jsonBytes);\n        return new ServletInputStream() {\n            @Override\n            public boolean isFinished() {\n                return true;\n            }\n\n            @Override\n            public boolean isReady() {\n                return true;\n            }\n\n            @Override\n            public int available() throws IOException {\n                return jsonBytes.length;\n            }\n\n            @Override\n            public void setReadListener(ReadListener readListener) {\n            }\n\n            @Override\n            public int read() throws IOException {\n                return bis.read();\n            }\n        };\n    }\n\n    /**\n     * 是否是Json请求\n     */\n    public boolean isJsonRequest() {\n        String header = super.getHeader(HttpHeaders.CONTENT_TYPE);\n        return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/helper/DataBaseHelper.java",
    "content": "package top.flya.common.helper;\n\nimport cn.hutool.core.convert.Convert;\nimport com.baomidou.dynamic.datasource.DynamicRoutingDataSource;\nimport top.flya.common.enums.DataBaseType;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\n\nimport javax.sql.DataSource;\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.SQLException;\n\n/**\n * 数据库助手\n *\n * @author Lion Li\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class DataBaseHelper {\n\n    private static final DynamicRoutingDataSource DS = SpringUtils.getBean(DynamicRoutingDataSource.class);\n\n    /**\n     * 获取当前数据库类型\n     */\n    public static DataBaseType getDataBaseType() {\n        DataSource dataSource = DS.determineDataSource();\n        try (Connection conn = dataSource.getConnection()) {\n            DatabaseMetaData metaData = conn.getMetaData();\n            String databaseProductName = metaData.getDatabaseProductName();\n            return DataBaseType.find(databaseProductName);\n        } catch (SQLException e) {\n            throw new ServiceException(e.getMessage());\n        }\n    }\n\n    public static boolean isMySql() {\n        return DataBaseType.MY_SQL == getDataBaseType();\n    }\n\n    public static boolean isOracle() {\n        return DataBaseType.ORACLE == getDataBaseType();\n    }\n\n    public static boolean isPostgerSql() {\n        return DataBaseType.POSTGRE_SQL == getDataBaseType();\n    }\n\n    public static boolean isSqlServer() {\n        return DataBaseType.SQL_SERVER == getDataBaseType();\n    }\n\n    public static String findInSet(Object var1, String var2) {\n        DataBaseType dataBasyType = getDataBaseType();\n        String var = Convert.toStr(var1);\n        if (dataBasyType == DataBaseType.SQL_SERVER) {\n            // charindex(',100,' , ',0,100,101,') <> 0\n            return \"charindex(',\" + var + \",' , ','+\" + var2 + \"+',') <> 0\";\n        } else if (dataBasyType == DataBaseType.POSTGRE_SQL) {\n            // (select position(',100,' in ',0,100,101,')) <> 0\n            return \"(select position(',\" + var + \",' in ','||\" + var2 + \"||',')) <> 0\";\n        } else if (dataBasyType == DataBaseType.ORACLE) {\n            // instr(',0,100,101,' , ',100,') <> 0\n            return \"instr(','||\" + var2 + \"||',' , ',\" + var + \",') <> 0\";\n        }\n        // find_in_set('100' , '0,100,101')\n        return \"find_in_set('\" + var + \"' , \" + var2 + \") <> 0\";\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/helper/DataPermissionHelper.java",
    "content": "package top.flya.common.helper;\n\nimport cn.dev33.satoken.context.SaHolder;\nimport cn.dev33.satoken.context.model.SaStorage;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Supplier;\n\n/**\n * 数据权限助手\n *\n * @author Lion Li\n * @version 3.5.0\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\n@SuppressWarnings(\"unchecked cast\")\npublic class DataPermissionHelper {\n\n    private static final String DATA_PERMISSION_KEY = \"data:permission\";\n\n    public static <T> T getVariable(String key) {\n        Map<String, Object> context = getContext();\n        return (T) context.get(key);\n    }\n\n\n    public static void setVariable(String key, Object value) {\n        Map<String, Object> context = getContext();\n        context.put(key, value);\n    }\n\n    public static Map<String, Object> getContext() {\n        SaStorage saStorage = SaHolder.getStorage();\n        Object attribute = saStorage.get(DATA_PERMISSION_KEY);\n        if (ObjectUtil.isNull(attribute)) {\n            saStorage.set(DATA_PERMISSION_KEY, new HashMap<>());\n            attribute = saStorage.get(DATA_PERMISSION_KEY);\n        }\n        if (attribute instanceof Map) {\n            return (Map<String, Object>) attribute;\n        }\n        throw new NullPointerException(\"data permission context type exception\");\n    }\n\n    /**\n     * 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭)\n     */\n    public static void enableIgnore() {\n        InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build());\n    }\n\n    /**\n     * 关闭忽略数据权限\n     */\n    public static void disableIgnore() {\n        InterceptorIgnoreHelper.clearIgnoreStrategy();\n    }\n\n    /**\n     * 在忽略数据权限中执行\n     *\n     * @param handle 处理执行方法\n     */\n    public static void ignore(Runnable handle) {\n        enableIgnore();\n        try {\n            handle.run();\n        } finally {\n            disableIgnore();\n        }\n    }\n\n    /**\n     * 在忽略数据权限中执行\n     *\n     * @param handle 处理执行方法\n     */\n    public static <T> T ignore(Supplier<T> handle) {\n        enableIgnore();\n        try {\n            return handle.get();\n        } finally {\n            disableIgnore();\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/helper/LoginHelper.java",
    "content": "package top.flya.common.helper;\n\nimport cn.dev33.satoken.context.SaHolder;\nimport cn.dev33.satoken.context.model.SaStorage;\nimport cn.dev33.satoken.stp.SaLoginModel;\nimport cn.dev33.satoken.stp.StpUtil;\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.util.ObjectUtil;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.domain.model.LoginUser;\nimport top.flya.common.enums.DeviceType;\nimport top.flya.common.enums.UserType;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\n\n/**\n * 登录鉴权助手\n * <p>\n * user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app\n * deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios\n * 可以组成 用户类型与设备类型多对多的 权限灵活控制\n * <p>\n * 多用户体系 针对 多种用户类型 但权限控制不一致\n * 可以组成 多用户类型表与多设备类型 分别控制权限\n *\n * @author Lion Li\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class LoginHelper {\n\n    public static final String LOGIN_USER_KEY = \"loginUser\";\n    public static final String USER_KEY = \"userId\";\n\n    /**\n     * 登录系统\n     *\n     * @param loginUser 登录用户信息\n     */\n    public static void login(LoginUser loginUser) {\n        loginByDevice(loginUser, null);\n    }\n\n    /**\n     * 登录系统 基于 设备类型\n     * 针对相同用户体系不同设备\n     *\n     * @param loginUser 登录用户信息\n     */\n    public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {\n        SaStorage storage = SaHolder.getStorage();\n        storage.set(LOGIN_USER_KEY, loginUser);\n        storage.set(USER_KEY, loginUser.getUserId());\n        SaLoginModel model = new SaLoginModel();\n        if (ObjectUtil.isNotNull(deviceType)) {\n            model.setDevice(deviceType.getDevice());\n        }\n        StpUtil.login(loginUser.getLoginId(), model.setExtra(USER_KEY, loginUser.getUserId()));\n        StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);\n    }\n\n    /**\n     * 获取用户(多级缓存)\n     */\n    public static LoginUser getLoginUser() {\n        LoginUser loginUser = (LoginUser) SaHolder.getStorage().get(LOGIN_USER_KEY);\n        if (loginUser != null) {\n            return loginUser;\n        }\n        loginUser = (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY);\n        SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);\n        return loginUser;\n    }\n\n    /**\n     * 获取用户基于token\n     */\n    public static LoginUser getLoginUser(String token) {\n        return (LoginUser) StpUtil.getTokenSessionByToken(token).get(LOGIN_USER_KEY);\n    }\n\n    /**\n     * 获取用户id\n     */\n    public static Long getUserId() {\n        Long userId;\n        try {\n            userId = Convert.toLong(SaHolder.getStorage().get(USER_KEY));\n            if (ObjectUtil.isNull(userId)) {\n                userId = Convert.toLong(StpUtil.getExtra(USER_KEY));\n                SaHolder.getStorage().set(USER_KEY, userId);\n            }\n        } catch (Exception e) {\n            return null;\n        }\n        return userId;\n    }\n\n    /**\n     * 获取部门ID\n     */\n    public static Long getDeptId() {\n        return getLoginUser().getDeptId();\n    }\n\n    /**\n     * 获取用户账户\n     */\n    public static String getUsername() {\n        return getLoginUser().getUsername();\n    }\n\n    /**\n     * 获取用户类型\n     */\n    public static UserType getUserType() {\n        String loginId = StpUtil.getLoginIdAsString();\n        return UserType.getUserType(loginId);\n    }\n\n    /**\n     * 是否为管理员\n     *\n     * @param userId 用户ID\n     * @return 结果\n     */\n    public static boolean isAdmin(Long userId) {\n        return UserConstants.ADMIN_ID.equals(userId);\n    }\n\n    public static boolean isAdmin() {\n        return isAdmin(getUserId());\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/jackson/DictDataJsonSerializer.java",
    "content": "package top.flya.common.jackson;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.BeanProperty;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport com.fasterxml.jackson.databind.JsonSerializer;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.ContextualSerializer;\nimport top.flya.common.annotation.DictDataMapper;\nimport top.flya.common.core.service.DictService;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.BeansException;\n\nimport java.io.IOException;\nimport java.util.Objects;\n\n/**\n * 字典数据json序列化工具\n *\n * @author itino\n * @deprecated 建议使用通用翻译注解\n */\n@Deprecated\n@Slf4j\npublic class DictDataJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {\n\n    private String dictType;\n\n    @Override\n    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {\n        try {\n            DictService dictService = SpringUtils.getBean(DictService.class);\n            if (ObjectUtil.isNotNull(dictService)) {\n                String label = dictService.getDictLabel(dictType, value);\n                gen.writeString(StringUtils.isNotBlank(label) ? label : value);\n            } else {\n                gen.writeString(value);\n            }\n        } catch (BeansException e) {\n            log.error(\"字典数据未查到, 采用默认处理 => {}\", e.getMessage());\n            gen.writeString(value);\n        }\n    }\n\n    @Override\n    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {\n        DictDataMapper anno = property.getAnnotation(DictDataMapper.class);\n        if (Objects.nonNull(anno) && StrUtil.isNotBlank(anno.dictType())) {\n            this.dictType = anno.dictType();\n            return this;\n        }\n        return prov.findValueSerializer(property.getType(), property);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/jackson/SensitiveJsonSerializer.java",
    "content": "package top.flya.common.jackson;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.BeanProperty;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport com.fasterxml.jackson.databind.JsonSerializer;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.ContextualSerializer;\nimport top.flya.common.annotation.Sensitive;\nimport top.flya.common.core.service.SensitiveService;\nimport top.flya.common.enums.SensitiveStrategy;\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.BeansException;\n\nimport java.io.IOException;\nimport java.util.Objects;\n\n/**\n * 数据脱敏json序列化工具\n *\n * @author Yjoioooo\n */\n@Slf4j\npublic class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {\n\n    private SensitiveStrategy strategy;\n\n    @Override\n    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {\n        try {\n            SensitiveService sensitiveService = SpringUtils.getBean(SensitiveService.class);\n            if (ObjectUtil.isNotNull(sensitiveService) && sensitiveService.isSensitive()) {\n                gen.writeString(strategy.desensitizer().apply(value));\n            } else {\n                gen.writeString(value);\n            }\n        } catch (BeansException e) {\n            log.error(\"脱敏实现不存在, 采用默认处理 => {}\", e.getMessage());\n            gen.writeString(value);\n        }\n    }\n\n    @Override\n    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {\n        Sensitive annotation = property.getAnnotation(Sensitive.class);\n        if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) {\n            this.strategy = annotation.strategy();\n            return this;\n        }\n        return prov.findValueSerializer(property.getType(), property);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/translation/TranslationInterface.java",
    "content": "package top.flya.common.translation;\n\nimport top.flya.common.annotation.TranslationType;\n\n/**\n * 翻译接口 (实现类需标注 {@link TranslationType} 注解标明翻译类型)\n *\n * @author Lion Li\n */\npublic interface TranslationInterface<T> {\n\n    /**\n     * 翻译\n     *\n     * @param key 需要被翻译的键(不为空)\n     * @return 返回键对应的值\n     */\n    T translation(Object key, String other);\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/translation/handler/TranslationBeanSerializerModifier.java",
    "content": "package top.flya.common.translation.handler;\n\nimport com.fasterxml.jackson.databind.BeanDescription;\nimport com.fasterxml.jackson.databind.SerializationConfig;\nimport com.fasterxml.jackson.databind.ser.BeanPropertyWriter;\nimport com.fasterxml.jackson.databind.ser.BeanSerializerModifier;\n\nimport java.util.List;\n\n/**\n * Bean 序列化修改器 解决 Null 被单独处理问题\n *\n * @author Lion Li\n */\npublic class TranslationBeanSerializerModifier extends BeanSerializerModifier {\n\n    @Override\n    public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,\n                                                     List<BeanPropertyWriter> beanProperties) {\n        for (BeanPropertyWriter writer : beanProperties) {\n            // 如果序列化器为 TranslationHandler 的话 将 Null 值也交给他处理\n            if (writer.getSerializer() instanceof TranslationHandler) {\n                writer.assignNullSerializer(writer.getSerializer());\n            }\n        }\n        return beanProperties;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/translation/handler/TranslationHandler.java",
    "content": "package top.flya.common.translation.handler;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.BeanProperty;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport com.fasterxml.jackson.databind.JsonSerializer;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.ContextualSerializer;\nimport top.flya.common.annotation.Translation;\nimport top.flya.common.translation.TranslationInterface;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.reflect.ReflectUtils;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 翻译处理器\n *\n * @author Lion Li\n */\n@Slf4j\npublic class TranslationHandler extends JsonSerializer<Object> implements ContextualSerializer {\n\n    /**\n     * 全局翻译实现类映射器\n     */\n    public static final Map<String, TranslationInterface<?>> TRANSLATION_MAPPER = new ConcurrentHashMap<>();\n\n    private Translation translation;\n\n    @Override\n    public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {\n        TranslationInterface<?> trans = TRANSLATION_MAPPER.get(translation.type());\n        if (ObjectUtil.isNotNull(trans)) {\n            // 如果映射字段不为空 则取映射字段的值\n            if (StringUtils.isNotBlank(translation.mapper())) {\n                value = ReflectUtils.invokeGetter(gen.getCurrentValue(), translation.mapper());\n            }\n            // 如果为 null 直接写出\n            if (ObjectUtil.isNull(value)) {\n                gen.writeNull();\n                return;\n            }\n            Object result = trans.translation(value, translation.other());\n            gen.writeObject(result);\n        } else {\n            gen.writeObject(value);\n        }\n    }\n\n    @Override\n    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {\n        Translation translation = property.getAnnotation(Translation.class);\n        if (Objects.nonNull(translation)) {\n            this.translation = translation;\n            return this;\n        }\n        return prov.findValueSerializer(property.getType(), property);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/translation/impl/DeptNameTranslationImpl.java",
    "content": "package top.flya.common.translation.impl;\n\nimport top.flya.common.annotation.TranslationType;\nimport top.flya.common.constant.TransConstant;\nimport top.flya.common.core.service.DeptService;\nimport top.flya.common.translation.TranslationInterface;\nimport lombok.AllArgsConstructor;\nimport org.springframework.stereotype.Component;\n\n/**\n * 部门翻译实现\n *\n * @author Lion Li\n */\n@Component\n@AllArgsConstructor\n@TranslationType(type = TransConstant.DEPT_ID_TO_NAME)\npublic class DeptNameTranslationImpl implements TranslationInterface<String> {\n\n    private final DeptService deptService;\n\n    @Override\n    public String translation(Object key, String other) {\n        return deptService.selectDeptNameByIds(key.toString());\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/translation/impl/DictTypeTranslationImpl.java",
    "content": "package top.flya.common.translation.impl;\n\nimport top.flya.common.annotation.TranslationType;\nimport top.flya.common.constant.TransConstant;\nimport top.flya.common.core.service.DictService;\nimport top.flya.common.translation.TranslationInterface;\nimport top.flya.common.utils.StringUtils;\nimport lombok.AllArgsConstructor;\nimport org.springframework.stereotype.Component;\n\n/**\n * 字典翻译实现\n *\n * @author Lion Li\n */\n@Component\n@AllArgsConstructor\n@TranslationType(type = TransConstant.DICT_TYPE_TO_LABEL)\npublic class DictTypeTranslationImpl implements TranslationInterface<String> {\n\n    private final DictService dictService;\n\n    @Override\n    public String translation(Object key, String other) {\n        if (key instanceof String && StringUtils.isNotBlank(other)) {\n            return dictService.getDictLabel(other, key.toString());\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/translation/impl/OssUrlTranslationImpl.java",
    "content": "package top.flya.common.translation.impl;\n\nimport top.flya.common.annotation.TranslationType;\nimport top.flya.common.constant.TransConstant;\nimport top.flya.common.core.service.OssService;\nimport top.flya.common.translation.TranslationInterface;\nimport lombok.AllArgsConstructor;\nimport org.springframework.stereotype.Component;\n\n/**\n * OSS翻译实现\n *\n * @author Lion Li\n */\n@Component\n@AllArgsConstructor\n@TranslationType(type = TransConstant.OSS_ID_TO_URL)\npublic class OssUrlTranslationImpl implements TranslationInterface<String> {\n\n    private final OssService ossService;\n\n    @Override\n    public String translation(Object key, String other) {\n        return ossService.selectUrlByIds(key.toString());\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/translation/impl/UserNameTranslationImpl.java",
    "content": "package top.flya.common.translation.impl;\n\nimport top.flya.common.annotation.TranslationType;\nimport top.flya.common.constant.TransConstant;\nimport top.flya.common.core.service.UserService;\nimport top.flya.common.translation.TranslationInterface;\nimport lombok.AllArgsConstructor;\nimport org.springframework.stereotype.Component;\n\n/**\n * 用户名翻译实现\n *\n * @author Lion Li\n */\n@Component\n@AllArgsConstructor\n@TranslationType(type = TransConstant.USER_ID_TO_NAME)\npublic class UserNameTranslationImpl implements TranslationInterface<String> {\n\n    private final UserService userService;\n\n    @Override\n    public String translation(Object key, String other) {\n        if (key instanceof Long) {\n            return userService.selectUserNameById((Long) key);\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/BeanCopyUtils.java",
    "content": "package top.flya.common.utils;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.SimpleCache;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.ReflectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport org.springframework.cglib.beans.BeanCopier;\nimport org.springframework.cglib.beans.BeanMap;\nimport org.springframework.cglib.core.Converter;\n\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * bean深拷贝工具(基于 cglib 性能优异)\n * <p>\n * 重点 cglib 不支持 拷贝到链式对象\n * 例如: 源对象 拷贝到 目标(链式对象)\n * 请区分好`浅拷贝`和`深拷贝`再做使用\n *\n * @author Lion Li\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class BeanCopyUtils {\n\n    /**\n     * 单对象基于class创建拷贝\n     *\n     * @param source 数据来源实体\n     * @param desc   描述对象 转换后的对象\n     * @return desc\n     */\n    public static <T, V> V copy(T source, Class<V> desc) {\n        if (ObjectUtil.isNull(source)) {\n            return null;\n        }\n        if (ObjectUtil.isNull(desc)) {\n            return null;\n        }\n        final V target = ReflectUtil.newInstanceIfPossible(desc);\n        return copy(source, target);\n    }\n\n    /**\n     * 单对象基于对象创建拷贝\n     *\n     * @param source 数据来源实体\n     * @param desc   转换后的对象\n     * @return desc\n     */\n    public static <T, V> V copy(T source, V desc) {\n        if (ObjectUtil.isNull(source)) {\n            return null;\n        }\n        if (ObjectUtil.isNull(desc)) {\n            return null;\n        }\n        BeanCopier beanCopier = BeanCopierCache.INSTANCE.get(source.getClass(), desc.getClass(), null);\n        beanCopier.copy(source, desc, null);\n        return desc;\n    }\n\n    /**\n     * 列表对象基于class创建拷贝\n     *\n     * @param sourceList 数据来源实体列表\n     * @param desc       描述对象 转换后的对象\n     * @return desc\n     */\n    public static <T, V> List<V> copyList(List<T> sourceList, Class<V> desc) {\n        if (ObjectUtil.isNull(sourceList)) {\n            return null;\n        }\n        if (CollUtil.isEmpty(sourceList)) {\n            return CollUtil.newArrayList();\n        }\n        return StreamUtils.toList(sourceList, source -> {\n            V target = ReflectUtil.newInstanceIfPossible(desc);\n            copy(source, target);\n            return target;\n        });\n    }\n\n    /**\n     * bean拷贝到map\n     *\n     * @param bean 数据来源实体\n     * @return map对象\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T> Map<String, Object> copyToMap(T bean) {\n        if (ObjectUtil.isNull(bean)) {\n            return null;\n        }\n        return BeanMap.create(bean);\n    }\n\n    /**\n     * map拷贝到bean\n     *\n     * @param map       数据来源\n     * @param beanClass bean类\n     * @return bean对象\n     */\n    public static <T> T mapToBean(Map<String, Object> map, Class<T> beanClass) {\n        if (MapUtil.isEmpty(map)) {\n            return null;\n        }\n        if (ObjectUtil.isNull(beanClass)) {\n            return null;\n        }\n        T bean = ReflectUtil.newInstanceIfPossible(beanClass);\n        return mapToBean(map, bean);\n    }\n\n    /**\n     * map拷贝到bean\n     *\n     * @param map  数据来源\n     * @param bean bean对象\n     * @return bean对象\n     */\n    public static <T> T mapToBean(Map<String, Object> map, T bean) {\n        if (MapUtil.isEmpty(map)) {\n            return null;\n        }\n        if (ObjectUtil.isNull(bean)) {\n            return null;\n        }\n        BeanMap.create(bean).putAll(map);\n        return bean;\n    }\n\n    /**\n     * map拷贝到map\n     *\n     * @param map   数据来源\n     * @param clazz 返回的对象类型\n     * @return map对象\n     */\n    public static <T, V> Map<String, V> mapToMap(Map<String, T> map, Class<V> clazz) {\n        if (MapUtil.isEmpty(map)) {\n            return null;\n        }\n        if (ObjectUtil.isNull(clazz)) {\n            return null;\n        }\n        Map<String, V> copyMap = new LinkedHashMap<>(map.size());\n        map.forEach((k, v) -> copyMap.put(k, copy(v, clazz)));\n        return copyMap;\n    }\n\n    /**\n     * BeanCopier属性缓存<br>\n     * 缓存用于防止多次反射造成的性能问题\n     *\n     * @author Looly\n     * @since 5.4.1\n     */\n    public enum BeanCopierCache {\n        /**\n         * BeanCopier属性缓存单例\n         */\n        INSTANCE;\n\n        private final SimpleCache<String, BeanCopier> cache = new SimpleCache<>();\n\n        /**\n         * 获得类与转换器生成的key在{@link BeanCopier}的Map中对应的元素\n         *\n         * @param srcClass    源Bean的类\n         * @param targetClass 目标Bean的类\n         * @param converter   转换器\n         * @return Map中对应的BeanCopier\n         */\n        public BeanCopier get(Class<?> srcClass, Class<?> targetClass, Converter converter) {\n            final String key = genKey(srcClass, targetClass, converter);\n            return cache.get(key, () -> BeanCopier.create(srcClass, targetClass, converter != null));\n        }\n\n        /**\n         * 获得类与转换器生成的key\n         *\n         * @param srcClass    源Bean的类\n         * @param targetClass 目标Bean的类\n         * @param converter   转换器\n         * @return 属性名和Map映射的key\n         */\n        private String genKey(Class<?> srcClass, Class<?> targetClass, Converter converter) {\n            final StringBuilder key = StrUtil.builder()\n                .append(srcClass.getName()).append('#').append(targetClass.getName());\n            if (null != converter) {\n                key.append('#').append(converter.getClass().getName());\n            }\n            return key.toString();\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/DateUtils.java",
    "content": "package top.flya.common.utils;\n\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport org.apache.commons.lang3.time.DateFormatUtils;\n\nimport java.lang.management.ManagementFactory;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.time.*;\nimport java.util.Calendar;\nimport java.util.Date;\n\n/**\n * 时间工具类\n *\n * @author ruoyi\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class DateUtils extends org.apache.commons.lang3.time.DateUtils {\n\n    public static final String YYYY = \"yyyy\";\n\n    public static final String YYYY_MM = \"yyyy-MM\";\n\n    public static final String YYYY_MM_DD = \"yyyy-MM-dd\";\n\n    public static final String YYYYMMDDHHMMSS = \"yyyyMMddHHmmss\";\n\n    public static final String YYYY_MM_DD_HH_MM_SS = \"yyyy-MM-dd HH:mm:ss\";\n\n    private static final String[] PARSE_PATTERNS = {\n        \"yyyy-MM-dd\", \"yyyy-MM-dd HH:mm:ss\", \"yyyy-MM-dd HH:mm\", \"yyyy-MM\",\n        \"yyyy/MM/dd\", \"yyyy/MM/dd HH:mm:ss\", \"yyyy/MM/dd HH:mm\", \"yyyy/MM\",\n        \"yyyy.MM.dd\", \"yyyy.MM.dd HH:mm:ss\", \"yyyy.MM.dd HH:mm\", \"yyyy.MM\"};\n\n    /**\n     * 获取当前Date型日期\n     *\n     * @return Date() 当前日期\n     */\n    public static Date getNowDate() {\n        return new Date();\n    }\n\n    /**\n     * 获取当前日期, 默认格式为yyyy-MM-dd\n     *\n     * @return String\n     */\n    public static String getDate() {\n        return dateTimeNow(YYYY_MM_DD);\n    }\n\n    public static String getTime() {\n        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);\n    }\n\n    public static String dateTimeNow() {\n        return dateTimeNow(YYYYMMDDHHMMSS);\n    }\n\n    public static String dateTimeNow(final String format) {\n        return parseDateToStr(format, new Date());\n    }\n\n    public static String dateTime(final Date date) {\n        return parseDateToStr(YYYY_MM_DD, date);\n    }\n\n    public static String parseDateToStr(final String format, final Date date) {\n        return new SimpleDateFormat(format).format(date);\n    }\n\n    public static Date dateTime(final String format, final String ts) {\n        try {\n            return new SimpleDateFormat(format).parse(ts);\n        } catch (ParseException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    /**\n     * 日期路径 即年/月/日 如2018/08/08\n     */\n    public static String datePath() {\n        Date now = new Date();\n        return DateFormatUtils.format(now, \"yyyy/MM/dd\");\n    }\n\n    /**\n     * 日期路径 即年/月/日 如20180808\n     */\n    public static String dateTime() {\n        Date now = new Date();\n        return DateFormatUtils.format(now, \"yyyyMMdd\");\n    }\n\n    /**\n     * 日期型字符串转化为日期 格式\n     */\n    public static Date parseDate(Object str) {\n        if (str == null) {\n            return null;\n        }\n        try {\n            return parseDate(str.toString(), PARSE_PATTERNS);\n        } catch (ParseException e) {\n            return null;\n        }\n    }\n\n    /**\n     * 获取服务器启动时间\n     */\n    public static Date getServerStartDate() {\n        long time = ManagementFactory.getRuntimeMXBean().getStartTime();\n        return new Date(time);\n    }\n\n    /**\n     * 计算相差天数\n     */\n    public static int differentDaysByMillisecond(Date date1, Date date2) {\n        return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));\n    }\n\n    /**\n     * 计算两个时间差\n     */\n    public static String getDatePoor(Date endDate, Date nowDate) {\n        long nd = 1000 * 24 * 60 * 60;\n        long nh = 1000 * 60 * 60;\n        long nm = 1000 * 60;\n        // long ns = 1000;\n        // 获得两个时间的毫秒时间差异\n        long diff = endDate.getTime() - nowDate.getTime();\n        // 计算差多少天\n        long day = diff / nd;\n        // 计算差多少小时\n        long hour = diff % nd / nh;\n        // 计算差多少分钟\n        long min = diff % nd % nh / nm;\n        // 计算差多少秒//输出结果\n        // long sec = diff % nd % nh % nm / ns;\n        return day + \"天\" + hour + \"小时\" + min + \"分钟\";\n    }\n\n    /**\n     * 增加 LocalDateTime ==> Date\n     */\n    public static Date toDate(LocalDateTime temporalAccessor) {\n        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());\n        return Date.from(zdt.toInstant());\n    }\n\n    /**\n     * 增加 LocalDate ==> Date\n     */\n    public static Date toDate(LocalDate temporalAccessor) {\n        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));\n        ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());\n        return Date.from(zdt.toInstant());\n    }\n\n    public static String[] getMonthStartAndEnd(String timeStr) throws ParseException {\n        SimpleDateFormat format = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        Date date = format.parse(timeStr);\n\n        Calendar calendar = Calendar.getInstance();\n        calendar.setTime(date);\n\n        // 获取本月开始时间\n        calendar.set(Calendar.DAY_OF_MONTH, 1);\n        String monthStart = format.format(calendar.getTime());\n\n        // 获取本月结束时间\n        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));\n        calendar.set(Calendar.HOUR_OF_DAY, 23);\n        calendar.set(Calendar.MINUTE, 59);\n        calendar.set(Calendar.SECOND, 59);\n        String monthEnd = format.format(calendar.getTime());\n\n        return new String[] { monthStart, monthEnd };\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/EncryptUtils.java",
    "content": "package top.flya.common.utils;\n\nimport cn.hutool.core.codec.Base64;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.crypto.SecureUtil;\nimport cn.hutool.crypto.SmUtil;\nimport cn.hutool.crypto.asymmetric.KeyType;\nimport cn.hutool.crypto.asymmetric.RSA;\nimport cn.hutool.crypto.asymmetric.SM2;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 安全相关工具类\n *\n * @author 老马\n */\npublic class EncryptUtils {\n    /**\n     * 公钥\n     */\n    public static final String PUBLIC_KEY = \"publicKey\";\n    /**\n     * 私钥\n     */\n    public static final String PRIVATE_KEY = \"privateKey\";\n\n    /**\n     * Base64加密\n     *\n     * @param data 待加密数据\n     * @return 加密后字符串\n     */\n    public static String encryptByBase64(String data) {\n        return Base64.encode(data, StandardCharsets.UTF_8);\n    }\n\n    /**\n     * Base64解密\n     *\n     * @param data 待解密数据\n     * @return 解密后字符串\n     */\n    public static String decryptByBase64(String data) {\n        return Base64.decodeStr(data, StandardCharsets.UTF_8);\n    }\n\n    /**\n     * AES加密\n     *\n     * @param data     待解密数据\n     * @param password 秘钥字符串\n     * @return 加密后字符串, 采用Base64编码\n     */\n    public static String encryptByAes(String data, String password) {\n        if (StrUtil.isBlank(password)) {\n            throw new IllegalArgumentException(\"AES需要传入秘钥信息\");\n        }\n        // aes算法的秘钥要求是16位、24位、32位\n        int[] array = {16, 24, 32};\n        if (!ArrayUtil.contains(array, password.length())) {\n            throw new IllegalArgumentException(\"AES秘钥长度要求为16位、24位、32位\");\n        }\n        return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);\n    }\n\n    /**\n     * AES解密\n     *\n     * @param data     待解密数据\n     * @param password 秘钥字符串\n     * @return 解密后字符串\n     */\n    public static String decryptByAes(String data, String password) {\n        if (StrUtil.isBlank(password)) {\n            throw new IllegalArgumentException(\"AES需要传入秘钥信息\");\n        }\n        // aes算法的秘钥要求是16位、24位、32位\n        int[] array = {16, 24, 32};\n        if (!ArrayUtil.contains(array, password.length())) {\n            throw new IllegalArgumentException(\"AES秘钥长度要求为16位、24位、32位\");\n        }\n        return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);\n    }\n\n    /**\n     * sm4加密\n     *\n     * @param data     待加密数据\n     * @param password 秘钥字符串\n     * @return 加密后字符串, 采用Base64编码\n     */\n    public static String encryptBySm4(String data, String password) {\n        if (StrUtil.isBlank(password)) {\n            throw new IllegalArgumentException(\"SM4需要传入秘钥信息\");\n        }\n        // sm4算法的秘钥要求是16位长度\n        int sm4PasswordLength = 16;\n        if (sm4PasswordLength != password.length()) {\n            throw new IllegalArgumentException(\"SM4秘钥长度要求为16位\");\n        }\n        return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);\n    }\n\n    /**\n     * sm4解密\n     *\n     * @param data     待解密数据\n     * @param password 秘钥字符串\n     * @return 解密后字符串\n     */\n    public static String decryptBySm4(String data, String password) {\n        if (StrUtil.isBlank(password)) {\n            throw new IllegalArgumentException(\"SM4需要传入秘钥信息\");\n        }\n        // sm4算法的秘钥要求是16位长度\n        int sm4PasswordLength = 16;\n        if (sm4PasswordLength != password.length()) {\n            throw new IllegalArgumentException(\"SM4秘钥长度要求为16位\");\n        }\n        return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);\n    }\n\n    /**\n     * 产生sm2加解密需要的公钥和私钥\n     *\n     * @return 公私钥Map\n     */\n    public static Map<String, String> generateSm2Key() {\n        Map<String, String> keyMap = new HashMap<>(2);\n        SM2 sm2 = SmUtil.sm2();\n        keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64());\n        keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64());\n        return keyMap;\n    }\n\n    /**\n     * sm2公钥加密\n     *\n     * @param data      待加密数据\n     * @param publicKey 公钥\n     * @return 加密后字符串, 采用Base64编码\n     */\n    public static String encryptBySm2(String data, String publicKey) {\n        if (StrUtil.isBlank(publicKey)) {\n            throw new IllegalArgumentException(\"SM2需要传入公钥进行加密\");\n        }\n        SM2 sm2 = SmUtil.sm2(null, publicKey);\n        return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);\n    }\n\n    /**\n     * sm2私钥解密\n     *\n     * @param data       待加密数据\n     * @param privateKey 私钥\n     * @return 解密后字符串\n     */\n    public static String decryptBySm2(String data, String privateKey) {\n        if (StrUtil.isBlank(privateKey)) {\n            throw new IllegalArgumentException(\"SM2需要传入私钥进行解密\");\n        }\n        SM2 sm2 = SmUtil.sm2(privateKey, null);\n        return sm2.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);\n    }\n\n    /**\n     * 产生RSA加解密需要的公钥和私钥\n     *\n     * @return 公私钥Map\n     */\n    public static Map<String, String> generateRsaKey() {\n        Map<String, String> keyMap = new HashMap<>(2);\n        RSA rsa = SecureUtil.rsa();\n        keyMap.put(PRIVATE_KEY, rsa.getPrivateKeyBase64());\n        keyMap.put(PUBLIC_KEY, rsa.getPublicKeyBase64());\n        return keyMap;\n    }\n\n    /**\n     * rsa公钥加密\n     *\n     * @param data      待加密数据\n     * @param publicKey 公钥\n     * @return 加密后字符串, 采用Base64编码\n     */\n    public static String encryptByRsa(String data, String publicKey) {\n        if (StrUtil.isBlank(publicKey)) {\n            throw new IllegalArgumentException(\"RSA需要传入公钥进行加密\");\n        }\n        RSA rsa = SecureUtil.rsa(null, publicKey);\n        return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);\n    }\n\n    /**\n     * rsa私钥解密\n     *\n     * @param data       待加密数据\n     * @param privateKey 私钥\n     * @return 解密后字符串\n     */\n    public static String decryptByRsa(String data, String privateKey) {\n        if (StrUtil.isBlank(privateKey)) {\n            throw new IllegalArgumentException(\"RSA需要传入私钥进行解密\");\n        }\n        RSA rsa = SecureUtil.rsa(privateKey, null);\n        return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);\n    }\n\n    /**\n     * md5加密\n     *\n     * @param data 待加密数据\n     * @return 加密后字符串, 采用Hex编码\n     */\n    public static String encryptByMd5(String data) {\n        return SecureUtil.md5(data);\n    }\n\n    /**\n     * sha256加密\n     *\n     * @param data 待加密数据\n     * @return 加密后字符串, 采用Hex编码\n     */\n    public static String encryptBySha256(String data) {\n        return SecureUtil.sha256(data);\n    }\n\n    /**\n     * sm3加密\n     *\n     * @param data 待加密数据\n     * @return 加密后字符串, 采用Hex编码\n     */\n    public static String encryptBySm3(String data) {\n        return SmUtil.sm3(data);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/JsonUtils.java",
    "content": "package top.flya.common.utils;\n\nimport cn.hutool.core.lang.Dict;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.exc.MismatchedInputException;\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * JSON 工具类\n *\n * @author 芋道源码\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class JsonUtils {\n\n    private static final ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class);\n\n    public static ObjectMapper getObjectMapper() {\n        return OBJECT_MAPPER;\n    }\n\n    public static String toJsonString(Object object) {\n        if (ObjectUtil.isNull(object)) {\n            return null;\n        }\n        try {\n            return OBJECT_MAPPER.writeValueAsString(object);\n        } catch (JsonProcessingException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static <T> T parseObject(String text, Class<T> clazz) {\n        if (StringUtils.isEmpty(text)) {\n            return null;\n        }\n        try {\n            return OBJECT_MAPPER.readValue(text, clazz);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static <T> T parseObject(byte[] bytes, Class<T> clazz) {\n        if (ArrayUtil.isEmpty(bytes)) {\n            return null;\n        }\n        try {\n            return OBJECT_MAPPER.readValue(bytes, clazz);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static <T> T parseObject(String text, TypeReference<T> typeReference) {\n        if (StringUtils.isBlank(text)) {\n            return null;\n        }\n        try {\n            return OBJECT_MAPPER.readValue(text, typeReference);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static Dict parseMap(String text) {\n        if (StringUtils.isBlank(text)) {\n            return null;\n        }\n        try {\n            return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructType(Dict.class));\n        } catch (MismatchedInputException e) {\n            // 类型不匹配说明不是json\n            return null;\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static List<Dict> parseArrayMap(String text) {\n        if (StringUtils.isBlank(text)) {\n            return null;\n        }\n        try {\n            return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, Dict.class));\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static <T> List<T> parseArray(String text, Class<T> clazz) {\n        if (StringUtils.isEmpty(text)) {\n            return new ArrayList<>();\n        }\n        try {\n            return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/MessageUtils.java",
    "content": "package top.flya.common.utils;\n\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.i18n.LocaleContextHolder;\n\n/**\n * 获取i18n资源文件\n *\n * @author Lion Li\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class MessageUtils {\n\n    private static final MessageSource MESSAGE_SOURCE = SpringUtils.getBean(MessageSource.class);\n\n    /**\n     * 根据消息键和参数 获取消息 委托给spring messageSource\n     *\n     * @param code 消息键\n     * @param args 参数\n     * @return 获取国际化翻译值\n     */\n    public static String message(String code, Object... args) {\n        return MESSAGE_SOURCE.getMessage(code, args, LocaleContextHolder.getLocale());\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/ServletUtils.java",
    "content": "package top.flya.common.utils;\n\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.extra.servlet.ServletUtil;\nimport cn.hutool.http.HttpStatus;\nimport top.flya.common.constant.Constants;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.context.request.RequestAttributes;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\nimport javax.servlet.ServletRequest;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLDecoder;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 客户端工具类\n *\n * @author ruoyi\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class ServletUtils extends ServletUtil {\n\n    /**\n     * 获取String参数\n     */\n    public static String getParameter(String name) {\n        return getRequest().getParameter(name);\n    }\n\n    /**\n     * 获取String参数\n     */\n    public static String getParameter(String name, String defaultValue) {\n        return Convert.toStr(getRequest().getParameter(name), defaultValue);\n    }\n\n    /**\n     * 获取Integer参数\n     */\n    public static Integer getParameterToInt(String name) {\n        return Convert.toInt(getRequest().getParameter(name));\n    }\n\n    /**\n     * 获取Integer参数\n     */\n    public static Integer getParameterToInt(String name, Integer defaultValue) {\n        return Convert.toInt(getRequest().getParameter(name), defaultValue);\n    }\n\n    /**\n     * 获取Boolean参数\n     */\n    public static Boolean getParameterToBool(String name) {\n        return Convert.toBool(getRequest().getParameter(name));\n    }\n\n    /**\n     * 获取Boolean参数\n     */\n    public static Boolean getParameterToBool(String name, Boolean defaultValue) {\n        return Convert.toBool(getRequest().getParameter(name), defaultValue);\n    }\n\n    /**\n     * 获得所有请求参数\n     *\n     * @param request 请求对象{@link ServletRequest}\n     * @return Map\n     */\n    public static Map<String, String[]> getParams(ServletRequest request) {\n        final Map<String, String[]> map = request.getParameterMap();\n        return Collections.unmodifiableMap(map);\n    }\n\n    /**\n     * 获得所有请求参数\n     *\n     * @param request 请求对象{@link ServletRequest}\n     * @return Map\n     */\n    public static Map<String, String> getParamMap(ServletRequest request) {\n        Map<String, String> params = new HashMap<>();\n        for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {\n            params.put(entry.getKey(), StringUtils.join(entry.getValue(), StringUtils.SEPARATOR));\n        }\n        return params;\n    }\n\n    /**\n     * 获取request\n     */\n    public static HttpServletRequest getRequest() {\n        return getRequestAttributes().getRequest();\n    }\n\n    /**\n     * 获取response\n     */\n    public static HttpServletResponse getResponse() {\n        return getRequestAttributes().getResponse();\n    }\n\n    /**\n     * 获取session\n     */\n    public static HttpSession getSession() {\n        return getRequest().getSession();\n    }\n\n    public static ServletRequestAttributes getRequestAttributes() {\n        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();\n        return (ServletRequestAttributes) attributes;\n    }\n\n    /**\n     * 将字符串渲染到客户端\n     *\n     * @param response 渲染对象\n     * @param string   待渲染的字符串\n     */\n    public static void renderString(HttpServletResponse response, String string) {\n        try {\n            response.setStatus(HttpStatus.HTTP_OK);\n            response.setContentType(MediaType.APPLICATION_JSON_VALUE);\n            response.setCharacterEncoding(StandardCharsets.UTF_8.toString());\n            response.getWriter().print(string);\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n    /**\n     * 是否是Ajax异步请求\n     *\n     * @param request\n     */\n    public static boolean isAjaxRequest(HttpServletRequest request) {\n\n        String accept = request.getHeader(\"accept\");\n        if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) {\n            return true;\n        }\n\n        String xRequestedWith = request.getHeader(\"X-Requested-With\");\n        if (xRequestedWith != null && xRequestedWith.contains(\"XMLHttpRequest\")) {\n            return true;\n        }\n\n        String uri = request.getRequestURI();\n        if (StringUtils.equalsAnyIgnoreCase(uri, \".json\", \".xml\")) {\n            return true;\n        }\n\n        String ajax = request.getParameter(\"__ajax\");\n        return StringUtils.equalsAnyIgnoreCase(ajax, \"json\", \"xml\");\n    }\n\n    public static String getClientIP() {\n        return getClientIP(getRequest());\n    }\n\n    /**\n     * 内容编码\n     *\n     * @param str 内容\n     * @return 编码后的内容\n     */\n    public static String urlEncode(String str) {\n        try {\n            return URLEncoder.encode(str, Constants.UTF8);\n        } catch (UnsupportedEncodingException e) {\n            return StringUtils.EMPTY;\n        }\n    }\n\n    /**\n     * 内容解码\n     *\n     * @param str 内容\n     * @return 解码后的内容\n     */\n    public static String urlDecode(String str) {\n        try {\n            return URLDecoder.decode(str, Constants.UTF8);\n        } catch (UnsupportedEncodingException e) {\n            return StringUtils.EMPTY;\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/StreamUtils.java",
    "content": "package top.flya.common.utils;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\n\nimport java.util.*;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\n/**\n * stream 流工具类\n *\n * @author Lion Li\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class StreamUtils {\n\n    /**\n     * 将collection过滤\n     *\n     * @param collection 需要转化的集合\n     * @param function   过滤方法\n     * @return 过滤后的list\n     */\n    public static <E> List<E> filter(Collection<E> collection, Predicate<E> function) {\n        if (CollUtil.isEmpty(collection)) {\n            return CollUtil.newArrayList();\n        }\n        return collection.stream().filter(function).collect(Collectors.toList());\n    }\n\n    /**\n     * 将collection拼接\n     *\n     * @param collection 需要转化的集合\n     * @param function   拼接方法\n     * @return 拼接后的list\n     */\n    public static <E> String join(Collection<E> collection, Function<E, String> function) {\n        return join(collection, function, StringUtils.SEPARATOR);\n    }\n\n    /**\n     * 将collection拼接\n     *\n     * @param collection 需要转化的集合\n     * @param function   拼接方法\n     * @param delimiter  拼接符\n     * @return 拼接后的list\n     */\n    public static <E> String join(Collection<E> collection, Function<E, String> function, CharSequence delimiter) {\n        if (CollUtil.isEmpty(collection)) {\n            return StringUtils.EMPTY;\n        }\n        return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter));\n    }\n\n    /**\n     * 将collection排序\n     *\n     * @param collection 需要转化的集合\n     * @param comparing  排序方法\n     * @return 排序后的list\n     */\n    public static <E> List<E> sorted(Collection<E> collection, Comparator<E> comparing) {\n        if (CollUtil.isEmpty(collection)) {\n            return CollUtil.newArrayList();\n        }\n        return collection.stream().sorted(comparing).collect(Collectors.toList());\n    }\n\n    /**\n     * 将collection转化为类型不变的map<br>\n     * <B>{@code Collection<V>  ---->  Map<K,V>}</B>\n     *\n     * @param collection 需要转化的集合\n     * @param key        V类型转化为K类型的lambda方法\n     * @param <V>        collection中的泛型\n     * @param <K>        map中的key类型\n     * @return 转化后的map\n     */\n    public static <V, K> Map<K, V> toIdentityMap(Collection<V> collection, Function<V, K> key) {\n        if (CollUtil.isEmpty(collection)) {\n            return MapUtil.newHashMap();\n        }\n        return collection.stream().collect(Collectors.toMap(key, Function.identity(), (l, r) -> l));\n    }\n\n    /**\n     * 将Collection转化为map(value类型与collection的泛型不同)<br>\n     * <B>{@code Collection<E> -----> Map<K,V>  }</B>\n     *\n     * @param collection 需要转化的集合\n     * @param key        E类型转化为K类型的lambda方法\n     * @param value      E类型转化为V类型的lambda方法\n     * @param <E>        collection中的泛型\n     * @param <K>        map中的key类型\n     * @param <V>        map中的value类型\n     * @return 转化后的map\n     */\n    public static <E, K, V> Map<K, V> toMap(Collection<E> collection, Function<E, K> key, Function<E, V> value) {\n        if (CollUtil.isEmpty(collection)) {\n            return MapUtil.newHashMap();\n        }\n        return collection.stream().collect(Collectors.toMap(key, value, (l, r) -> l));\n    }\n\n    /**\n     * 将collection按照规则(比如有相同的班级id)分类成map<br>\n     * <B>{@code Collection<E> -------> Map<K,List<E>> } </B>\n     *\n     * @param collection 需要分类的集合\n     * @param key        分类的规则\n     * @param <E>        collection中的泛型\n     * @param <K>        map中的key类型\n     * @return 分类后的map\n     */\n    public static <E, K> Map<K, List<E>> groupByKey(Collection<E> collection, Function<E, K> key) {\n        if (CollUtil.isEmpty(collection)) {\n            return MapUtil.newHashMap();\n        }\n        return collection\n            .stream()\n            .collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList()));\n    }\n\n    /**\n     * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map<br>\n     * <B>{@code Collection<E>  --->  Map<T,Map<U,List<E>>> } </B>\n     *\n     * @param collection 需要分类的集合\n     * @param key1       第一个分类的规则\n     * @param key2       第二个分类的规则\n     * @param <E>        集合元素类型\n     * @param <K>        第一个map中的key类型\n     * @param <U>        第二个map中的key类型\n     * @return 分类后的map\n     */\n    public static <E, K, U> Map<K, Map<U, List<E>>> groupBy2Key(Collection<E> collection, Function<E, K> key1, Function<E, U> key2) {\n        if (CollUtil.isEmpty(collection)) {\n            return MapUtil.newHashMap();\n        }\n        return collection\n            .stream()\n            .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList())));\n    }\n\n    /**\n     * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map<br>\n     * <B>{@code Collection<E>  --->  Map<T,Map<U,E>> } </B>\n     *\n     * @param collection 需要分类的集合\n     * @param key1       第一个分类的规则\n     * @param key2       第二个分类的规则\n     * @param <T>        第一个map中的key类型\n     * @param <U>        第二个map中的key类型\n     * @param <E>        collection中的泛型\n     * @return 分类后的map\n     */\n    public static <E, T, U> Map<T, Map<U, E>> group2Map(Collection<E> collection, Function<E, T> key1, Function<E, U> key2) {\n        if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) {\n            return MapUtil.newHashMap();\n        }\n        return collection\n            .stream()\n            .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l)));\n    }\n\n    /**\n     * 将collection转化为List集合，但是两者的泛型不同<br>\n     * <B>{@code Collection<E>  ------>  List<T> } </B>\n     *\n     * @param collection 需要转化的集合\n     * @param function   collection中的泛型转化为list泛型的lambda表达式\n     * @param <E>        collection中的泛型\n     * @param <T>        List中的泛型\n     * @return 转化后的list\n     */\n    public static <E, T> List<T> toList(Collection<E> collection, Function<E, T> function) {\n        if (CollUtil.isEmpty(collection)) {\n            return CollUtil.newArrayList();\n        }\n        return collection\n            .stream()\n            .map(function)\n            .filter(Objects::nonNull)\n            .collect(Collectors.toList());\n    }\n\n    /**\n     * 将collection转化为Set集合，但是两者的泛型不同<br>\n     * <B>{@code Collection<E>  ------>  Set<T> } </B>\n     *\n     * @param collection 需要转化的集合\n     * @param function   collection中的泛型转化为set泛型的lambda表达式\n     * @param <E>        collection中的泛型\n     * @param <T>        Set中的泛型\n     * @return 转化后的Set\n     */\n    public static <E, T> Set<T> toSet(Collection<E> collection, Function<E, T> function) {\n        if (CollUtil.isEmpty(collection) || function == null) {\n            return CollUtil.newHashSet();\n        }\n        return collection\n            .stream()\n            .map(function)\n            .filter(Objects::nonNull)\n            .collect(Collectors.toSet());\n    }\n\n\n    /**\n     * 合并两个相同key类型的map\n     *\n     * @param map1  第一个需要合并的 map\n     * @param map2  第二个需要合并的 map\n     * @param merge 合并的lambda，将key  value1 value2合并成最终的类型,注意value可能为空的情况\n     * @param <K>   map中的key类型\n     * @param <X>   第一个 map的value类型\n     * @param <Y>   第二个 map的value类型\n     * @param <V>   最终map的value类型\n     * @return 合并后的map\n     */\n    public static <K, X, Y, V> Map<K, V> merge(Map<K, X> map1, Map<K, Y> map2, BiFunction<X, Y, V> merge) {\n        if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) {\n            return MapUtil.newHashMap();\n        } else if (MapUtil.isEmpty(map1)) {\n            map1 = MapUtil.newHashMap();\n        } else if (MapUtil.isEmpty(map2)) {\n            map2 = MapUtil.newHashMap();\n        }\n        Set<K> key = new HashSet<>();\n        key.addAll(map1.keySet());\n        key.addAll(map2.keySet());\n        Map<K, V> map = new HashMap<>();\n        for (K t : key) {\n            X x = map1.get(t);\n            Y y = map2.get(t);\n            V z = merge.apply(x, y);\n            if (z != null) {\n                map.put(t, z);\n            }\n        }\n        return map;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/StringUtils.java",
    "content": "package top.flya.common.utils;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.lang.Validator;\nimport cn.hutool.core.util.StrUtil;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport org.springframework.util.AntPathMatcher;\n\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * 字符串工具类\n *\n * @author Lion Li\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class StringUtils extends org.apache.commons.lang3.StringUtils {\n\n    public static final String SEPARATOR = \",\";\n\n    /**\n     * 获取参数不为空值\n     *\n     * @param str defaultValue 要判断的value\n     * @return value 返回值\n     */\n    public static String blankToDefault(String str, String defaultValue) {\n        return StrUtil.blankToDefault(str, defaultValue);\n    }\n\n    /**\n     * * 判断一个字符串是否为空串\n     *\n     * @param str String\n     * @return true：为空 false：非空\n     */\n    public static boolean isEmpty(String str) {\n        return StrUtil.isEmpty(str);\n    }\n\n    /**\n     * * 判断一个字符串是否为非空串\n     *\n     * @param str String\n     * @return true：非空串 false：空串\n     */\n    public static boolean isNotEmpty(String str) {\n        return !isEmpty(str);\n    }\n\n    /**\n     * 去空格\n     */\n    public static String trim(String str) {\n        return StrUtil.trim(str);\n    }\n\n    /**\n     * 截取字符串\n     *\n     * @param str   字符串\n     * @param start 开始\n     * @return 结果\n     */\n    public static String substring(final String str, int start) {\n        return substring(str, start, str.length());\n    }\n\n    /**\n     * 截取字符串\n     *\n     * @param str   字符串\n     * @param start 开始\n     * @param end   结束\n     * @return 结果\n     */\n    public static String substring(final String str, int start, int end) {\n        return StrUtil.sub(str, start, end);\n    }\n\n    /**\n     * 格式化文本, {} 表示占位符<br>\n     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>\n     * 如果想输出 {} 使用 \\\\转义 { 即可，如果想输出 {} 之前的 \\ 使用双转义符 \\\\\\\\ 即可<br>\n     * 例：<br>\n     * 通常使用：format(\"this is {} for {}\", \"a\", \"b\") -> this is a for b<br>\n     * 转义{}： format(\"this is \\\\{} for {}\", \"a\", \"b\") -> this is {} for a<br>\n     * 转义\\： format(\"this is \\\\\\\\{} for {}\", \"a\", \"b\") -> this is \\a for b<br>\n     *\n     * @param template 文本模板，被替换的部分用 {} 表示\n     * @param params   参数值\n     * @return 格式化后的文本\n     */\n    public static String format(String template, Object... params) {\n        return StrUtil.format(template, params);\n    }\n\n    /**\n     * 是否为http(s)://开头\n     *\n     * @param link 链接\n     * @return 结果\n     */\n    public static boolean ishttp(String link) {\n        return Validator.isUrl(link);\n    }\n\n    /**\n     * 字符串转set\n     *\n     * @param str 字符串\n     * @param sep 分隔符\n     * @return set集合\n     */\n    public static Set<String> str2Set(String str, String sep) {\n        return new HashSet<>(str2List(str, sep, true, false));\n    }\n\n    /**\n     * 字符串转list\n     *\n     * @param str         字符串\n     * @param sep         分隔符\n     * @param filterBlank 过滤纯空白\n     * @param trim        去掉首尾空白\n     * @return list集合\n     */\n    public static List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) {\n        List<String> list = new ArrayList<>();\n        if (isEmpty(str)) {\n            return list;\n        }\n\n        // 过滤空白字符串\n        if (filterBlank && isBlank(str)) {\n            return list;\n        }\n        String[] split = str.split(sep);\n        for (String string : split) {\n            if (filterBlank && isBlank(string)) {\n                continue;\n            }\n            if (trim) {\n                string = trim(string);\n            }\n            list.add(string);\n        }\n\n        return list;\n    }\n\n    /**\n     * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写\n     *\n     * @param cs                  指定字符串\n     * @param searchCharSequences 需要检查的字符串数组\n     * @return 是否包含任意一个字符串\n     */\n    public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) {\n        return StrUtil.containsAnyIgnoreCase(cs, searchCharSequences);\n    }\n\n    /**\n     * 驼峰转下划线命名\n     */\n    public static String toUnderScoreCase(String str) {\n        return StrUtil.toUnderlineCase(str);\n    }\n\n    /**\n     * 是否包含字符串\n     *\n     * @param str  验证字符串\n     * @param strs 字符串组\n     * @return 包含返回true\n     */\n    public static boolean inStringIgnoreCase(String str, String... strs) {\n        return StrUtil.equalsAnyIgnoreCase(str, strs);\n    }\n\n    /**\n     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空，则返回空字符串。 例如：HELLO_WORLD->HelloWorld\n     *\n     * @param name 转换前的下划线大写方式命名的字符串\n     * @return 转换后的驼峰式命名的字符串\n     */\n    public static String convertToCamelCase(String name) {\n        return StrUtil.upperFirst(StrUtil.toCamelCase(name));\n    }\n\n    /**\n     * 驼峰式命名法 例如：user_name->userName\n     */\n    public static String toCamelCase(String s) {\n        return StrUtil.toCamelCase(s);\n    }\n\n    /**\n     * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串\n     *\n     * @param str  指定字符串\n     * @param strs 需要检查的字符串数组\n     * @return 是否匹配\n     */\n    public static boolean matches(String str, List<String> strs) {\n        if (isEmpty(str) || CollUtil.isEmpty(strs)) {\n            return false;\n        }\n        for (String pattern : strs) {\n            if (isMatch(pattern, str)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 判断url是否与规则配置:\n     * ? 表示单个字符;\n     * * 表示一层路径内的任意字符串，不可跨层级;\n     * ** 表示任意层路径;\n     *\n     * @param pattern 匹配规则\n     * @param url     需要匹配的url\n     */\n    public static boolean isMatch(String pattern, String url) {\n        AntPathMatcher matcher = new AntPathMatcher();\n        return matcher.match(pattern, url);\n    }\n\n    /**\n     * 数字左边补齐0，使之达到指定长度。注意，如果数字转换为字符串后，长度大于size，则只保留 最后size个字符。\n     *\n     * @param num  数字对象\n     * @param size 字符串指定长度\n     * @return 返回数字的字符串格式，该字符串为指定长度。\n     */\n    public static String padl(final Number num, final int size) {\n        return padl(num.toString(), size, '0');\n    }\n\n    /**\n     * 字符串左补齐。如果原始字符串s长度大于size，则只保留最后size个字符。\n     *\n     * @param s    原始字符串\n     * @param size 字符串指定长度\n     * @param c    用于补齐的字符\n     * @return 返回指定长度的字符串，由原字符串左补齐或截取得到。\n     */\n    public static String padl(final String s, final int size, final char c) {\n        final StringBuilder sb = new StringBuilder(size);\n        if (s != null) {\n            final int len = s.length();\n            if (s.length() <= size) {\n                for (int i = size - len; i > 0; i--) {\n                    sb.append(c);\n                }\n                sb.append(s);\n            } else {\n                return s.substring(len - size, len);\n            }\n        } else {\n            for (int i = size; i > 0; i--) {\n                sb.append(c);\n            }\n        }\n        return sb.toString();\n    }\n\n    /**\n     * 切分字符串(分隔符默认逗号)\n     *\n     * @param str 被切分的字符串\n     * @return 分割后的数据列表\n     */\n    public static List<String> splitList(String str) {\n        return splitTo(str, Convert::toStr);\n    }\n\n    /**\n     * 切分字符串\n     *\n     * @param str       被切分的字符串\n     * @param separator 分隔符\n     * @return 分割后的数据列表\n     */\n    public static List<String> splitList(String str, String separator) {\n        return splitTo(str, separator, Convert::toStr);\n    }\n\n    /**\n     * 切分字符串自定义转换(分隔符默认逗号)\n     *\n     * @param str    被切分的字符串\n     * @param mapper 自定义转换\n     * @return 分割后的数据列表\n     */\n    public static <T> List<T> splitTo(String str, Function<? super Object, T> mapper) {\n        return splitTo(str, SEPARATOR, mapper);\n    }\n\n    /**\n     * 切分字符串自定义转换\n     *\n     * @param str       被切分的字符串\n     * @param separator 分隔符\n     * @param mapper    自定义转换\n     * @return 分割后的数据列表\n     */\n    public static <T> List<T> splitTo(String str, String separator, Function<? super Object, T> mapper) {\n        if (isBlank(str)) {\n            return new ArrayList<>(0);\n        }\n        return StrUtil.split(str, separator)\n            .stream()\n            .filter(Objects::nonNull)\n            .map(mapper)\n            .collect(Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/Threads.java",
    "content": "package top.flya.common.utils;\n\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.concurrent.*;\n\n/**\n * 线程相关工具类.\n *\n * @author ruoyi\n */\n@Slf4j\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class Threads {\n\n    /**\n     * sleep等待,单位为毫秒\n     */\n    public static void sleep(long milliseconds) {\n        try {\n            Thread.sleep(milliseconds);\n        } catch (InterruptedException e) {\n            return;\n        }\n    }\n\n    /**\n     * 停止线程池\n     * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.\n     * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数.\n     * 如果仍然超時，則強制退出.\n     * 另对在shutdown时线程本身被调用中断做了处理.\n     */\n    public static void shutdownAndAwaitTermination(ExecutorService pool) {\n        if (pool != null && !pool.isShutdown()) {\n            pool.shutdown();\n            try {\n                if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {\n                    pool.shutdownNow();\n                    if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {\n                        log.info(\"Pool did not terminate\");\n                    }\n                }\n            } catch (InterruptedException ie) {\n                pool.shutdownNow();\n                Thread.currentThread().interrupt();\n            }\n        }\n    }\n\n    /**\n     * 打印线程异常信息\n     */\n    public static void printException(Runnable r, Throwable t) {\n        if (t == null && r instanceof Future<?>) {\n            try {\n                Future<?> future = (Future<?>) r;\n                if (future.isDone()) {\n                    future.get();\n                }\n            } catch (CancellationException ce) {\n                t = ce;\n            } catch (ExecutionException ee) {\n                t = ee.getCause();\n            } catch (InterruptedException ie) {\n                Thread.currentThread().interrupt();\n            }\n        }\n        if (t != null) {\n            log.error(t.getMessage(), t);\n        }\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/TreeBuildUtils.java",
    "content": "package top.flya.common.utils;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNodeConfig;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport cn.hutool.core.lang.tree.parser.NodeParser;\nimport top.flya.common.utils.reflect.ReflectUtils;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n/**\n * 扩展 hutool TreeUtil 封装系统树构建\n *\n * @author Lion Li\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class TreeBuildUtils extends TreeUtil {\n\n    /**\n     * 根据前端定制差异化字段\n     */\n    public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey(\"label\");\n\n    public static <T, K> List<Tree<K>> build(List<T> list, NodeParser<T, K> nodeParser) {\n        if (CollUtil.isEmpty(list)) {\n            return null;\n        }\n        K k = ReflectUtils.invokeGetter(list.get(0), \"parentId\");\n        return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/ValidatorUtils.java",
    "content": "package top.flya.common.utils;\n\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.ConstraintViolation;\nimport javax.validation.ConstraintViolationException;\nimport javax.validation.Validator;\nimport java.util.Set;\n\n/**\n * Validator 校验框架工具\n *\n * @author Lion Li\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class ValidatorUtils {\n\n    private static final Validator VALID = SpringUtils.getBean(Validator.class);\n\n    public static <T> void validate(T object, Class<?>... groups) {\n        Set<ConstraintViolation<T>> validate = VALID.validate(object, groups);\n        if (!validate.isEmpty()) {\n            throw new ConstraintViolationException(\"参数校验异常\", validate);\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/email/MailUtils.java",
    "content": "package top.flya.common.utils.email;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.io.IoUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.CharUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.mail.*;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\n\nimport javax.mail.Authenticator;\nimport javax.mail.Session;\nimport java.io.File;\nimport java.io.InputStream;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n\n/**\n * 邮件工具类\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class MailUtils {\n\n    private static final MailAccount ACCOUNT = SpringUtils.getBean(MailAccount.class);\n\n    /**\n     * 获取邮件发送实例\n     */\n    public static MailAccount getMailAccount() {\n        return ACCOUNT;\n    }\n\n    /**\n     * 获取邮件发送实例 (自定义发送人以及授权码)\n     *\n     * @param user 发送人\n     * @param pass 授权码\n     */\n    public static MailAccount getMailAccount(String from, String user, String pass) {\n        ACCOUNT.setFrom(StringUtils.blankToDefault(from, ACCOUNT.getFrom()));\n        ACCOUNT.setUser(StringUtils.blankToDefault(user, ACCOUNT.getUser()));\n        ACCOUNT.setPass(StringUtils.blankToDefault(pass, ACCOUNT.getPass()));\n        return ACCOUNT;\n    }\n\n    /**\n     * 使用配置文件中设置的账户发送文本邮件，发送给单个或多个收件人<br>\n     * 多个收件人可以使用逗号“,”分隔，也可以通过分号“;”分隔\n     *\n     * @param to      收件人\n     * @param subject 标题\n     * @param content 正文\n     * @param files   附件列表\n     * @return message-id\n     * @since 3.2.0\n     */\n    public static String sendText(String to, String subject, String content, File... files) {\n        return send(to, subject, content, false, files);\n    }\n\n    /**\n     * 使用配置文件中设置的账户发送HTML邮件，发送给单个或多个收件人<br>\n     * 多个收件人可以使用逗号“,”分隔，也可以通过分号“;”分隔\n     *\n     * @param to      收件人\n     * @param subject 标题\n     * @param content 正文\n     * @param files   附件列表\n     * @return message-id\n     * @since 3.2.0\n     */\n    public static String sendHtml(String to, String subject, String content, File... files) {\n        return send(to, subject, content, true, files);\n    }\n\n    /**\n     * 使用配置文件中设置的账户发送邮件，发送单个或多个收件人<br>\n     * 多个收件人可以使用逗号“,”分隔，也可以通过分号“;”分隔\n     *\n     * @param to      收件人\n     * @param subject 标题\n     * @param content 正文\n     * @param isHtml  是否为HTML\n     * @param files   附件列表\n     * @return message-id\n     */\n    public static String send(String to, String subject, String content, boolean isHtml, File... files) {\n        return send(splitAddress(to), subject, content, isHtml, files);\n    }\n\n    /**\n     * 使用配置文件中设置的账户发送邮件，发送单个或多个收件人<br>\n     * 多个收件人、抄送人、密送人可以使用逗号“,”分隔，也可以通过分号“;”分隔\n     *\n     * @param to      收件人，可以使用逗号“,”分隔，也可以通过分号“;”分隔\n     * @param cc      抄送人，可以使用逗号“,”分隔，也可以通过分号“;”分隔\n     * @param bcc     密送人，可以使用逗号“,”分隔，也可以通过分号“;”分隔\n     * @param subject 标题\n     * @param content 正文\n     * @param isHtml  是否为HTML\n     * @param files   附件列表\n     * @return message-id\n     * @since 4.0.3\n     */\n    public static String send(String to, String cc, String bcc, String subject, String content, boolean isHtml, File... files) {\n        return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, isHtml, files);\n    }\n\n    /**\n     * 使用配置文件中设置的账户发送文本邮件，发送给多人\n     *\n     * @param tos     收件人列表\n     * @param subject 标题\n     * @param content 正文\n     * @param files   附件列表\n     * @return message-id\n     */\n    public static String sendText(Collection<String> tos, String subject, String content, File... files) {\n        return send(tos, subject, content, false, files);\n    }\n\n    /**\n     * 使用配置文件中设置的账户发送HTML邮件，发送给多人\n     *\n     * @param tos     收件人列表\n     * @param subject 标题\n     * @param content 正文\n     * @param files   附件列表\n     * @return message-id\n     * @since 3.2.0\n     */\n    public static String sendHtml(Collection<String> tos, String subject, String content, File... files) {\n        return send(tos, subject, content, true, files);\n    }\n\n    /**\n     * 使用配置文件中设置的账户发送邮件，发送给多人\n     *\n     * @param tos     收件人列表\n     * @param subject 标题\n     * @param content 正文\n     * @param isHtml  是否为HTML\n     * @param files   附件列表\n     * @return message-id\n     */\n    public static String send(Collection<String> tos, String subject, String content, boolean isHtml, File... files) {\n        return send(tos, null, null, subject, content, isHtml, files);\n    }\n\n    /**\n     * 使用配置文件中设置的账户发送邮件，发送给多人\n     *\n     * @param tos     收件人列表\n     * @param ccs     抄送人列表，可以为null或空\n     * @param bccs    密送人列表，可以为null或空\n     * @param subject 标题\n     * @param content 正文\n     * @param isHtml  是否为HTML\n     * @param files   附件列表\n     * @return message-id\n     * @since 4.0.3\n     */\n    public static String send(Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, boolean isHtml, File... files) {\n        return send(getMailAccount(), true, tos, ccs, bccs, subject, content, null, isHtml, files);\n    }\n\n    // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount\n\n    /**\n     * 发送邮件给多人\n     *\n     * @param mailAccount 邮件认证对象\n     * @param to          收件人，多个收件人逗号或者分号隔开\n     * @param subject     标题\n     * @param content     正文\n     * @param isHtml      是否为HTML格式\n     * @param files       附件列表\n     * @return message-id\n     * @since 3.2.0\n     */\n    public static String send(MailAccount mailAccount, String to, String subject, String content, boolean isHtml, File... files) {\n        return send(mailAccount, splitAddress(to), subject, content, isHtml, files);\n    }\n\n    /**\n     * 发送邮件给多人\n     *\n     * @param mailAccount 邮件帐户信息\n     * @param tos         收件人列表\n     * @param subject     标题\n     * @param content     正文\n     * @param isHtml      是否为HTML格式\n     * @param files       附件列表\n     * @return message-id\n     */\n    public static String send(MailAccount mailAccount, Collection<String> tos, String subject, String content, boolean isHtml, File... files) {\n        return send(mailAccount, tos, null, null, subject, content, isHtml, files);\n    }\n\n    /**\n     * 发送邮件给多人\n     *\n     * @param mailAccount 邮件帐户信息\n     * @param tos         收件人列表\n     * @param ccs         抄送人列表，可以为null或空\n     * @param bccs        密送人列表，可以为null或空\n     * @param subject     标题\n     * @param content     正文\n     * @param isHtml      是否为HTML格式\n     * @param files       附件列表\n     * @return message-id\n     * @since 4.0.3\n     */\n    public static String send(MailAccount mailAccount, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, boolean isHtml, File... files) {\n        return send(mailAccount, false, tos, ccs, bccs, subject, content, null, isHtml, files);\n    }\n\n    /**\n     * 使用配置文件中设置的账户发送HTML邮件，发送给单个或多个收件人<br>\n     * 多个收件人可以使用逗号“,”分隔，也可以通过分号“;”分隔\n     *\n     * @param to       收件人\n     * @param subject  标题\n     * @param content  正文\n     * @param imageMap 图片与占位符，占位符格式为cid:$IMAGE_PLACEHOLDER\n     * @param files    附件列表\n     * @return message-id\n     * @since 3.2.0\n     */\n    public static String sendHtml(String to, String subject, String content, Map<String, InputStream> imageMap, File... files) {\n        return send(to, subject, content, imageMap, true, files);\n    }\n\n    /**\n     * 使用配置文件中设置的账户发送邮件，发送单个或多个收件人<br>\n     * 多个收件人可以使用逗号“,”分隔，也可以通过分号“;”分隔\n     *\n     * @param to       收件人\n     * @param subject  标题\n     * @param content  正文\n     * @param imageMap 图片与占位符，占位符格式为cid:$IMAGE_PLACEHOLDER\n     * @param isHtml   是否为HTML\n     * @param files    附件列表\n     * @return message-id\n     */\n    public static String send(String to, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {\n        return send(splitAddress(to), subject, content, imageMap, isHtml, files);\n    }\n\n    /**\n     * 使用配置文件中设置的账户发送邮件，发送单个或多个收件人<br>\n     * 多个收件人、抄送人、密送人可以使用逗号“,”分隔，也可以通过分号“;”分隔\n     *\n     * @param to       收件人，可以使用逗号“,”分隔，也可以通过分号“;”分隔\n     * @param cc       抄送人，可以使用逗号“,”分隔，也可以通过分号“;”分隔\n     * @param bcc      密送人，可以使用逗号“,”分隔，也可以通过分号“;”分隔\n     * @param subject  标题\n     * @param content  正文\n     * @param imageMap 图片与占位符，占位符格式为cid:$IMAGE_PLACEHOLDER\n     * @param isHtml   是否为HTML\n     * @param files    附件列表\n     * @return message-id\n     * @since 4.0.3\n     */\n    public static String send(String to, String cc, String bcc, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {\n        return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, imageMap, isHtml, files);\n    }\n\n    /**\n     * 使用配置文件中设置的账户发送HTML邮件，发送给多人\n     *\n     * @param tos      收件人列表\n     * @param subject  标题\n     * @param content  正文\n     * @param imageMap 图片与占位符，占位符格式为cid:$IMAGE_PLACEHOLDER\n     * @param files    附件列表\n     * @return message-id\n     * @since 3.2.0\n     */\n    public static String sendHtml(Collection<String> tos, String subject, String content, Map<String, InputStream> imageMap, File... files) {\n        return send(tos, subject, content, imageMap, true, files);\n    }\n\n    /**\n     * 使用配置文件中设置的账户发送邮件，发送给多人\n     *\n     * @param tos      收件人列表\n     * @param subject  标题\n     * @param content  正文\n     * @param imageMap 图片与占位符，占位符格式为cid:$IMAGE_PLACEHOLDER\n     * @param isHtml   是否为HTML\n     * @param files    附件列表\n     * @return message-id\n     */\n    public static String send(Collection<String> tos, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {\n        return send(tos, null, null, subject, content, imageMap, isHtml, files);\n    }\n\n    /**\n     * 使用配置文件中设置的账户发送邮件，发送给多人\n     *\n     * @param tos      收件人列表\n     * @param ccs      抄送人列表，可以为null或空\n     * @param bccs     密送人列表，可以为null或空\n     * @param subject  标题\n     * @param content  正文\n     * @param imageMap 图片与占位符，占位符格式为cid:$IMAGE_PLACEHOLDER\n     * @param isHtml   是否为HTML\n     * @param files    附件列表\n     * @return message-id\n     * @since 4.0.3\n     */\n    public static String send(Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {\n        return send(getMailAccount(), true, tos, ccs, bccs, subject, content, imageMap, isHtml, files);\n    }\n\n    // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount\n\n    /**\n     * 发送邮件给多人\n     *\n     * @param mailAccount 邮件认证对象\n     * @param to          收件人，多个收件人逗号或者分号隔开\n     * @param subject     标题\n     * @param content     正文\n     * @param imageMap    图片与占位符，占位符格式为cid:$IMAGE_PLACEHOLDER\n     * @param isHtml      是否为HTML格式\n     * @param files       附件列表\n     * @return message-id\n     * @since 3.2.0\n     */\n    public static String send(MailAccount mailAccount, String to, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {\n        return send(mailAccount, splitAddress(to), subject, content, imageMap, isHtml, files);\n    }\n\n    /**\n     * 发送邮件给多人\n     *\n     * @param mailAccount 邮件帐户信息\n     * @param tos         收件人列表\n     * @param subject     标题\n     * @param content     正文\n     * @param imageMap    图片与占位符，占位符格式为cid:$IMAGE_PLACEHOLDER\n     * @param isHtml      是否为HTML格式\n     * @param files       附件列表\n     * @return message-id\n     * @since 4.6.3\n     */\n    public static String send(MailAccount mailAccount, Collection<String> tos, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {\n        return send(mailAccount, tos, null, null, subject, content, imageMap, isHtml, files);\n    }\n\n    /**\n     * 发送邮件给多人\n     *\n     * @param mailAccount 邮件帐户信息\n     * @param tos         收件人列表\n     * @param ccs         抄送人列表，可以为null或空\n     * @param bccs        密送人列表，可以为null或空\n     * @param subject     标题\n     * @param content     正文\n     * @param imageMap    图片与占位符，占位符格式为cid:$IMAGE_PLACEHOLDER\n     * @param isHtml      是否为HTML格式\n     * @param files       附件列表\n     * @return message-id\n     * @since 4.6.3\n     */\n    public static String send(MailAccount mailAccount, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, Map<String, InputStream> imageMap,\n                              boolean isHtml, File... files) {\n        return send(mailAccount, false, tos, ccs, bccs, subject, content, imageMap, isHtml, files);\n    }\n\n    /**\n     * 根据配置文件，获取邮件客户端会话\n     *\n     * @param mailAccount 邮件账户配置\n     * @param isSingleton 是否单例（全局共享会话）\n     * @return {@link Session}\n     * @since 5.5.7\n     */\n    public static Session getSession(MailAccount mailAccount, boolean isSingleton) {\n        Authenticator authenticator = null;\n        if (mailAccount.isAuth()) {\n            authenticator = new UserPassAuthenticator(mailAccount.getUser(), mailAccount.getPass());\n        }\n\n        return isSingleton ? Session.getDefaultInstance(mailAccount.getSmtpProps(), authenticator) //\n            : Session.getInstance(mailAccount.getSmtpProps(), authenticator);\n    }\n\n    // ------------------------------------------------------------------------------------------------------------------------ Private method start\n\n    /**\n     * 发送邮件给多人\n     *\n     * @param mailAccount      邮件帐户信息\n     * @param useGlobalSession 是否全局共享Session\n     * @param tos              收件人列表\n     * @param ccs              抄送人列表，可以为null或空\n     * @param bccs             密送人列表，可以为null或空\n     * @param subject          标题\n     * @param content          正文\n     * @param imageMap         图片与占位符，占位符格式为cid:${cid}\n     * @param isHtml           是否为HTML格式\n     * @param files            附件列表\n     * @return message-id\n     * @since 4.6.3\n     */\n    private static String send(MailAccount mailAccount, boolean useGlobalSession, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content,\n                               Map<String, InputStream> imageMap, boolean isHtml, File... files) {\n        final Mail mail = Mail.create(mailAccount).setUseGlobalSession(useGlobalSession);\n\n        // 可选抄送人\n        if (CollUtil.isNotEmpty(ccs)) {\n            mail.setCcs(ccs.toArray(new String[0]));\n        }\n        // 可选密送人\n        if (CollUtil.isNotEmpty(bccs)) {\n            mail.setBccs(bccs.toArray(new String[0]));\n        }\n\n        mail.setTos(tos.toArray(new String[0]));\n        mail.setTitle(subject);\n        mail.setContent(content);\n        mail.setHtml(isHtml);\n        mail.setFiles(files);\n\n        // 图片\n        if (MapUtil.isNotEmpty(imageMap)) {\n            for (Map.Entry<String, InputStream> entry : imageMap.entrySet()) {\n                mail.addImage(entry.getKey(), entry.getValue());\n                // 关闭流\n                IoUtil.close(entry.getValue());\n            }\n        }\n\n        return mail.send();\n    }\n\n    /**\n     * 将多个联系人转为列表，分隔符为逗号或者分号\n     *\n     * @param addresses 多个联系人，如果为空返回null\n     * @return 联系人列表\n     */\n    private static List<String> splitAddress(String addresses) {\n        if (StrUtil.isBlank(addresses)) {\n            return null;\n        }\n\n        List<String> result;\n        if (StrUtil.contains(addresses, CharUtil.COMMA)) {\n            result = StrUtil.splitTrim(addresses, CharUtil.COMMA);\n        } else if (StrUtil.contains(addresses, ';')) {\n            result = StrUtil.splitTrim(addresses, ';');\n        } else {\n            result = CollUtil.newArrayList(addresses);\n        }\n        return result;\n    }\n    // ------------------------------------------------------------------------------------------------------------------------ Private method end\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/file/FileUtils.java",
    "content": "package top.flya.common.utils.file;\n\nimport cn.hutool.core.io.FileUtil;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * 文件处理工具类\n *\n * @author Lion Li\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class FileUtils extends FileUtil {\n\n    /**\n     * 下载文件名重新编码\n     *\n     * @param response     响应对象\n     * @param realFileName 真实文件名\n     */\n    public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {\n        String percentEncodedFileName = percentEncode(realFileName);\n\n        StringBuilder contentDispositionValue = new StringBuilder();\n        contentDispositionValue.append(\"attachment; filename=\")\n            .append(percentEncodedFileName)\n            .append(\";\")\n            .append(\"filename*=\")\n            .append(\"utf-8''\")\n            .append(percentEncodedFileName);\n\n        response.addHeader(\"Access-Control-Expose-Headers\", \"Content-Disposition,download-filename\");\n        response.setHeader(\"Content-disposition\", contentDispositionValue.toString());\n        response.setHeader(\"download-filename\", percentEncodedFileName);\n    }\n\n    /**\n     * 百分号编码工具方法\n     *\n     * @param s 需要百分号编码的字符串\n     * @return 百分号编码后的字符串\n     */\n    public static String percentEncode(String s) throws UnsupportedEncodingException {\n        String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());\n        return encode.replaceAll(\"\\\\+\", \"%20\");\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/file/MimeTypeUtils.java",
    "content": "package top.flya.common.utils.file;\n\n/**\n * 媒体类型工具类\n *\n * @author ruoyi\n */\npublic class MimeTypeUtils {\n    public static final String IMAGE_PNG = \"image/png\";\n\n    public static final String IMAGE_JPG = \"image/jpg\";\n\n    public static final String IMAGE_JPEG = \"image/jpeg\";\n\n    public static final String IMAGE_BMP = \"image/bmp\";\n\n    public static final String IMAGE_GIF = \"image/gif\";\n\n    public static final String[] IMAGE_EXTENSION = {\"bmp\", \"gif\", \"jpg\", \"jpeg\", \"png\"};\n\n    public static final String[] FLASH_EXTENSION = {\"swf\", \"flv\"};\n\n    public static final String[] MEDIA_EXTENSION = {\"swf\", \"flv\", \"mp3\", \"wav\", \"wma\", \"wmv\", \"mid\", \"avi\", \"mpg\",\n        \"asf\", \"rm\", \"rmvb\"};\n\n    public static final String[] VIDEO_EXTENSION = {\"mp4\", \"avi\", \"rmvb\"};\n\n    public static final String[] DEFAULT_ALLOWED_EXTENSION = {\n        // 图片\n        \"bmp\", \"gif\", \"jpg\", \"jpeg\", \"png\",\n        // word excel powerpoint\n        \"doc\", \"docx\", \"xls\", \"xlsx\", \"ppt\", \"pptx\", \"html\", \"htm\", \"txt\",\n        // 压缩文件\n        \"rar\", \"zip\", \"gz\", \"bz2\",\n        // 视频格式\n        \"mp4\", \"avi\", \"rmvb\",\n        // pdf\n        \"pdf\"};\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/ip/AddressUtils.java",
    "content": "package top.flya.common.utils.ip;\n\nimport cn.hutool.core.net.NetUtil;\nimport cn.hutool.http.HtmlUtil;\nimport top.flya.common.utils.StringUtils;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * 获取地址类\n *\n * @author Lion Li\n */\n@Slf4j\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class AddressUtils {\n\n    // 未知地址\n    public static final String UNKNOWN = \"XX XX\";\n\n    public static String getRealAddressByIP(String ip) {\n        if (StringUtils.isBlank(ip)) {\n            return UNKNOWN;\n        }\n        // 内网不查询\n        ip = \"0:0:0:0:0:0:0:1\".equals(ip) ? \"127.0.0.1\" : HtmlUtil.cleanHtmlTag(ip);\n        if (NetUtil.isInnerIP(ip)) {\n            return \"内网IP\";\n        }\n        return RegionUtils.getCityInfo(ip);\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/ip/RegionUtils.java",
    "content": "package top.flya.common.utils.ip;\n\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.core.io.resource.ClassPathResource;\nimport cn.hutool.core.util.ObjectUtil;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.utils.file.FileUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.lionsoul.ip2region.xdb.Searcher;\n\nimport java.io.File;\n\n/**\n * 根据ip地址定位工具类，离线方式\n * 参考地址：<a href=\"https://gitee.com/lionsoul/ip2region/tree/master/binding/java\">集成 ip2region 实现离线IP地址定位库</a>\n *\n * @author lishuyan\n */\n@Slf4j\npublic class RegionUtils {\n\n    private static final Searcher SEARCHER;\n\n    static {\n        String fileName = \"/ip2region.xdb\";\n        File existFile = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);\n        if (!FileUtils.exist(existFile)) {\n            ClassPathResource fileStream = new ClassPathResource(fileName);\n            if (ObjectUtil.isEmpty(fileStream.getStream())) {\n                throw new ServiceException(\"RegionUtils初始化失败，原因：IP地址库数据不存在！\");\n            }\n            FileUtils.writeFromStream(fileStream.getStream(), existFile);\n        }\n\n        String dbPath = existFile.getPath();\n\n        // 1、从 dbPath 加载整个 xdb 到内存。\n        byte[] cBuff;\n        try {\n            cBuff = Searcher.loadContentFromFile(dbPath);\n        } catch (Exception e) {\n            throw new ServiceException(\"RegionUtils初始化失败，原因：从ip2region.xdb文件加载内容失败！\" + e.getMessage());\n        }\n        // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。\n        try {\n            SEARCHER = Searcher.newWithBuffer(cBuff);\n        } catch (Exception e) {\n            throw new ServiceException(\"RegionUtils初始化失败，原因：\" + e.getMessage());\n        }\n    }\n\n    /**\n     * 根据IP地址离线获取城市\n     */\n    public static String getCityInfo(String ip) {\n        try {\n            ip = ip.trim();\n            // 3、执行查询\n            String region = SEARCHER.search(ip);\n            return region.replace(\"0|\", \"\").replace(\"|0\", \"\");\n        } catch (Exception e) {\n            log.error(\"IP地址离线获取城市异常 {}\", ip);\n            return \"未知\";\n        }\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/poi/ExcelUtil.java",
    "content": "package top.flya.common.utils.poi;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.io.resource.ClassPathResource;\nimport cn.hutool.core.util.IdUtil;\nimport com.alibaba.excel.EasyExcel;\nimport com.alibaba.excel.ExcelWriter;\nimport com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;\nimport com.alibaba.excel.write.metadata.WriteSheet;\nimport com.alibaba.excel.write.metadata.fill.FillConfig;\nimport com.alibaba.excel.write.metadata.fill.FillWrapper;\nimport com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;\nimport top.flya.common.convert.ExcelBigNumberConvert;\nimport top.flya.common.excel.CellMergeStrategy;\nimport top.flya.common.excel.DefaultExcelListener;\nimport top.flya.common.excel.ExcelListener;\nimport top.flya.common.excel.ExcelResult;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.file.FileUtils;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\n\nimport javax.servlet.ServletOutputStream;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Excel相关处理\n *\n * @author Lion Li\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class ExcelUtil {\n\n    /**\n     * 同步导入(适用于小数据量)\n     *\n     * @param is 输入流\n     * @return 转换后集合\n     */\n    public static <T> List<T> importExcel(InputStream is, Class<T> clazz) {\n        return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync();\n    }\n\n\n    /**\n     * 使用校验监听器 异步导入 同步返回\n     *\n     * @param is         输入流\n     * @param clazz      对象类型\n     * @param isValidate 是否 Validator 检验 默认为是\n     * @return 转换后集合\n     */\n    public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, boolean isValidate) {\n        DefaultExcelListener<T> listener = new DefaultExcelListener<>(isValidate);\n        EasyExcel.read(is, clazz, listener).sheet().doRead();\n        return listener.getExcelResult();\n    }\n\n    /**\n     * 使用自定义监听器 异步导入 自定义返回\n     *\n     * @param is       输入流\n     * @param clazz    对象类型\n     * @param listener 自定义监听器\n     * @return 转换后集合\n     */\n    public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, ExcelListener<T> listener) {\n        EasyExcel.read(is, clazz, listener).sheet().doRead();\n        return listener.getExcelResult();\n    }\n\n    /**\n     * 导出excel\n     *\n     * @param list      导出数据集合\n     * @param sheetName 工作表的名称\n     * @param clazz     实体类\n     * @param response  响应体\n     */\n    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) {\n        try {\n            resetResponse(sheetName, response);\n            ServletOutputStream os = response.getOutputStream();\n            exportExcel(list, sheetName, clazz, false, os);\n        } catch (IOException e) {\n            throw new RuntimeException(\"导出Excel异常\");\n        }\n    }\n\n    /**\n     * 导出excel\n     *\n     * @param list      导出数据集合\n     * @param sheetName 工作表的名称\n     * @param clazz     实体类\n     * @param merge     是否合并单元格\n     * @param response  响应体\n     */\n    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, HttpServletResponse response) {\n        try {\n            resetResponse(sheetName, response);\n            ServletOutputStream os = response.getOutputStream();\n            exportExcel(list, sheetName, clazz, merge, os);\n        } catch (IOException e) {\n            throw new RuntimeException(\"导出Excel异常\");\n        }\n    }\n\n    /**\n     * 导出excel\n     *\n     * @param list      导出数据集合\n     * @param sheetName 工作表的名称\n     * @param clazz     实体类\n     * @param os        输出流\n     */\n    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, OutputStream os) {\n        exportExcel(list, sheetName, clazz, false, os);\n    }\n\n    /**\n     * 导出excel\n     *\n     * @param list      导出数据集合\n     * @param sheetName 工作表的名称\n     * @param clazz     实体类\n     * @param merge     是否合并单元格\n     * @param os        输出流\n     */\n    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, OutputStream os) {\n        ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)\n            .autoCloseStream(false)\n            // 自动适配\n            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())\n            // 大数值自动转换 防止失真\n            .registerConverter(new ExcelBigNumberConvert())\n            .sheet(sheetName);\n        if (merge) {\n            // 合并处理器\n            builder.registerWriteHandler(new CellMergeStrategy(list, true));\n        }\n        builder.doWrite(list);\n    }\n\n    /**\n     * 单表多数据模板导出 模板格式为 {.属性}\n     *\n     * @param filename     文件名\n     * @param templatePath 模板路径 resource 目录下的路径包括模板文件名\n     *                     例如: excel/temp.xlsx\n     *                     重点: 模板文件必须放置到启动类对应的 resource 目录下\n     * @param data         模板需要的数据\n     * @param response     响应体\n     */\n    public static void exportTemplate(List<Object> data, String filename, String templatePath, HttpServletResponse response) {\n        try {\n            resetResponse(filename, response);\n            ServletOutputStream os = response.getOutputStream();\n            exportTemplate(data, templatePath, os);\n        } catch (IOException e) {\n            throw new RuntimeException(\"导出Excel异常\");\n        }\n    }\n\n    /**\n     * 单表多数据模板导出 模板格式为 {.属性}\n     *\n     * @param templatePath 模板路径 resource 目录下的路径包括模板文件名\n     *                     例如: excel/temp.xlsx\n     *                     重点: 模板文件必须放置到启动类对应的 resource 目录下\n     * @param data         模板需要的数据\n     * @param os           输出流\n     */\n    public static void exportTemplate(List<Object> data, String templatePath, OutputStream os) {\n        ClassPathResource templateResource = new ClassPathResource(templatePath);\n        ExcelWriter excelWriter = EasyExcel.write(os)\n            .withTemplate(templateResource.getStream())\n            .autoCloseStream(false)\n            // 大数值自动转换 防止失真\n            .registerConverter(new ExcelBigNumberConvert())\n            .build();\n        WriteSheet writeSheet = EasyExcel.writerSheet().build();\n        if (CollUtil.isEmpty(data)) {\n            throw new IllegalArgumentException(\"数据为空\");\n        }\n        // 单表多数据导出 模板格式为 {.属性}\n        for (Object d : data) {\n            excelWriter.fill(d, writeSheet);\n        }\n        excelWriter.finish();\n    }\n\n    /**\n     * 多表多数据模板导出 模板格式为 {key.属性}\n     *\n     * @param filename     文件名\n     * @param templatePath 模板路径 resource 目录下的路径包括模板文件名\n     *                     例如: excel/temp.xlsx\n     *                     重点: 模板文件必须放置到启动类对应的 resource 目录下\n     * @param data         模板需要的数据\n     * @param response     响应体\n     */\n    public static void exportTemplateMultiList(Map<String, Object> data, String filename, String templatePath, HttpServletResponse response) {\n        try {\n            resetResponse(filename, response);\n            ServletOutputStream os = response.getOutputStream();\n            exportTemplateMultiList(data, templatePath, os);\n        } catch (IOException e) {\n            throw new RuntimeException(\"导出Excel异常\");\n        }\n    }\n\n    /**\n     * 多表多数据模板导出 模板格式为 {key.属性}\n     *\n     * @param templatePath 模板路径 resource 目录下的路径包括模板文件名\n     *                     例如: excel/temp.xlsx\n     *                     重点: 模板文件必须放置到启动类对应的 resource 目录下\n     * @param data         模板需要的数据\n     * @param os           输出流\n     */\n    public static void exportTemplateMultiList(Map<String, Object> data, String templatePath, OutputStream os) {\n        ClassPathResource templateResource = new ClassPathResource(templatePath);\n        ExcelWriter excelWriter = EasyExcel.write(os)\n            .withTemplate(templateResource.getStream())\n            .autoCloseStream(false)\n            // 大数值自动转换 防止失真\n            .registerConverter(new ExcelBigNumberConvert())\n            .build();\n        WriteSheet writeSheet = EasyExcel.writerSheet().build();\n        if (CollUtil.isEmpty(data)) {\n            throw new IllegalArgumentException(\"数据为空\");\n        }\n        for (Map.Entry<String, Object> map : data.entrySet()) {\n            // 设置列表后续还有数据\n            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();\n            if (map.getValue() instanceof Collection) {\n                // 多表导出必须使用 FillWrapper\n                excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet);\n            } else {\n                excelWriter.fill(map.getValue(), writeSheet);\n            }\n        }\n        excelWriter.finish();\n    }\n\n    /**\n     * 重置响应体\n     */\n    private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException {\n        String filename = encodingFilename(sheetName);\n        FileUtils.setAttachmentResponseHeader(response, filename);\n        response.setContentType(\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8\");\n    }\n\n    /**\n     * 解析导出值 0=男,1=女,2=未知\n     *\n     * @param propertyValue 参数值\n     * @param converterExp  翻译注解\n     * @param separator     分隔符\n     * @return 解析后值\n     */\n    public static String convertByExp(String propertyValue, String converterExp, String separator) {\n        StringBuilder propertyString = new StringBuilder();\n        String[] convertSource = converterExp.split(StringUtils.SEPARATOR);\n        for (String item : convertSource) {\n            String[] itemArray = item.split(\"=\");\n            if (StringUtils.containsAny(propertyValue, separator)) {\n                for (String value : propertyValue.split(separator)) {\n                    if (itemArray[0].equals(value)) {\n                        propertyString.append(itemArray[1] + separator);\n                        break;\n                    }\n                }\n            } else {\n                if (itemArray[0].equals(propertyValue)) {\n                    return itemArray[1];\n                }\n            }\n        }\n        return StringUtils.stripEnd(propertyString.toString(), separator);\n    }\n\n    /**\n     * 反向解析值 男=0,女=1,未知=2\n     *\n     * @param propertyValue 参数值\n     * @param converterExp  翻译注解\n     * @param separator     分隔符\n     * @return 解析后值\n     */\n    public static String reverseByExp(String propertyValue, String converterExp, String separator) {\n        StringBuilder propertyString = new StringBuilder();\n        String[] convertSource = converterExp.split(StringUtils.SEPARATOR);\n        for (String item : convertSource) {\n            String[] itemArray = item.split(\"=\");\n            if (StringUtils.containsAny(propertyValue, separator)) {\n                for (String value : propertyValue.split(separator)) {\n                    if (itemArray[1].equals(value)) {\n                        propertyString.append(itemArray[0] + separator);\n                        break;\n                    }\n                }\n            } else {\n                if (itemArray[1].equals(propertyValue)) {\n                    return itemArray[0];\n                }\n            }\n        }\n        return StringUtils.stripEnd(propertyString.toString(), separator);\n    }\n\n    /**\n     * 编码文件名\n     */\n    public static String encodingFilename(String filename) {\n        return IdUtil.fastSimpleUUID() + \"_\" + filename + \".xlsx\";\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/redis/CacheUtils.java",
    "content": "package top.flya.common.utils.redis;\n\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport org.redisson.api.RMap;\nimport org.springframework.cache.Cache;\nimport org.springframework.cache.CacheManager;\n\nimport java.util.Set;\n\n/**\n * 缓存操作工具类 {@link }\n *\n * @author Michelle.Chung\n * @date 2022/8/13\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\n@SuppressWarnings(value = {\"unchecked\"})\npublic class CacheUtils {\n\n    private static final CacheManager CACHE_MANAGER = SpringUtils.getBean(CacheManager.class);\n\n    /**\n     * 获取缓存组内所有的KEY\n     *\n     * @param cacheNames 缓存组名称\n     */\n    public static Set<Object> keys(String cacheNames) {\n        RMap<Object, Object> rmap = (RMap<Object, Object>) CACHE_MANAGER.getCache(cacheNames).getNativeCache();\n        return rmap.keySet();\n    }\n\n    /**\n     * 获取缓存值\n     *\n     * @param cacheNames 缓存组名称\n     * @param key        缓存key\n     */\n    public static <T> T get(String cacheNames, Object key) {\n        Cache.ValueWrapper wrapper = CACHE_MANAGER.getCache(cacheNames).get(key);\n        return wrapper != null ? (T) wrapper.get() : null;\n    }\n\n    /**\n     * 保存缓存值\n     *\n     * @param cacheNames 缓存组名称\n     * @param key        缓存key\n     * @param value      缓存值\n     */\n    public static void put(String cacheNames, Object key, Object value) {\n        CACHE_MANAGER.getCache(cacheNames).put(key, value);\n    }\n\n    /**\n     * 删除缓存值\n     *\n     * @param cacheNames 缓存组名称\n     * @param key        缓存key\n     */\n    public static void evict(String cacheNames, Object key) {\n        CACHE_MANAGER.getCache(cacheNames).evict(key);\n    }\n\n    /**\n     * 清空缓存值\n     *\n     * @param cacheNames 缓存组名称\n     */\n    public static void clear(String cacheNames) {\n        CACHE_MANAGER.getCache(cacheNames).clear();\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/redis/QueueUtils.java",
    "content": "package top.flya.common.utils.redis;\n\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport org.redisson.api.*;\n\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Consumer;\n\n/**\n * 分布式队列工具\n * 轻量级队列 重量级数据量 请使用 MQ\n * 要求 redis 5.X 以上\n *\n * @author Lion Li\n * @version 3.6.0 新增\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class QueueUtils {\n\n    private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);\n\n\n    /**\n     * 获取客户端实例\n     */\n    public static RedissonClient getClient() {\n        return CLIENT;\n    }\n\n    /**\n     * 添加普通队列数据\n     *\n     * @param queueName 队列名\n     * @param data      数据\n     */\n    public static <T> boolean addQueueObject(String queueName, T data) {\n        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);\n        return queue.offer(data);\n    }\n\n    /**\n     * 通用获取一个队列数据 没有数据返回 null(不支持延迟队列)\n     *\n     * @param queueName 队列名\n     */\n    public static <T> T getQueueObject(String queueName) {\n        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);\n        return queue.poll();\n    }\n\n    /**\n     * 通用删除队列数据(不支持延迟队列)\n     */\n    public static <T> boolean removeQueueObject(String queueName, T data) {\n        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);\n        return queue.remove(data);\n    }\n\n    /**\n     * 通用销毁队列 所有阻塞监听 报错(不支持延迟队列)\n     */\n    public static <T> boolean destroyQueue(String queueName) {\n        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);\n        return queue.delete();\n    }\n\n    /**\n     * 添加延迟队列数据 默认毫秒\n     *\n     * @param queueName 队列名\n     * @param data      数据\n     * @param time      延迟时间\n     */\n    public static <T> void addDelayedQueueObject(String queueName, T data, long time) {\n        addDelayedQueueObject(queueName, data, time, TimeUnit.MILLISECONDS);\n    }\n\n    /**\n     * 添加延迟队列数据\n     *\n     * @param queueName 队列名\n     * @param data      数据\n     * @param time      延迟时间\n     * @param timeUnit  单位\n     */\n    public static <T> void addDelayedQueueObject(String queueName, T data, long time, TimeUnit timeUnit) {\n        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);\n        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);\n        delayedQueue.offer(data, time, timeUnit);\n    }\n\n    /**\n     * 获取一个延迟队列数据 没有数据返回 null\n     *\n     * @param queueName 队列名\n     */\n    public static <T> T getDelayedQueueObject(String queueName) {\n        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);\n        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);\n        return delayedQueue.poll();\n    }\n\n    /**\n     * 删除延迟队列数据\n     */\n    public static <T> boolean removeDelayedQueueObject(String queueName, T data) {\n        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);\n        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);\n        return delayedQueue.remove(data);\n    }\n\n    /**\n     * 销毁延迟队列 所有阻塞监听 报错\n     */\n    public static <T> void destroyDelayedQueue(String queueName) {\n        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);\n        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);\n        delayedQueue.destroy();\n    }\n\n    /**\n     * 添加优先队列数据\n     *\n     * @param queueName 队列名\n     * @param data      数据\n     */\n    public static <T> boolean addPriorityQueueObject(String queueName, T data) {\n        RPriorityBlockingQueue<T> priorityBlockingQueue = CLIENT.getPriorityBlockingQueue(queueName);\n        return priorityBlockingQueue.offer(data);\n    }\n\n    /**\n     * 尝试设置 有界队列 容量 用于限制数量\n     *\n     * @param queueName 队列名\n     * @param capacity  容量\n     */\n    public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity) {\n        RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);\n        return boundedBlockingQueue.trySetCapacity(capacity);\n    }\n\n    /**\n     * 尝试设置 有界队列 容量 用于限制数量\n     *\n     * @param queueName 队列名\n     * @param capacity  容量\n     * @param destroy   已存在是否销毁\n     */\n    public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity, boolean destroy) {\n        RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);\n        if (boundedBlockingQueue.isExists() && destroy) {\n            destroyQueue(queueName);\n        }\n        return boundedBlockingQueue.trySetCapacity(capacity);\n    }\n\n    /**\n     * 添加有界队列数据\n     *\n     * @param queueName 队列名\n     * @param data      数据\n     * @return 添加成功 true 已达到界限 false\n     */\n    public static <T> boolean addBoundedQueueObject(String queueName, T data) {\n        RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);\n        return boundedBlockingQueue.offer(data);\n    }\n\n    /**\n     * 订阅阻塞队列(可订阅所有实现类 例如: 延迟 优先 有界 等)\n     */\n    public static <T> void subscribeBlockingQueue(String queueName, Consumer<T> consumer) {\n        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);\n        queue.subscribeOnElements(consumer);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/redis/RedisUtils.java",
    "content": "package top.flya.common.utils.redis;\n\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport org.redisson.api.*;\n\nimport java.time.Duration;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * redis 工具类\n *\n * @author Lion Li\n * @version 3.1.0 新增\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\n@SuppressWarnings(value = {\"unchecked\", \"rawtypes\"})\npublic class RedisUtils {\n\n    private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);\n\n    /**\n     * 限流\n     *\n     * @param key          限流key\n     * @param rateType     限流类型\n     * @param rate         速率\n     * @param rateInterval 速率间隔\n     * @return -1 表示失败\n     */\n    public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval) {\n        RRateLimiter rateLimiter = CLIENT.getRateLimiter(key);\n        rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS);\n        if (rateLimiter.tryAcquire()) {\n            return rateLimiter.availablePermits();\n        } else {\n            return -1L;\n        }\n    }\n\n    /**\n     * 获取客户端实例\n     */\n    public static RedissonClient getClient() {\n        return CLIENT;\n    }\n\n    /**\n     * 发布通道消息\n     *\n     * @param channelKey 通道key\n     * @param msg        发送数据\n     * @param consumer   自定义处理\n     */\n    public static <T> void publish(String channelKey, T msg, Consumer<T> consumer) {\n        RTopic topic = CLIENT.getTopic(channelKey);\n        topic.publish(msg);\n        consumer.accept(msg);\n    }\n\n    public static <T> void publish(String channelKey, T msg) {\n        RTopic topic = CLIENT.getTopic(channelKey);\n        topic.publish(msg);\n    }\n\n    /**\n     * 订阅通道接收消息\n     *\n     * @param channelKey 通道key\n     * @param clazz      消息类型\n     * @param consumer   自定义处理\n     */\n    public static <T> void subscribe(String channelKey, Class<T> clazz, Consumer<T> consumer) {\n        RTopic topic = CLIENT.getTopic(channelKey);\n        topic.addListener(clazz, (channel, msg) -> consumer.accept(msg));\n    }\n\n    /**\n     * 缓存基本的对象，Integer、String、实体类等\n     *\n     * @param key   缓存的键值\n     * @param value 缓存的值\n     */\n    public static <T> void setCacheObject(final String key, final T value) {\n        setCacheObject(key, value, false);\n    }\n\n    /**\n     * 缓存基本的对象，保留当前对象 TTL 有效期\n     *\n     * @param key       缓存的键值\n     * @param value     缓存的值\n     * @param isSaveTtl 是否保留TTL有效期(例如: set之前ttl剩余90 set之后还是为90)\n     * @since Redis 6.X 以上使用 setAndKeepTTL 兼容 5.X 方案\n     */\n    public static <T> void setCacheObject(final String key, final T value, final boolean isSaveTtl) {\n        RBucket<T> bucket = CLIENT.getBucket(key);\n        if (isSaveTtl) {\n            try {\n                bucket.setAndKeepTTL(value);\n            } catch (Exception e) {\n                long timeToLive = bucket.remainTimeToLive();\n                setCacheObject(key, value, Duration.ofMillis(timeToLive));\n            }\n        } else {\n            bucket.set(value);\n        }\n    }\n\n    /**\n     * 缓存基本的对象，Integer、String、实体类等\n     *\n     * @param key      缓存的键值\n     * @param value    缓存的值\n     * @param duration 时间\n     */\n    public static <T> void setCacheObject(final String key, final T value, final Duration duration) {\n        RBatch batch = CLIENT.createBatch();\n        RBucketAsync<T> bucket = batch.getBucket(key);\n        bucket.setAsync(value);\n        bucket.expireAsync(duration);\n        batch.execute();\n    }\n\n    /**\n     * 注册对象监听器\n     * <p>\n     * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置\n     *\n     * @param key      缓存的键值\n     * @param listener 监听器配置\n     */\n    public static <T> void addObjectListener(final String key, final ObjectListener listener) {\n        RBucket<T> result = CLIENT.getBucket(key);\n        result.addListener(listener);\n    }\n\n    /**\n     * 设置有效时间\n     *\n     * @param key     Redis键\n     * @param timeout 超时时间\n     * @return true=设置成功；false=设置失败\n     */\n    public static boolean expire(final String key, final long timeout) {\n        return expire(key, Duration.ofSeconds(timeout));\n    }\n\n    /**\n     * 设置有效时间\n     *\n     * @param key      Redis键\n     * @param duration 超时时间\n     * @return true=设置成功；false=设置失败\n     */\n    public static boolean expire(final String key, final Duration duration) {\n        RBucket rBucket = CLIENT.getBucket(key);\n        return rBucket.expire(duration);\n    }\n\n    /**\n     * 获得缓存的基本对象。\n     *\n     * @param key 缓存键值\n     * @return 缓存键值对应的数据\n     */\n    public static <T> T getCacheObject(final String key) {\n        RBucket<T> rBucket = CLIENT.getBucket(key);\n        return rBucket.get();\n    }\n\n    /**\n     * 获得key剩余存活时间\n     *\n     * @param key 缓存键值\n     * @return 剩余存活时间\n     */\n    public static <T> long getTimeToLive(final String key) {\n        RBucket<T> rBucket = CLIENT.getBucket(key);\n        return rBucket.remainTimeToLive();\n    }\n\n    /**\n     * 删除单个对象\n     *\n     * @param key 缓存的键值\n     */\n    public static boolean deleteObject(final String key) {\n        return CLIENT.getBucket(key).delete();\n    }\n\n    /**\n     * 删除集合对象\n     *\n     * @param collection 多个对象\n     */\n    public static void deleteObject(final Collection collection) {\n        RBatch batch = CLIENT.createBatch();\n        collection.forEach(t -> {\n            batch.getBucket(t.toString()).deleteAsync();\n        });\n        batch.execute();\n    }\n\n    /**\n     * 检查缓存对象是否存在\n     *\n     * @param key 缓存的键值\n     */\n    public static boolean isExistsObject(final String key) {\n        return CLIENT.getBucket(key).isExists();\n    }\n\n    /**\n     * 缓存List数据\n     *\n     * @param key      缓存的键值\n     * @param dataList 待缓存的List数据\n     * @return 缓存的对象\n     */\n    public static <T> boolean setCacheList(final String key, final List<T> dataList) {\n        RList<T> rList = CLIENT.getList(key);\n        return rList.addAll(dataList);\n    }\n\n    /**\n     * 注册List监听器\n     * <p>\n     * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置\n     *\n     * @param key      缓存的键值\n     * @param listener 监听器配置\n     */\n    public static <T> void addListListener(final String key, final ObjectListener listener) {\n        RList<T> rList = CLIENT.getList(key);\n        rList.addListener(listener);\n    }\n\n    /**\n     * 获得缓存的list对象\n     *\n     * @param key 缓存的键值\n     * @return 缓存键值对应的数据\n     */\n    public static <T> List<T> getCacheList(final String key) {\n        RList<T> rList = CLIENT.getList(key);\n        return rList.readAll();\n    }\n\n    /**\n     * 缓存Set\n     *\n     * @param key     缓存键值\n     * @param dataSet 缓存的数据\n     * @return 缓存数据的对象\n     */\n    public static <T> boolean setCacheSet(final String key, final Set<T> dataSet) {\n        RSet<T> rSet = CLIENT.getSet(key);\n        return rSet.addAll(dataSet);\n    }\n\n    /**\n     * 注册Set监听器\n     * <p>\n     * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置\n     *\n     * @param key      缓存的键值\n     * @param listener 监听器配置\n     */\n    public static <T> void addSetListener(final String key, final ObjectListener listener) {\n        RSet<T> rSet = CLIENT.getSet(key);\n        rSet.addListener(listener);\n    }\n\n    /**\n     * 获得缓存的set\n     *\n     * @param key 缓存的key\n     * @return set对象\n     */\n    public static <T> Set<T> getCacheSet(final String key) {\n        RSet<T> rSet = CLIENT.getSet(key);\n        return rSet.readAll();\n    }\n\n    /**\n     * 缓存Map\n     *\n     * @param key     缓存的键值\n     * @param dataMap 缓存的数据\n     */\n    public static <T> void setCacheMap(final String key, final Map<String, T> dataMap) {\n        if (dataMap != null) {\n            RMap<String, T> rMap = CLIENT.getMap(key);\n            rMap.putAll(dataMap);\n        }\n    }\n\n    /**\n     * 注册Map监听器\n     * <p>\n     * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置\n     *\n     * @param key      缓存的键值\n     * @param listener 监听器配置\n     */\n    public static <T> void addMapListener(final String key, final ObjectListener listener) {\n        RMap<String, T> rMap = CLIENT.getMap(key);\n        rMap.addListener(listener);\n    }\n\n    /**\n     * 获得缓存的Map\n     *\n     * @param key 缓存的键值\n     * @return map对象\n     */\n    public static <T> Map<String, T> getCacheMap(final String key) {\n        RMap<String, T> rMap = CLIENT.getMap(key);\n        return rMap.getAll(rMap.keySet());\n    }\n\n    /**\n     * 获得缓存Map的key列表\n     *\n     * @param key 缓存的键值\n     * @return key列表\n     */\n    public static <T> Set<String> getCacheMapKeySet(final String key) {\n        RMap<String, T> rMap = CLIENT.getMap(key);\n        return rMap.keySet();\n    }\n\n    /**\n     * 往Hash中存入数据\n     *\n     * @param key   Redis键\n     * @param hKey  Hash键\n     * @param value 值\n     */\n    public static <T> void setCacheMapValue(final String key, final String hKey, final T value) {\n        RMap<String, T> rMap = CLIENT.getMap(key);\n        rMap.put(hKey, value);\n    }\n\n    /**\n     * 获取Hash中的数据\n     *\n     * @param key  Redis键\n     * @param hKey Hash键\n     * @return Hash中的对象\n     */\n    public static <T> T getCacheMapValue(final String key, final String hKey) {\n        RMap<String, T> rMap = CLIENT.getMap(key);\n        return rMap.get(hKey);\n    }\n\n    /**\n     * 删除Hash中的数据\n     *\n     * @param key  Redis键\n     * @param hKey Hash键\n     * @return Hash中的对象\n     */\n    public static <T> T delCacheMapValue(final String key, final String hKey) {\n        RMap<String, T> rMap = CLIENT.getMap(key);\n        return rMap.remove(hKey);\n    }\n\n    /**\n     * 获取多个Hash中的数据\n     *\n     * @param key   Redis键\n     * @param hKeys Hash键集合\n     * @return Hash对象集合\n     */\n    public static <K, V> Map<K, V> getMultiCacheMapValue(final String key, final Set<K> hKeys) {\n        RMap<K, V> rMap = CLIENT.getMap(key);\n        return rMap.getAll(hKeys);\n    }\n\n    /**\n     * 设置原子值\n     *\n     * @param key   Redis键\n     * @param value 值\n     */\n    public static void setAtomicValue(String key, long value) {\n        RAtomicLong atomic = CLIENT.getAtomicLong(key);\n        atomic.set(value);\n    }\n\n    /**\n     * 获取原子值\n     *\n     * @param key Redis键\n     * @return 当前值\n     */\n    public static long getAtomicValue(String key) {\n        RAtomicLong atomic = CLIENT.getAtomicLong(key);\n        return atomic.get();\n    }\n\n    /**\n     * 递增原子值\n     *\n     * @param key Redis键\n     * @return 当前值\n     */\n    public static long incrAtomicValue(String key) {\n        RAtomicLong atomic = CLIENT.getAtomicLong(key);\n        return atomic.incrementAndGet();\n    }\n\n    /**\n     * 递减原子值\n     *\n     * @param key Redis键\n     * @return 当前值\n     */\n    public static long decrAtomicValue(String key) {\n        RAtomicLong atomic = CLIENT.getAtomicLong(key);\n        return atomic.decrementAndGet();\n    }\n\n    /**\n     * 获得缓存的基本对象列表\n     *\n     * @param pattern 字符串前缀\n     * @return 对象列表\n     */\n    public static Collection<String> keys(final String pattern) {\n        Stream<String> stream = CLIENT.getKeys().getKeysStreamByPattern(pattern);\n        return stream.collect(Collectors.toList());\n    }\n\n    /**\n     * 删除缓存的基本对象列表\n     *\n     * @param pattern 字符串前缀\n     */\n    public static void deleteKeys(final String pattern) {\n        CLIENT.getKeys().deleteByPattern(pattern);\n    }\n\n    /**\n     * 检查redis中是否存在key\n     *\n     * @param key 键\n     */\n    public static Boolean hasKey(String key) {\n        RKeys rKeys = CLIENT.getKeys();\n        return rKeys.countExists(key) > 0;\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/reflect/ReflectUtils.java",
    "content": "package top.flya.common.utils.reflect;\n\nimport cn.hutool.core.util.ReflectUtil;\nimport top.flya.common.utils.StringUtils;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\n\nimport java.lang.reflect.Method;\n\n/**\n * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.\n *\n * @author Lion Li\n */\n@SuppressWarnings(\"rawtypes\")\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class ReflectUtils extends ReflectUtil {\n\n    private static final String SETTER_PREFIX = \"set\";\n\n    private static final String GETTER_PREFIX = \"get\";\n\n    /**\n     * 调用Getter方法.\n     * 支持多级，如：对象名.对象名.方法\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <E> E invokeGetter(Object obj, String propertyName) {\n        Object object = obj;\n        for (String name : StringUtils.split(propertyName, \".\")) {\n            String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);\n            object = invoke(object, getterMethodName);\n        }\n        return (E) object;\n    }\n\n    /**\n     * 调用Setter方法, 仅匹配方法名。\n     * 支持多级，如：对象名.对象名.方法\n     */\n    public static <E> void invokeSetter(Object obj, String propertyName, E value) {\n        Object object = obj;\n        String[] names = StringUtils.split(propertyName, \".\");\n        for (int i = 0; i < names.length; i++) {\n            if (i < names.length - 1) {\n                String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);\n                object = invoke(object, getterMethodName);\n            } else {\n                String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);\n                Method method = getMethodByName(object.getClass(), setterMethodName);\n                invoke(object, method, value);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/spring/SpringUtils.java",
    "content": "package top.flya.common.utils.spring;\n\nimport cn.hutool.extra.spring.SpringUtil;\nimport org.springframework.aop.framework.AopContext;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.stereotype.Component;\n\n/**\n * spring工具类\n *\n * @author Lion Li\n */\n@Component\npublic final class SpringUtils extends SpringUtil {\n\n    /**\n     * 如果BeanFactory包含一个与所给名称匹配的bean定义，则返回true\n     *\n     * @param name\n     * @return boolean\n     */\n    public static boolean containsBean(String name) {\n        return getBeanFactory().containsBean(name);\n    }\n\n    /**\n     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。\n     * 如果与给定名字相应的bean定义没有被找到，将会抛出一个异常（NoSuchBeanDefinitionException）\n     *\n     * @param name\n     * @return boolean\n     */\n    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {\n        return getBeanFactory().isSingleton(name);\n    }\n\n    /**\n     * @param name\n     * @return Class 注册对象的类型\n     */\n    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {\n        return getBeanFactory().getType(name);\n    }\n\n    /**\n     * 如果给定的bean名字在bean定义中有别名，则返回这些别名\n     *\n     * @param name\n     */\n    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {\n        return getBeanFactory().getAliases(name);\n    }\n\n    /**\n     * 获取aop代理对象\n     *\n     * @param invoker\n     * @return\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T getAopProxy(T invoker) {\n        return (T) AopContext.currentProxy();\n    }\n\n\n    /**\n     * 获取spring上下文\n     */\n    public static ApplicationContext context() {\n        return getApplicationContext();\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/utils/sql/SqlUtil.java",
    "content": "package top.flya.common.utils.sql;\n\nimport top.flya.common.exception.UtilException;\nimport top.flya.common.utils.StringUtils;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\n\n/**\n * sql操作工具类\n *\n * @author ruoyi\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class SqlUtil {\n\n    /**\n     * 定义常用的 sql关键字\n     */\n    public static final String SQL_REGEX = \"select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare \";\n\n    /**\n     * 仅支持字母、数字、下划线、空格、逗号、小数点（支持多个字段排序）\n     */\n    public static final String SQL_PATTERN = \"[a-zA-Z0-9_\\\\ \\\\,\\\\.]+\";\n\n    /**\n     * 检查字符，防止注入绕过\n     */\n    public static String escapeOrderBySql(String value) {\n        //&& !isValidOrderBySql(value)\n//        if (StringUtils.isNotEmpty(value) ) {\n//            throw new UtilException(\"参数不符合规范，不能进行查询\");\n//        }\n        return value;\n    }\n\n    /**\n     * 验证 order by 语法是否符合规范\n     */\n    public static boolean isValidOrderBySql(String value) {\n        return value.matches(SQL_PATTERN);\n    }\n\n    /**\n     * SQL关键字检查\n     */\n    public static void filterKeyword(String value) {\n        if (StringUtils.isEmpty(value)) {\n            return;\n        }\n        String[] sqlKeywords = StringUtils.split(SQL_REGEX, \"\\\\|\");\n        for (String sqlKeyword : sqlKeywords) {\n            if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) {\n                throw new UtilException(\"参数存在SQL注入风险\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/xss/Xss.java",
    "content": "package top.flya.common.xss;\n\nimport javax.validation.Constraint;\nimport javax.validation.Payload;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 自定义xss校验注解\n *\n * @author Lion Li\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER})\n@Constraint(validatedBy = {XssValidator.class})\npublic @interface Xss {\n\n    String message() default \"不允许任何脚本运行\";\n\n    Class<?>[] groups() default {};\n\n    Class<? extends Payload>[] payload() default {};\n\n}\n"
  },
  {
    "path": "ruoyi-common/src/main/java/top/flya/common/xss/XssValidator.java",
    "content": "package top.flya.common.xss;\n\nimport cn.hutool.core.util.ReUtil;\nimport cn.hutool.http.HtmlUtil;\nimport lombok.extern.slf4j.Slf4j;\n\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\n\n/**\n * 自定义xss校验注解实现\n *\n * @author Lion Li\n */\n@Slf4j\npublic class XssValidator implements ConstraintValidator<Xss, String> {\n\n    @Override\n    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {\n        log.info(\"xss validator: {}\", value);\n        return !ReUtil.contains(HtmlUtil.RE_HTML_MARK, value);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>ruoyi-vue-plus</artifactId>\n        <groupId>com.ruoyi</groupId>\n        <version>4.7.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>ruoyi-extend</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>ruoyi-monitor-admin</module>\n        <module>ruoyi-xxl-job-admin</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-monitor-admin/Dockerfile",
    "content": "FROM anapsix/alpine-java:8_server-jre_unlimited\n\nMAINTAINER Lion Li\n\nRUN mkdir -p /ruoyi/monitor/logs\n\nWORKDIR /ruoyi/monitor\n\nEXPOSE 9090\n\nADD ./target/ruoyi-monitor-admin.jar ./app.jar\n\nENTRYPOINT [\"java\", \"-Djava.security.egd=file:/dev/./urandom\", \"-jar\", \"app.jar\"]\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-monitor-admin/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>ruoyi-extend</artifactId>\n        <groupId>com.ruoyi</groupId>\n        <version>4.7.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>jar</packaging>\n    <artifactId>ruoyi-monitor-admin</artifactId>\n\n    <dependencies>\n        <!-- SpringWeb模块 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- spring security 安全认证 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-server</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-client</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring-boot.version}</version>\n                <configuration>\n                    <fork>true</fork> <!-- 如果没有该配置，devtools不会生效 -->\n                </configuration>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-monitor-admin/src/main/java/top/flya/monitor/admin/MonitorAdminApplication.java",
    "content": "package top.flya.monitor.admin;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * Admin 监控启动程序\n *\n * @author Lion Li\n */\n@SpringBootApplication\npublic class MonitorAdminApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(MonitorAdminApplication.class, args);\n        System.out.println(\"Admin 监控启动成功\");\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-monitor-admin/src/main/java/top/flya/monitor/admin/config/AdminServerConfig.java",
    "content": "package top.flya.monitor.admin.config;\n\nimport de.codecentric.boot.admin.server.config.EnableAdminServer;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;\nimport org.springframework.boot.task.TaskExecutorBuilder;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;\n\nimport java.util.concurrent.Executor;\n\n/**\n * springboot-admin server配置类\n *\n * @author Lion Li\n */\n@Configuration\n@EnableAdminServer\npublic class AdminServerConfig {\n\n    @Lazy\n    @Bean(name = TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)\n    @ConditionalOnMissingBean(Executor.class)\n    public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {\n        return builder.build();\n    }\n\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-monitor-admin/src/main/java/top/flya/monitor/admin/config/SecurityConfig.java",
    "content": "package top.flya.monitor.admin.config;\n\nimport de.codecentric.boot.admin.server.config.AdminServerProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;\n\n/**\n * admin 监控 安全配置\n *\n * @author Lion Li\n */\n@EnableWebSecurity\npublic class SecurityConfig {\n\n    private final String adminContextPath;\n\n    public SecurityConfig(AdminServerProperties adminServerProperties) {\n        this.adminContextPath = adminServerProperties.getContextPath();\n    }\n\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {\n        SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();\n        successHandler.setTargetUrlParameter(\"redirectTo\");\n        successHandler.setDefaultTargetUrl(adminContextPath + \"/\");\n\n        return httpSecurity\n                .headers().frameOptions().disable()\n                .and().authorizeRequests()\n                .antMatchers(adminContextPath + \"/assets/**\"\n                    , adminContextPath + \"/login\"\n                    , \"/actuator\"\n                    , \"/actuator/**\"\n                ).permitAll()\n                .anyRequest().authenticated()\n                .and()\n                .formLogin().loginPage(adminContextPath + \"/login\")\n                .successHandler(successHandler).and()\n                .logout().logoutUrl(adminContextPath + \"/logout\")\n                .and()\n                .httpBasic().and()\n                .csrf()\n                .disable()\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-monitor-admin/src/main/java/top/flya/monitor/admin/notifier/CustomNotifier.java",
    "content": "package top.flya.monitor.admin.notifier;\n\nimport de.codecentric.boot.admin.server.domain.entities.Instance;\nimport de.codecentric.boot.admin.server.domain.entities.InstanceRepository;\nimport de.codecentric.boot.admin.server.domain.events.InstanceEvent;\nimport de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;\nimport de.codecentric.boot.admin.server.notify.AbstractEventNotifier;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\nimport reactor.core.publisher.Mono;\n\n/**\n * 自定义事件通知处理\n *\n * @author Lion Li\n */\n@Slf4j\n@Component\npublic class CustomNotifier extends AbstractEventNotifier {\n\n    protected CustomNotifier(InstanceRepository repository) {\n        super(repository);\n    }\n\n    @Override\n    @SuppressWarnings(\"all\")\n    protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {\n        return Mono.fromRunnable(() -> {\n            // 实例状态改变事件\n            if (event instanceof InstanceStatusChangedEvent) {\n                String registName = instance.getRegistration().getName();\n                String instanceId = event.getInstance().getValue();\n                String status = ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus();\n                log.info(\"Instance Status Change: [{}],[{}],[{}]\", registName, instanceId, status);\n            }\n\n        });\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml",
    "content": "server:\n  port: 9090\nspring:\n  application:\n    name: ruoyi-monitor-admin\n  profiles:\n    active: @profiles.active@\n\nlogging:\n  config: classpath:logback-plus.xml\n\n--- # 监控中心服务端配置\nspring:\n  security:\n    user:\n      name: ruoyi\n      password: 123456\n  boot:\n    admin:\n      ui:\n        title: RuoYi-Vue-Plus服务监控中心\n      context-path: /admin\n\n--- # Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: '*'\n  endpoint:\n    health:\n      show-details: ALWAYS\n    logfile:\n      external-file: ./logs/ruoyi-monitor-admin.log\n\n--- # 监控配置\nspring.boot.admin.client:\n  # 增加客户端开关\n  enabled: false\n  # 设置 Spring Boot Admin Server 地址\n  url: http://localhost:9090/admin\n  instance:\n    service-host-type: IP\n  username: ruoyi\n  password: 123456\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-monitor-admin/src/main/resources/banner.txt",
    "content": "Application Version: ${ruoyi-vue-plus.version}\nSpring Boot Version: ${spring-boot.version}\n __  __             _ _                              _           _\n|  \\/  |           (_) |                    /\\      | |         (_)\n| \\  / | ___  _ __  _| |_ ___  _ __ ______ /  \\   __| |_ __ ___  _ _ __\n| |\\/| |/ _ \\| '_ \\| | __/ _ \\| '__|______/ /\\ \\ / _` | '_ ` _ \\| | '_ \\\n| |  | | (_) | | | | | || (_) | |        / ____ \\ (_| | | | | | | | | | |\n|_|  |_|\\___/|_| |_|_|\\__\\___/|_|       /_/    \\_\\__,_|_| |_| |_|_|_| |_|\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback-plus.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration debug=\"false\" scan=\"true\" scanPeriod=\"1 seconds\">\n\n    <contextName>logback</contextName>\n    <property name=\"log.path\" value=\"./logs/ruoyi-monitor-admin\"/>\n    <property name=\"console.log.pattern\"\n              value=\"%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n\"/>\n    <property name=\"log.pattern\" value=\"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"/>\n\n    <appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>${console.log.pattern}</pattern>\n            <charset>utf-8</charset>\n        </encoder>\n    </appender>\n\n    <appender name=\"file\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${log.path}.log</file>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n            <fileNamePattern>${log.path}.%d{yyyy-MM-dd}.log</fileNamePattern>\n            <!-- 日志最大的历史 60天 -->\n            <maxHistory>60</maxHistory>\n        </rollingPolicy>\n        <encoder>\n            <pattern>${log.pattern}</pattern>\n        </encoder>\n    </appender>\n\n    <root level=\"info\">\n        <appender-ref ref=\"console\"/>\n        <appender-ref ref=\"file\"/>\n    </root>\n\n</configuration>"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/Dockerfile",
    "content": "FROM anapsix/alpine-java:8_server-jre_unlimited\n\nMAINTAINER Lion Li\n\nRUN mkdir -p /ruoyi/xxljob/logs\n\nWORKDIR /ruoyi/xxljob\n\nENV TZ=PRC\nRUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone\n\nEXPOSE 9100\n\nADD ./target/ruoyi-xxl-job-admin.jar ./app.jar\n\nENTRYPOINT [\"java\", \"-Djava.security.egd=file:/dev/./urandom\", \"-jar\", \"app.jar\"]\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <artifactId>ruoyi-extend</artifactId>\n        <groupId>com.ruoyi</groupId>\n        <version>4.7.0</version>\n    </parent>\n    <artifactId>ruoyi-xxl-job-admin</artifactId>\n    <packaging>jar</packaging>\n\n    <properties>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring-boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n\n        <!-- starter-web：spring-webmvc + autoconfigure + logback + yaml + tomcat -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <!-- starter-test：junit + spring-test + mockito -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <!-- freemarker-starter -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-freemarker</artifactId>\n        </dependency>\n\n        <!-- mail-starter -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-mail</artifactId>\n        </dependency>\n\n        <!-- starter-actuator -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n        <!-- mybatis-starter：mybatis + mybatis-spring + hikari（default） -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>${spring-boot.mybatis}</version>\n        </dependency>\n        <!-- mysql -->\n        <dependency>\n            <groupId>com.mysql</groupId>\n            <artifactId>mysql-connector-j</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-client</artifactId>\n        </dependency>\n\n        <!-- xxl-job-core -->\n        <dependency>\n            <groupId>com.xuxueli</groupId>\n            <artifactId>xxl-job-core</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring-boot.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/XxlJobAdminApplication.java",
    "content": "package com.xxl.job.admin;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * @author xuxueli 2018-10-28 00:38:13\n */\n@SpringBootApplication\npublic class XxlJobAdminApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(XxlJobAdminApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java",
    "content": "package com.xxl.job.admin.controller;\n\nimport com.xxl.job.admin.controller.annotation.PermissionLimit;\nimport com.xxl.job.admin.service.LoginService;\nimport com.xxl.job.admin.service.XxlJobService;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport org.springframework.beans.propertyeditors.CustomDateEditor;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.WebDataBinder;\nimport org.springframework.web.bind.annotation.InitBinder;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.view.RedirectView;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Map;\n\n/**\n * index controller\n *\n * @author xuxueli 2015-12-19 16:13:16\n */\n@Controller\npublic class IndexController {\n\n    @Resource\n    private XxlJobService xxlJobService;\n    @Resource\n    private LoginService loginService;\n\n\n    @RequestMapping(\"/\")\n    public String index(Model model) {\n\n        Map<String, Object> dashboardMap = xxlJobService.dashboardInfo();\n        model.addAllAttributes(dashboardMap);\n\n        return \"index\";\n    }\n\n    @RequestMapping(\"/chartInfo\")\n    @ResponseBody\n    public ReturnT<Map<String, Object>> chartInfo(Date startDate, Date endDate) {\n        ReturnT<Map<String, Object>> chartInfo = xxlJobService.chartInfo(startDate, endDate);\n        return chartInfo;\n    }\n\n    @RequestMapping(\"/toLogin\")\n    @PermissionLimit(limit = false)\n    public ModelAndView toLogin(HttpServletRequest request, HttpServletResponse response, ModelAndView modelAndView) {\n        if (loginService.ifLogin(request, response) != null) {\n            modelAndView.setView(new RedirectView(\"/\", true, false));\n            return modelAndView;\n        }\n        return new ModelAndView(\"login\");\n    }\n\n    @RequestMapping(value = \"login\", method = RequestMethod.POST)\n    @ResponseBody\n    @PermissionLimit(limit = false)\n    public ReturnT<String> loginDo(HttpServletRequest request, HttpServletResponse response, String userName, String password, String ifRemember) {\n        boolean ifRem = (ifRemember != null && ifRemember.trim().length() > 0 && \"on\".equals(ifRemember)) ? true : false;\n        return loginService.login(request, response, userName, password, ifRem);\n    }\n\n    @RequestMapping(value = \"logout\", method = RequestMethod.POST)\n    @ResponseBody\n    @PermissionLimit(limit = false)\n    public ReturnT<String> logout(HttpServletRequest request, HttpServletResponse response) {\n        return loginService.logout(request, response);\n    }\n\n    @RequestMapping(\"/help\")\n    public String help() {\n\n        /*if (!PermissionInterceptor.ifLogin(request)) {\n            return \"redirect:/toLogin\";\n        }*/\n\n        return \"help\";\n    }\n\n    @InitBinder\n    public void initBinder(WebDataBinder binder) {\n        SimpleDateFormat dateFormat = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        dateFormat.setLenient(false);\n        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobApiController.java",
    "content": "package com.xxl.job.admin.controller;\n\nimport com.xxl.job.admin.controller.annotation.PermissionLimit;\nimport com.xxl.job.admin.core.conf.XxlJobAdminConfig;\nimport com.xxl.job.core.biz.AdminBiz;\nimport com.xxl.job.core.biz.model.HandleCallbackParam;\nimport com.xxl.job.core.biz.model.RegistryParam;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.util.GsonTool;\nimport com.xxl.job.core.util.XxlJobRemotingUtil;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.List;\n\n/**\n * Created by xuxueli on 17/5/10.\n */\n@Controller\n@RequestMapping(\"/api\")\npublic class JobApiController {\n\n    @Resource\n    private AdminBiz adminBiz;\n\n    /**\n     * api\n     *\n     * @param uri\n     * @param data\n     * @return\n     */\n    @RequestMapping(\"/{uri}\")\n    @ResponseBody\n    @PermissionLimit(limit = false)\n    public ReturnT<String> api(HttpServletRequest request, @PathVariable(\"uri\") String uri, @RequestBody(required = false) String data) {\n\n        // valid\n        if (!\"POST\".equalsIgnoreCase(request.getMethod())) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, \"invalid request, HttpMethod not support.\");\n        }\n        if (uri == null || uri.trim().length() == 0) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, \"invalid request, uri-mapping empty.\");\n        }\n        if (XxlJobAdminConfig.getAdminConfig().getAccessToken() != null\n            && XxlJobAdminConfig.getAdminConfig().getAccessToken().trim().length() > 0\n            && !XxlJobAdminConfig.getAdminConfig().getAccessToken().equals(request.getHeader(XxlJobRemotingUtil.XXL_JOB_ACCESS_TOKEN))) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, \"The access token is wrong.\");\n        }\n\n        // services mapping\n        if (\"callback\".equals(uri)) {\n            List<HandleCallbackParam> callbackParamList = GsonTool.fromJson(data, List.class, HandleCallbackParam.class);\n            return adminBiz.callback(callbackParamList);\n        } else if (\"registry\".equals(uri)) {\n            RegistryParam registryParam = GsonTool.fromJson(data, RegistryParam.class);\n            return adminBiz.registry(registryParam);\n        } else if (\"registryRemove\".equals(uri)) {\n            RegistryParam registryParam = GsonTool.fromJson(data, RegistryParam.class);\n            return adminBiz.registryRemove(registryParam);\n        } else {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, \"invalid request, uri-mapping(\" + uri + \") not found.\");\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobCodeController.java",
    "content": "package com.xxl.job.admin.controller;\n\nimport com.xxl.job.admin.core.model.XxlJobInfo;\nimport com.xxl.job.admin.core.model.XxlJobLogGlue;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport com.xxl.job.admin.dao.XxlJobInfoDao;\nimport com.xxl.job.admin.dao.XxlJobLogGlueDao;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.glue.GlueTypeEnum;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * job code controller\n *\n * @author xuxueli 2015-12-19 16:13:16\n */\n@Controller\n@RequestMapping(\"/jobcode\")\npublic class JobCodeController {\n\n    @Resource\n    private XxlJobInfoDao xxlJobInfoDao;\n    @Resource\n    private XxlJobLogGlueDao xxlJobLogGlueDao;\n\n    @RequestMapping\n    public String index(HttpServletRequest request, Model model, int jobId) {\n        XxlJobInfo jobInfo = xxlJobInfoDao.loadById(jobId);\n        List<XxlJobLogGlue> jobLogGlues = xxlJobLogGlueDao.findByJobId(jobId);\n\n        if (jobInfo == null) {\n            throw new RuntimeException(I18nUtil.getString(\"jobinfo_glue_jobid_unvalid\"));\n        }\n        if (GlueTypeEnum.BEAN == GlueTypeEnum.match(jobInfo.getGlueType())) {\n            throw new RuntimeException(I18nUtil.getString(\"jobinfo_glue_gluetype_unvalid\"));\n        }\n\n        // valid permission\n        JobInfoController.validPermission(request, jobInfo.getJobGroup());\n\n        // Glue类型-字典\n        model.addAttribute(\"GlueTypeEnum\", GlueTypeEnum.values());\n\n        model.addAttribute(\"jobInfo\", jobInfo);\n        model.addAttribute(\"jobLogGlues\", jobLogGlues);\n        return \"jobcode/jobcode.index\";\n    }\n\n    @RequestMapping(\"/save\")\n    @ResponseBody\n    public ReturnT<String> save(Model model, int id, String glueSource, String glueRemark) {\n        // valid\n        if (glueRemark == null) {\n            return new ReturnT<String>(500, (I18nUtil.getString(\"system_please_input\") + I18nUtil.getString(\"jobinfo_glue_remark\")));\n        }\n        if (glueRemark.length() < 4 || glueRemark.length() > 100) {\n            return new ReturnT<String>(500, I18nUtil.getString(\"jobinfo_glue_remark_limit\"));\n        }\n        XxlJobInfo exists_jobInfo = xxlJobInfoDao.loadById(id);\n        if (exists_jobInfo == null) {\n            return new ReturnT<String>(500, I18nUtil.getString(\"jobinfo_glue_jobid_unvalid\"));\n        }\n\n        // update new code\n        exists_jobInfo.setGlueSource(glueSource);\n        exists_jobInfo.setGlueRemark(glueRemark);\n        exists_jobInfo.setGlueUpdatetime(new Date());\n\n        exists_jobInfo.setUpdateTime(new Date());\n        xxlJobInfoDao.update(exists_jobInfo);\n\n        // log old code\n        XxlJobLogGlue xxlJobLogGlue = new XxlJobLogGlue();\n        xxlJobLogGlue.setJobId(exists_jobInfo.getId());\n        xxlJobLogGlue.setGlueType(exists_jobInfo.getGlueType());\n        xxlJobLogGlue.setGlueSource(glueSource);\n        xxlJobLogGlue.setGlueRemark(glueRemark);\n\n        xxlJobLogGlue.setAddTime(new Date());\n        xxlJobLogGlue.setUpdateTime(new Date());\n        xxlJobLogGlueDao.save(xxlJobLogGlue);\n\n        // remove code backup more than 30\n        xxlJobLogGlueDao.removeOld(exists_jobInfo.getId(), 30);\n\n        return ReturnT.SUCCESS;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java",
    "content": "package com.xxl.job.admin.controller;\n\nimport com.xxl.job.admin.controller.annotation.PermissionLimit;\nimport com.xxl.job.admin.core.model.XxlJobGroup;\nimport com.xxl.job.admin.core.model.XxlJobRegistry;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport com.xxl.job.admin.dao.XxlJobGroupDao;\nimport com.xxl.job.admin.dao.XxlJobInfoDao;\nimport com.xxl.job.admin.dao.XxlJobRegistryDao;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.enums.RegistryConfig;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.*;\n\n/**\n * job group controller\n *\n * @author xuxueli 2016-10-02 20:52:56\n */\n@Controller\n@RequestMapping(\"/jobgroup\")\npublic class JobGroupController {\n\n    @Resource\n    public XxlJobInfoDao xxlJobInfoDao;\n    @Resource\n    public XxlJobGroupDao xxlJobGroupDao;\n    @Resource\n    private XxlJobRegistryDao xxlJobRegistryDao;\n\n    @RequestMapping\n    @PermissionLimit(adminuser = true)\n    public String index(Model model) {\n        return \"jobgroup/jobgroup.index\";\n    }\n\n    @RequestMapping(\"/pageList\")\n    @ResponseBody\n    @PermissionLimit(adminuser = true)\n    public Map<String, Object> pageList(HttpServletRequest request,\n                                        @RequestParam(required = false, defaultValue = \"0\") int start,\n                                        @RequestParam(required = false, defaultValue = \"10\") int length,\n                                        String appname, String title) {\n\n        // page query\n        List<XxlJobGroup> list = xxlJobGroupDao.pageList(start, length, appname, title);\n        int list_count = xxlJobGroupDao.pageListCount(start, length, appname, title);\n\n        // package result\n        Map<String, Object> maps = new HashMap<String, Object>();\n        maps.put(\"recordsTotal\", list_count);        // 总记录数\n        maps.put(\"recordsFiltered\", list_count);    // 过滤后的总记录数\n        maps.put(\"data\", list);                    // 分页列表\n        return maps;\n    }\n\n    @RequestMapping(\"/save\")\n    @ResponseBody\n    @PermissionLimit(adminuser = true)\n    public ReturnT<String> save(XxlJobGroup xxlJobGroup) {\n\n        // valid\n        if (xxlJobGroup.getAppname() == null || xxlJobGroup.getAppname().trim().length() == 0) {\n            return new ReturnT<String>(500, (I18nUtil.getString(\"system_please_input\") + \"AppName\"));\n        }\n        if (xxlJobGroup.getAppname().length() < 4 || xxlJobGroup.getAppname().length() > 64) {\n            return new ReturnT<String>(500, I18nUtil.getString(\"jobgroup_field_appname_length\"));\n        }\n        if (xxlJobGroup.getAppname().contains(\">\") || xxlJobGroup.getAppname().contains(\"<\")) {\n            return new ReturnT<String>(500, \"AppName\" + I18nUtil.getString(\"system_unvalid\"));\n        }\n        if (xxlJobGroup.getTitle() == null || xxlJobGroup.getTitle().trim().length() == 0) {\n            return new ReturnT<String>(500, (I18nUtil.getString(\"system_please_input\") + I18nUtil.getString(\"jobgroup_field_title\")));\n        }\n        if (xxlJobGroup.getTitle().contains(\">\") || xxlJobGroup.getTitle().contains(\"<\")) {\n            return new ReturnT<String>(500, I18nUtil.getString(\"jobgroup_field_title\") + I18nUtil.getString(\"system_unvalid\"));\n        }\n        if (xxlJobGroup.getAddressType() != 0) {\n            if (xxlJobGroup.getAddressList() == null || xxlJobGroup.getAddressList().trim().length() == 0) {\n                return new ReturnT<String>(500, I18nUtil.getString(\"jobgroup_field_addressType_limit\"));\n            }\n            if (xxlJobGroup.getAddressList().contains(\">\") || xxlJobGroup.getAddressList().contains(\"<\")) {\n                return new ReturnT<String>(500, I18nUtil.getString(\"jobgroup_field_registryList\") + I18nUtil.getString(\"system_unvalid\"));\n            }\n\n            String[] addresss = xxlJobGroup.getAddressList().split(\",\");\n            for (String item : addresss) {\n                if (item == null || item.trim().length() == 0) {\n                    return new ReturnT<String>(500, I18nUtil.getString(\"jobgroup_field_registryList_unvalid\"));\n                }\n            }\n        }\n\n        // process\n        xxlJobGroup.setUpdateTime(new Date());\n\n        int ret = xxlJobGroupDao.save(xxlJobGroup);\n        return (ret > 0) ? ReturnT.SUCCESS : ReturnT.FAIL;\n    }\n\n    @RequestMapping(\"/update\")\n    @ResponseBody\n    @PermissionLimit(adminuser = true)\n    public ReturnT<String> update(XxlJobGroup xxlJobGroup) {\n        // valid\n        if (xxlJobGroup.getAppname() == null || xxlJobGroup.getAppname().trim().length() == 0) {\n            return new ReturnT<String>(500, (I18nUtil.getString(\"system_please_input\") + \"AppName\"));\n        }\n        if (xxlJobGroup.getAppname().length() < 4 || xxlJobGroup.getAppname().length() > 64) {\n            return new ReturnT<String>(500, I18nUtil.getString(\"jobgroup_field_appname_length\"));\n        }\n        if (xxlJobGroup.getTitle() == null || xxlJobGroup.getTitle().trim().length() == 0) {\n            return new ReturnT<String>(500, (I18nUtil.getString(\"system_please_input\") + I18nUtil.getString(\"jobgroup_field_title\")));\n        }\n        if (xxlJobGroup.getAddressType() == 0) {\n            // 0=自动注册\n            List<String> registryList = findRegistryByAppName(xxlJobGroup.getAppname());\n            String addressListStr = null;\n            if (registryList != null && !registryList.isEmpty()) {\n                Collections.sort(registryList);\n                addressListStr = \"\";\n                for (String item : registryList) {\n                    addressListStr += item + \",\";\n                }\n                addressListStr = addressListStr.substring(0, addressListStr.length() - 1);\n            }\n            xxlJobGroup.setAddressList(addressListStr);\n        } else {\n            // 1=手动录入\n            if (xxlJobGroup.getAddressList() == null || xxlJobGroup.getAddressList().trim().length() == 0) {\n                return new ReturnT<String>(500, I18nUtil.getString(\"jobgroup_field_addressType_limit\"));\n            }\n            String[] addresss = xxlJobGroup.getAddressList().split(\",\");\n            for (String item : addresss) {\n                if (item == null || item.trim().length() == 0) {\n                    return new ReturnT<String>(500, I18nUtil.getString(\"jobgroup_field_registryList_unvalid\"));\n                }\n            }\n        }\n\n        // process\n        xxlJobGroup.setUpdateTime(new Date());\n\n        int ret = xxlJobGroupDao.update(xxlJobGroup);\n        return (ret > 0) ? ReturnT.SUCCESS : ReturnT.FAIL;\n    }\n\n    private List<String> findRegistryByAppName(String appnameParam) {\n        HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>();\n        List<XxlJobRegistry> list = xxlJobRegistryDao.findAll(RegistryConfig.DEAD_TIMEOUT, new Date());\n        if (list != null) {\n            for (XxlJobRegistry item : list) {\n                if (RegistryConfig.RegistType.EXECUTOR.name().equals(item.getRegistryGroup())) {\n                    String appname = item.getRegistryKey();\n                    List<String> registryList = appAddressMap.get(appname);\n                    if (registryList == null) {\n                        registryList = new ArrayList<String>();\n                    }\n\n                    if (!registryList.contains(item.getRegistryValue())) {\n                        registryList.add(item.getRegistryValue());\n                    }\n                    appAddressMap.put(appname, registryList);\n                }\n            }\n        }\n        return appAddressMap.get(appnameParam);\n    }\n\n    @RequestMapping(\"/remove\")\n    @ResponseBody\n    @PermissionLimit(adminuser = true)\n    public ReturnT<String> remove(int id) {\n\n        // valid\n        int count = xxlJobInfoDao.pageListCount(0, 10, id, -1, null, null, null);\n        if (count > 0) {\n            return new ReturnT<String>(500, I18nUtil.getString(\"jobgroup_del_limit_0\"));\n        }\n\n        List<XxlJobGroup> allList = xxlJobGroupDao.findAll();\n        if (allList.size() == 1) {\n            return new ReturnT<String>(500, I18nUtil.getString(\"jobgroup_del_limit_1\"));\n        }\n\n        int ret = xxlJobGroupDao.remove(id);\n        return (ret > 0) ? ReturnT.SUCCESS : ReturnT.FAIL;\n    }\n\n    @RequestMapping(\"/loadById\")\n    @ResponseBody\n    @PermissionLimit(adminuser = true)\n    public ReturnT<XxlJobGroup> loadById(int id) {\n        XxlJobGroup jobGroup = xxlJobGroupDao.load(id);\n        return jobGroup != null ? new ReturnT<XxlJobGroup>(jobGroup) : new ReturnT<XxlJobGroup>(ReturnT.FAIL_CODE, null);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobInfoController.java",
    "content": "package com.xxl.job.admin.controller;\n\nimport com.xxl.job.admin.core.exception.XxlJobException;\nimport com.xxl.job.admin.core.model.XxlJobGroup;\nimport com.xxl.job.admin.core.model.XxlJobInfo;\nimport com.xxl.job.admin.core.model.XxlJobUser;\nimport com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;\nimport com.xxl.job.admin.core.scheduler.MisfireStrategyEnum;\nimport com.xxl.job.admin.core.scheduler.ScheduleTypeEnum;\nimport com.xxl.job.admin.core.thread.JobScheduleHelper;\nimport com.xxl.job.admin.core.thread.JobTriggerPoolHelper;\nimport com.xxl.job.admin.core.trigger.TriggerTypeEnum;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport com.xxl.job.admin.dao.XxlJobGroupDao;\nimport com.xxl.job.admin.service.LoginService;\nimport com.xxl.job.admin.service.XxlJobService;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.enums.ExecutorBlockStrategyEnum;\nimport com.xxl.job.core.glue.GlueTypeEnum;\nimport com.xxl.job.core.util.DateUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.*;\n\n/**\n * index controller\n *\n * @author xuxueli 2015-12-19 16:13:16\n */\n@Controller\n@RequestMapping(\"/jobinfo\")\npublic class JobInfoController {\n    private static Logger logger = LoggerFactory.getLogger(JobInfoController.class);\n\n    @Resource\n    private XxlJobGroupDao xxlJobGroupDao;\n    @Resource\n    private XxlJobService xxlJobService;\n\n    @RequestMapping\n    public String index(HttpServletRequest request, Model model, @RequestParam(required = false, defaultValue = \"-1\") int jobGroup) {\n\n        // 枚举-字典\n        model.addAttribute(\"ExecutorRouteStrategyEnum\", ExecutorRouteStrategyEnum.values());        // 路由策略-列表\n        model.addAttribute(\"GlueTypeEnum\", GlueTypeEnum.values());                                // Glue类型-字典\n        model.addAttribute(\"ExecutorBlockStrategyEnum\", ExecutorBlockStrategyEnum.values());        // 阻塞处理策略-字典\n        model.addAttribute(\"ScheduleTypeEnum\", ScheduleTypeEnum.values());                        // 调度类型\n        model.addAttribute(\"MisfireStrategyEnum\", MisfireStrategyEnum.values());                    // 调度过期策略\n\n        // 执行器列表\n        List<XxlJobGroup> jobGroupList_all = xxlJobGroupDao.findAll();\n\n        // filter group\n        List<XxlJobGroup> jobGroupList = filterJobGroupByRole(request, jobGroupList_all);\n        if (jobGroupList == null || jobGroupList.size() == 0) {\n            throw new XxlJobException(I18nUtil.getString(\"jobgroup_empty\"));\n        }\n\n        model.addAttribute(\"JobGroupList\", jobGroupList);\n        model.addAttribute(\"jobGroup\", jobGroup);\n\n        return \"jobinfo/jobinfo.index\";\n    }\n\n    public static List<XxlJobGroup> filterJobGroupByRole(HttpServletRequest request, List<XxlJobGroup> jobGroupList_all) {\n        List<XxlJobGroup> jobGroupList = new ArrayList<>();\n        if (jobGroupList_all != null && jobGroupList_all.size() > 0) {\n            XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);\n            if (loginUser.getRole() == 1) {\n                jobGroupList = jobGroupList_all;\n            } else {\n                List<String> groupIdStrs = new ArrayList<>();\n                if (loginUser.getPermission() != null && loginUser.getPermission().trim().length() > 0) {\n                    groupIdStrs = Arrays.asList(loginUser.getPermission().trim().split(\",\"));\n                }\n                for (XxlJobGroup groupItem : jobGroupList_all) {\n                    if (groupIdStrs.contains(String.valueOf(groupItem.getId()))) {\n                        jobGroupList.add(groupItem);\n                    }\n                }\n            }\n        }\n        return jobGroupList;\n    }\n\n    public static void validPermission(HttpServletRequest request, int jobGroup) {\n        XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);\n        if (!loginUser.validPermission(jobGroup)) {\n            throw new RuntimeException(I18nUtil.getString(\"system_permission_limit\") + \"[username=\" + loginUser.getUsername() + \"]\");\n        }\n    }\n\n    @RequestMapping(\"/pageList\")\n    @ResponseBody\n    public Map<String, Object> pageList(@RequestParam(required = false, defaultValue = \"0\") int start,\n                                        @RequestParam(required = false, defaultValue = \"10\") int length,\n                                        int jobGroup, int triggerStatus, String jobDesc, String executorHandler, String author) {\n\n        return xxlJobService.pageList(start, length, jobGroup, triggerStatus, jobDesc, executorHandler, author);\n    }\n\n    @RequestMapping(\"/add\")\n    @ResponseBody\n    public ReturnT<String> add(XxlJobInfo jobInfo) {\n        return xxlJobService.add(jobInfo);\n    }\n\n    @RequestMapping(\"/update\")\n    @ResponseBody\n    public ReturnT<String> update(XxlJobInfo jobInfo) {\n        return xxlJobService.update(jobInfo);\n    }\n\n    @RequestMapping(\"/remove\")\n    @ResponseBody\n    public ReturnT<String> remove(int id) {\n        return xxlJobService.remove(id);\n    }\n\n    @RequestMapping(\"/stop\")\n    @ResponseBody\n    public ReturnT<String> pause(int id) {\n        return xxlJobService.stop(id);\n    }\n\n    @RequestMapping(\"/start\")\n    @ResponseBody\n    public ReturnT<String> start(int id) {\n        return xxlJobService.start(id);\n    }\n\n    @RequestMapping(\"/trigger\")\n    @ResponseBody\n    //@PermissionLimit(limit = false)\n    public ReturnT<String> triggerJob(int id, String executorParam, String addressList) {\n        // force cover job param\n        if (executorParam == null) {\n            executorParam = \"\";\n        }\n\n        JobTriggerPoolHelper.trigger(id, TriggerTypeEnum.MANUAL, -1, null, executorParam, addressList);\n        return ReturnT.SUCCESS;\n    }\n\n    @RequestMapping(\"/nextTriggerTime\")\n    @ResponseBody\n    public ReturnT<List<String>> nextTriggerTime(String scheduleType, String scheduleConf) {\n\n        XxlJobInfo paramXxlJobInfo = new XxlJobInfo();\n        paramXxlJobInfo.setScheduleType(scheduleType);\n        paramXxlJobInfo.setScheduleConf(scheduleConf);\n\n        List<String> result = new ArrayList<>();\n        try {\n            Date lastTime = new Date();\n            for (int i = 0; i < 5; i++) {\n                lastTime = JobScheduleHelper.generateNextValidTime(paramXxlJobInfo, lastTime);\n                if (lastTime != null) {\n                    result.add(DateUtil.formatDateTime(lastTime));\n                } else {\n                    break;\n                }\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return new ReturnT<List<String>>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"schedule_type\") + I18nUtil.getString(\"system_unvalid\")) + e.getMessage());\n        }\n        return new ReturnT<List<String>>(result);\n\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java",
    "content": "package com.xxl.job.admin.controller;\n\nimport com.xxl.job.admin.core.exception.XxlJobException;\nimport com.xxl.job.admin.core.complete.XxlJobCompleter;\nimport com.xxl.job.admin.core.model.XxlJobGroup;\nimport com.xxl.job.admin.core.model.XxlJobInfo;\nimport com.xxl.job.admin.core.model.XxlJobLog;\nimport com.xxl.job.admin.core.scheduler.XxlJobScheduler;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport com.xxl.job.admin.dao.XxlJobGroupDao;\nimport com.xxl.job.admin.dao.XxlJobInfoDao;\nimport com.xxl.job.admin.dao.XxlJobLogDao;\nimport com.xxl.job.core.biz.ExecutorBiz;\nimport com.xxl.job.core.biz.model.KillParam;\nimport com.xxl.job.core.biz.model.LogParam;\nimport com.xxl.job.core.biz.model.LogResult;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.util.DateUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * index controller\n *\n * @author xuxueli 2015-12-19 16:13:16\n */\n@Controller\n@RequestMapping(\"/joblog\")\npublic class JobLogController {\n    private static Logger logger = LoggerFactory.getLogger(JobLogController.class);\n\n    @Resource\n    private XxlJobGroupDao xxlJobGroupDao;\n    @Resource\n    public XxlJobInfoDao xxlJobInfoDao;\n    @Resource\n    public XxlJobLogDao xxlJobLogDao;\n\n    @RequestMapping\n    public String index(HttpServletRequest request, Model model, @RequestParam(required = false, defaultValue = \"0\") Integer jobId) {\n\n        // 执行器列表\n        List<XxlJobGroup> jobGroupList_all = xxlJobGroupDao.findAll();\n\n        // filter group\n        List<XxlJobGroup> jobGroupList = JobInfoController.filterJobGroupByRole(request, jobGroupList_all);\n        if (jobGroupList == null || jobGroupList.size() == 0) {\n            throw new XxlJobException(I18nUtil.getString(\"jobgroup_empty\"));\n        }\n\n        model.addAttribute(\"JobGroupList\", jobGroupList);\n\n        // 任务\n        if (jobId > 0) {\n            XxlJobInfo jobInfo = xxlJobInfoDao.loadById(jobId);\n            if (jobInfo == null) {\n                throw new RuntimeException(I18nUtil.getString(\"jobinfo_field_id\") + I18nUtil.getString(\"system_unvalid\"));\n            }\n\n            model.addAttribute(\"jobInfo\", jobInfo);\n\n            // valid permission\n            JobInfoController.validPermission(request, jobInfo.getJobGroup());\n        }\n\n        return \"joblog/joblog.index\";\n    }\n\n    @RequestMapping(\"/getJobsByGroup\")\n    @ResponseBody\n    public ReturnT<List<XxlJobInfo>> getJobsByGroup(int jobGroup) {\n        List<XxlJobInfo> list = xxlJobInfoDao.getJobsByGroup(jobGroup);\n        return new ReturnT<List<XxlJobInfo>>(list);\n    }\n\n    @RequestMapping(\"/pageList\")\n    @ResponseBody\n    public Map<String, Object> pageList(HttpServletRequest request,\n                                        @RequestParam(required = false, defaultValue = \"0\") int start,\n                                        @RequestParam(required = false, defaultValue = \"10\") int length,\n                                        int jobGroup, int jobId, int logStatus, String filterTime) {\n\n        // valid permission\n        JobInfoController.validPermission(request, jobGroup);    // 仅管理员支持查询全部；普通用户仅支持查询有权限的 jobGroup\n\n        // parse param\n        Date triggerTimeStart = null;\n        Date triggerTimeEnd = null;\n        if (filterTime != null && filterTime.trim().length() > 0) {\n            String[] temp = filterTime.split(\" - \");\n            if (temp.length == 2) {\n                triggerTimeStart = DateUtil.parseDateTime(temp[0]);\n                triggerTimeEnd = DateUtil.parseDateTime(temp[1]);\n            }\n        }\n\n        // page query\n        List<XxlJobLog> list = xxlJobLogDao.pageList(start, length, jobGroup, jobId, triggerTimeStart, triggerTimeEnd, logStatus);\n        int list_count = xxlJobLogDao.pageListCount(start, length, jobGroup, jobId, triggerTimeStart, triggerTimeEnd, logStatus);\n\n        // package result\n        Map<String, Object> maps = new HashMap<String, Object>();\n        maps.put(\"recordsTotal\", list_count);        // 总记录数\n        maps.put(\"recordsFiltered\", list_count);    // 过滤后的总记录数\n        maps.put(\"data\", list);                    // 分页列表\n        return maps;\n    }\n\n    @RequestMapping(\"/logDetailPage\")\n    public String logDetailPage(int id, Model model) {\n\n        // base check\n        ReturnT<String> logStatue = ReturnT.SUCCESS;\n        XxlJobLog jobLog = xxlJobLogDao.load(id);\n        if (jobLog == null) {\n            throw new RuntimeException(I18nUtil.getString(\"joblog_logid_unvalid\"));\n        }\n\n        model.addAttribute(\"triggerCode\", jobLog.getTriggerCode());\n        model.addAttribute(\"handleCode\", jobLog.getHandleCode());\n        model.addAttribute(\"logId\", jobLog.getId());\n        return \"joblog/joblog.detail\";\n    }\n\n    @RequestMapping(\"/logDetailCat\")\n    @ResponseBody\n    public ReturnT<LogResult> logDetailCat(long logId, int fromLineNum) {\n        try {\n            // valid\n            XxlJobLog jobLog = xxlJobLogDao.load(logId);\t// todo, need to improve performance\n            if (jobLog == null) {\n                return new ReturnT<LogResult>(ReturnT.FAIL_CODE, I18nUtil.getString(\"joblog_logid_unvalid\"));\n            }\n\n            // log cat\n            ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(jobLog.getExecutorAddress());\n            ReturnT<LogResult> logResult = executorBiz.log(new LogParam(jobLog.getTriggerTime().getTime(), logId, fromLineNum));\n\n            // is end\n            if (logResult.getContent() != null && logResult.getContent().getFromLineNum() > logResult.getContent().getToLineNum()) {\n                if (jobLog.getHandleCode() > 0) {\n                    logResult.getContent().setEnd(true);\n                }\n            }\n\n            return logResult;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return new ReturnT<LogResult>(ReturnT.FAIL_CODE, e.getMessage());\n        }\n    }\n\n    @RequestMapping(\"/logKill\")\n    @ResponseBody\n    public ReturnT<String> logKill(int id) {\n        // base check\n        XxlJobLog log = xxlJobLogDao.load(id);\n        XxlJobInfo jobInfo = xxlJobInfoDao.loadById(log.getJobId());\n        if (jobInfo == null) {\n            return new ReturnT<String>(500, I18nUtil.getString(\"jobinfo_glue_jobid_unvalid\"));\n        }\n        if (ReturnT.SUCCESS_CODE != log.getTriggerCode()) {\n            return new ReturnT<String>(500, I18nUtil.getString(\"joblog_kill_log_limit\"));\n        }\n\n        // request of kill\n        ReturnT<String> runResult = null;\n        try {\n            ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(log.getExecutorAddress());\n            runResult = executorBiz.kill(new KillParam(jobInfo.getId()));\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            runResult = new ReturnT<String>(500, e.getMessage());\n        }\n\n        if (ReturnT.SUCCESS_CODE == runResult.getCode()) {\n            log.setHandleCode(ReturnT.FAIL_CODE);\n            log.setHandleMsg(I18nUtil.getString(\"joblog_kill_log_byman\") + \":\" + (runResult.getMsg() != null ? runResult.getMsg() : \"\"));\n            log.setHandleTime(new Date());\n            XxlJobCompleter.updateHandleInfoAndFinish(log);\n            return new ReturnT<String>(runResult.getMsg());\n        } else {\n            return new ReturnT<String>(500, runResult.getMsg());\n        }\n    }\n\n    @RequestMapping(\"/clearLog\")\n    @ResponseBody\n    public ReturnT<String> clearLog(int jobGroup, int jobId, int type) {\n\n        Date clearBeforeTime = null;\n        int clearBeforeNum = 0;\n        if (type == 1) {\n            clearBeforeTime = DateUtil.addMonths(new Date(), -1);    // 清理一个月之前日志数据\n        } else if (type == 2) {\n            clearBeforeTime = DateUtil.addMonths(new Date(), -3);    // 清理三个月之前日志数据\n        } else if (type == 3) {\n            clearBeforeTime = DateUtil.addMonths(new Date(), -6);    // 清理六个月之前日志数据\n        } else if (type == 4) {\n            clearBeforeTime = DateUtil.addYears(new Date(), -1);    // 清理一年之前日志数据\n        } else if (type == 5) {\n            clearBeforeNum = 1000;        // 清理一千条以前日志数据\n        } else if (type == 6) {\n            clearBeforeNum = 10000;        // 清理一万条以前日志数据\n        } else if (type == 7) {\n            clearBeforeNum = 30000;        // 清理三万条以前日志数据\n        } else if (type == 8) {\n            clearBeforeNum = 100000;    // 清理十万条以前日志数据\n        } else if (type == 9) {\n            clearBeforeNum = 0;            // 清理所有日志数据\n        } else {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString(\"joblog_clean_type_unvalid\"));\n        }\n\n        List<Long> logIds = null;\n        do {\n            logIds = xxlJobLogDao.findClearLogIds(jobGroup, jobId, clearBeforeTime, clearBeforeNum, 1000);\n            if (logIds != null && logIds.size() > 0) {\n                xxlJobLogDao.clearLog(logIds);\n            }\n        } while (logIds != null && logIds.size() > 0);\n\n        return ReturnT.SUCCESS;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/UserController.java",
    "content": "package com.xxl.job.admin.controller;\n\nimport com.xxl.job.admin.controller.annotation.PermissionLimit;\nimport com.xxl.job.admin.core.model.XxlJobGroup;\nimport com.xxl.job.admin.core.model.XxlJobUser;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport com.xxl.job.admin.dao.XxlJobGroupDao;\nimport com.xxl.job.admin.dao.XxlJobUserDao;\nimport com.xxl.job.admin.service.LoginService;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.util.DigestUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author xuxueli 2019-05-04 16:39:50\n */\n@Controller\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Resource\n    private XxlJobUserDao xxlJobUserDao;\n    @Resource\n    private XxlJobGroupDao xxlJobGroupDao;\n\n    @RequestMapping\n    @PermissionLimit(adminuser = true)\n    public String index(Model model) {\n\n        // 执行器列表\n        List<XxlJobGroup> groupList = xxlJobGroupDao.findAll();\n        model.addAttribute(\"groupList\", groupList);\n\n        return \"user/user.index\";\n    }\n\n    @RequestMapping(\"/pageList\")\n    @ResponseBody\n    @PermissionLimit(adminuser = true)\n    public Map<String, Object> pageList(@RequestParam(required = false, defaultValue = \"0\") int start,\n                                        @RequestParam(required = false, defaultValue = \"10\") int length,\n                                        String username, int role) {\n\n        // page list\n        List<XxlJobUser> list = xxlJobUserDao.pageList(start, length, username, role);\n        int list_count = xxlJobUserDao.pageListCount(start, length, username, role);\n\n        // filter\n        if (list != null && list.size() > 0) {\n            for (XxlJobUser item : list) {\n                item.setPassword(null);\n            }\n        }\n\n        // package result\n        Map<String, Object> maps = new HashMap<String, Object>();\n        maps.put(\"recordsTotal\", list_count);        // 总记录数\n        maps.put(\"recordsFiltered\", list_count);    // 过滤后的总记录数\n        maps.put(\"data\", list);                    // 分页列表\n        return maps;\n    }\n\n    @RequestMapping(\"/add\")\n    @ResponseBody\n    @PermissionLimit(adminuser = true)\n    public ReturnT<String> add(XxlJobUser xxlJobUser) {\n\n        // valid username\n        if (!StringUtils.hasText(xxlJobUser.getUsername())) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString(\"system_please_input\") + I18nUtil.getString(\"user_username\"));\n        }\n        xxlJobUser.setUsername(xxlJobUser.getUsername().trim());\n        if (!(xxlJobUser.getUsername().length() >= 4 && xxlJobUser.getUsername().length() <= 20)) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString(\"system_lengh_limit\") + \"[4-20]\");\n        }\n        // valid password\n        if (!StringUtils.hasText(xxlJobUser.getPassword())) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString(\"system_please_input\") + I18nUtil.getString(\"user_password\"));\n        }\n        xxlJobUser.setPassword(xxlJobUser.getPassword().trim());\n        if (!(xxlJobUser.getPassword().length() >= 4 && xxlJobUser.getPassword().length() <= 20)) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString(\"system_lengh_limit\") + \"[4-20]\");\n        }\n        // md5 password\n        xxlJobUser.setPassword(DigestUtils.md5DigestAsHex(xxlJobUser.getPassword().getBytes()));\n\n        // check repeat\n        XxlJobUser existUser = xxlJobUserDao.loadByUserName(xxlJobUser.getUsername());\n        if (existUser != null) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString(\"user_username_repeat\"));\n        }\n\n        // write\n        xxlJobUserDao.save(xxlJobUser);\n        return ReturnT.SUCCESS;\n    }\n\n    @RequestMapping(\"/update\")\n    @ResponseBody\n    @PermissionLimit(adminuser = true)\n    public ReturnT<String> update(HttpServletRequest request, XxlJobUser xxlJobUser) {\n\n        // avoid opt login seft\n        XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);\n        if (loginUser.getUsername().equals(xxlJobUser.getUsername())) {\n            return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString(\"user_update_loginuser_limit\"));\n        }\n\n        // valid password\n        if (StringUtils.hasText(xxlJobUser.getPassword())) {\n            xxlJobUser.setPassword(xxlJobUser.getPassword().trim());\n            if (!(xxlJobUser.getPassword().length() >= 4 && xxlJobUser.getPassword().length() <= 20)) {\n                return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString(\"system_lengh_limit\") + \"[4-20]\");\n            }\n            // md5 password\n            xxlJobUser.setPassword(DigestUtils.md5DigestAsHex(xxlJobUser.getPassword().getBytes()));\n        } else {\n            xxlJobUser.setPassword(null);\n        }\n\n        // write\n        xxlJobUserDao.update(xxlJobUser);\n        return ReturnT.SUCCESS;\n    }\n\n    @RequestMapping(\"/remove\")\n    @ResponseBody\n    @PermissionLimit(adminuser = true)\n    public ReturnT<String> remove(HttpServletRequest request, int id) {\n\n        // avoid opt login seft\n        XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);\n        if (loginUser.getId() == id) {\n            return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString(\"user_update_loginuser_limit\"));\n        }\n\n        xxlJobUserDao.delete(id);\n        return ReturnT.SUCCESS;\n    }\n\n    @RequestMapping(\"/updatePwd\")\n    @ResponseBody\n    public ReturnT<String> updatePwd(HttpServletRequest request, String password) {\n\n        // valid password\n        if (password == null || password.trim().length() == 0) {\n            return new ReturnT<String>(ReturnT.FAIL.getCode(), \"密码不可为空\");\n        }\n        password = password.trim();\n        if (!(password.length() >= 4 && password.length() <= 20)) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString(\"system_lengh_limit\") + \"[4-20]\");\n        }\n\n        // md5 password\n        String md5Password = DigestUtils.md5DigestAsHex(password.getBytes());\n\n        // update pwd\n        XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);\n\n        // do write\n        XxlJobUser existUser = xxlJobUserDao.loadByUserName(loginUser.getUsername());\n        existUser.setPassword(md5Password);\n        xxlJobUserDao.update(existUser);\n\n        return ReturnT.SUCCESS;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/annotation/PermissionLimit.java",
    "content": "package com.xxl.job.admin.controller.annotation;\n\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 权限限制\n *\n * @author xuxueli 2015-12-12 18:29:02\n */\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface PermissionLimit {\n\n    /**\n     * 登录拦截 (默认拦截)\n     */\n    boolean limit() default true;\n\n    /**\n     * 要求管理员权限\n     *\n     * @return\n     */\n    boolean adminuser() default false;\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java",
    "content": "package com.xxl.job.admin.controller.interceptor;\n\nimport com.xxl.job.admin.core.util.FtlUtil;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.servlet.AsyncHandlerInterceptor;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.HashMap;\n\n/**\n * push cookies to model as cookieMap\n *\n * @author xuxueli 2015-12-12 18:09:04\n */\n@Component\npublic class CookieInterceptor implements AsyncHandlerInterceptor {\n\n    @Override\n    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,\n                           ModelAndView modelAndView) throws Exception {\n\n        // cookie\n        if (modelAndView != null && request.getCookies() != null && request.getCookies().length > 0) {\n            HashMap<String, Cookie> cookieMap = new HashMap<String, Cookie>();\n            for (Cookie ck : request.getCookies()) {\n                cookieMap.put(ck.getName(), ck);\n            }\n            modelAndView.addObject(\"cookieMap\", cookieMap);\n        }\n\n        // static method\n        if (modelAndView != null) {\n            modelAndView.addObject(\"I18nUtil\", FtlUtil.generateStaticModel(I18nUtil.class.getName()));\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java",
    "content": "package com.xxl.job.admin.controller.interceptor;\n\nimport com.xxl.job.admin.controller.annotation.PermissionLimit;\nimport com.xxl.job.admin.core.model.XxlJobUser;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport com.xxl.job.admin.service.LoginService;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.method.HandlerMethod;\nimport org.springframework.web.servlet.AsyncHandlerInterceptor;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n/**\n * 权限拦截\n *\n * @author xuxueli 2015-12-12 18:09:04\n */\n@Component\npublic class PermissionInterceptor implements AsyncHandlerInterceptor {\n\n    @Resource\n    private LoginService loginService;\n\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {\n\n        if (!(handler instanceof HandlerMethod)) {\n            return true;    // proceed with the next interceptor\n        }\n\n        // if need login\n        boolean needLogin = true;\n        boolean needAdminuser = false;\n        HandlerMethod method = (HandlerMethod) handler;\n        PermissionLimit permission = method.getMethodAnnotation(PermissionLimit.class);\n        if (permission != null) {\n            needLogin = permission.limit();\n            needAdminuser = permission.adminuser();\n        }\n\n        if (needLogin) {\n            XxlJobUser loginUser = loginService.ifLogin(request, response);\n            if (loginUser == null) {\n                response.setStatus(302);\n                response.setHeader(\"location\", request.getContextPath() + \"/toLogin\");\n                return false;\n            }\n            if (needAdminuser && loginUser.getRole() != 1) {\n                throw new RuntimeException(I18nUtil.getString(\"system_permission_limit\"));\n            }\n            request.setAttribute(LoginService.LOGIN_IDENTITY_KEY, loginUser);\n        }\n\n        return true;    // proceed with the next interceptor\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/WebMvcConfig.java",
    "content": "package com.xxl.job.admin.controller.interceptor;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\nimport javax.annotation.Resource;\n\n/**\n * web mvc config\n *\n * @author xuxueli 2018-04-02 20:48:20\n */\n@Configuration\npublic class WebMvcConfig implements WebMvcConfigurer {\n\n    @Resource\n    private PermissionInterceptor permissionInterceptor;\n    @Resource\n    private CookieInterceptor cookieInterceptor;\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        registry.addInterceptor(permissionInterceptor).addPathPatterns(\"/**\");\n        registry.addInterceptor(cookieInterceptor).addPathPatterns(\"/**\");\n    }\n\n}"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/resolver/WebExceptionResolver.java",
    "content": "package com.xxl.job.admin.controller.resolver;\n\nimport com.xxl.job.admin.core.exception.XxlJobException;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.admin.core.util.JacksonUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.method.HandlerMethod;\nimport org.springframework.web.servlet.HandlerExceptionResolver;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * common exception resolver\n *\n * @author xuxueli 2016-1-6 19:22:18\n */\n@Component\npublic class WebExceptionResolver implements HandlerExceptionResolver {\n    private static transient Logger logger = LoggerFactory.getLogger(WebExceptionResolver.class);\n\n    @Override\n    public ModelAndView resolveException(HttpServletRequest request,\n                                         HttpServletResponse response, Object handler, Exception ex) {\n\n        if (!(ex instanceof XxlJobException)) {\n            logger.error(\"WebExceptionResolver:{}\", ex);\n        }\n\n        // if json\n        boolean isJson = false;\n        if (handler instanceof HandlerMethod) {\n            HandlerMethod method = (HandlerMethod) handler;\n            ResponseBody responseBody = method.getMethodAnnotation(ResponseBody.class);\n            if (responseBody != null) {\n                isJson = true;\n            }\n        }\n\n        // error result\n        ReturnT<String> errorResult = new ReturnT<String>(ReturnT.FAIL_CODE, ex.toString().replaceAll(\"\\n\", \"<br/>\"));\n\n        // response\n        ModelAndView mv = new ModelAndView();\n        if (isJson) {\n            try {\n                response.setContentType(\"application/json;charset=utf-8\");\n                response.getWriter().print(JacksonUtil.writeValueAsString(errorResult));\n            } catch (IOException e) {\n                logger.error(e.getMessage(), e);\n            }\n            return mv;\n        } else {\n\n            mv.addObject(\"exceptionMsg\", errorResult.getMsg());\n            mv.setViewName(\"/common/common.exception\");\n            return mv;\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/JobAlarm.java",
    "content": "package com.xxl.job.admin.core.alarm;\n\nimport com.xxl.job.admin.core.model.XxlJobInfo;\nimport com.xxl.job.admin.core.model.XxlJobLog;\n\n/**\n * @author xuxueli 2020-01-19\n */\npublic interface JobAlarm {\n\n    /**\n     * job alarm\n     *\n     * @param info\n     * @param jobLog\n     * @return\n     */\n    public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog);\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/JobAlarmer.java",
    "content": "package com.xxl.job.admin.core.alarm;\n\nimport com.xxl.job.admin.core.model.XxlJobInfo;\nimport com.xxl.job.admin.core.model.XxlJobLog;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n@Component\npublic class JobAlarmer implements ApplicationContextAware, InitializingBean {\n    private static Logger logger = LoggerFactory.getLogger(JobAlarmer.class);\n\n    private ApplicationContext applicationContext;\n    private List<JobAlarm> jobAlarmList;\n\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        this.applicationContext = applicationContext;\n    }\n\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        Map<String, JobAlarm> serviceBeanMap = applicationContext.getBeansOfType(JobAlarm.class);\n        if (serviceBeanMap != null && serviceBeanMap.size() > 0) {\n            jobAlarmList = new ArrayList<JobAlarm>(serviceBeanMap.values());\n        }\n    }\n\n    /**\n     * job alarm\n     *\n     * @param info\n     * @param jobLog\n     * @return\n     */\n    public boolean alarm(XxlJobInfo info, XxlJobLog jobLog) {\n\n        boolean result = false;\n        if (jobAlarmList != null && jobAlarmList.size() > 0) {\n            result = true;  // success means all-success\n            for (JobAlarm alarm : jobAlarmList) {\n                boolean resultItem = false;\n                try {\n                    resultItem = alarm.doAlarm(info, jobLog);\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                }\n                if (!resultItem) {\n                    result = false;\n                }\n            }\n        }\n\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/impl/EmailJobAlarm.java",
    "content": "package com.xxl.job.admin.core.alarm.impl;\n\nimport com.xxl.job.admin.core.alarm.JobAlarm;\nimport com.xxl.job.admin.core.conf.XxlJobAdminConfig;\nimport com.xxl.job.admin.core.model.XxlJobGroup;\nimport com.xxl.job.admin.core.model.XxlJobInfo;\nimport com.xxl.job.admin.core.model.XxlJobLog;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.mail.javamail.MimeMessageHelper;\nimport org.springframework.stereotype.Component;\n\nimport javax.mail.internet.MimeMessage;\nimport java.text.MessageFormat;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * job alarm by email\n *\n * @author xuxueli 2020-01-19\n */\n@Component\npublic class EmailJobAlarm implements JobAlarm {\n    private static Logger logger = LoggerFactory.getLogger(EmailJobAlarm.class);\n\n    /**\n     * fail alarm\n     *\n     * @param jobLog\n     */\n    @Override\n    public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog) {\n        boolean alarmResult = true;\n\n        // send monitor email\n        if (info != null && info.getAlarmEmail() != null && info.getAlarmEmail().trim().length() > 0) {\n\n            // alarmContent\n            String alarmContent = \"Alarm Job LogId=\" + jobLog.getId();\n            if (jobLog.getTriggerCode() != ReturnT.SUCCESS_CODE) {\n                alarmContent += \"<br>TriggerMsg=<br>\" + jobLog.getTriggerMsg();\n            }\n            if (jobLog.getHandleCode() > 0 && jobLog.getHandleCode() != ReturnT.SUCCESS_CODE) {\n                alarmContent += \"<br>HandleCode=\" + jobLog.getHandleMsg();\n            }\n\n            // email info\n            XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(Integer.valueOf(info.getJobGroup()));\n            String personal = I18nUtil.getString(\"admin_name_full\");\n            String title = I18nUtil.getString(\"jobconf_monitor\");\n            String content = MessageFormat.format(loadEmailJobAlarmTemplate(),\n                group != null ? group.getTitle() : \"null\",\n                info.getId(),\n                info.getJobDesc(),\n                alarmContent);\n\n            Set<String> emailSet = new HashSet<String>(Arrays.asList(info.getAlarmEmail().split(\",\")));\n            for (String email : emailSet) {\n\n                // make mail\n                try {\n                    MimeMessage mimeMessage = XxlJobAdminConfig.getAdminConfig().getMailSender().createMimeMessage();\n\n                    MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);\n                    helper.setFrom(XxlJobAdminConfig.getAdminConfig().getEmailFrom(), personal);\n                    helper.setTo(email);\n                    helper.setSubject(title);\n                    helper.setText(content, true);\n\n                    XxlJobAdminConfig.getAdminConfig().getMailSender().send(mimeMessage);\n                } catch (Exception e) {\n                    logger.error(\">>>>>>>>>>> xxl-job, job fail alarm email send error, JobLogId:{}\", jobLog.getId(), e);\n\n                    alarmResult = false;\n                }\n\n            }\n        }\n\n        return alarmResult;\n    }\n\n    /**\n     * load email job alarm template\n     *\n     * @return\n     */\n    private static final String loadEmailJobAlarmTemplate() {\n        String mailBodyTemplate = \"<h5>\" + I18nUtil.getString(\"jobconf_monitor_detail\") + \"：</span>\" +\n            \"<table border=\\\"1\\\" cellpadding=\\\"3\\\" style=\\\"border-collapse:collapse; width:80%;\\\" >\\n\" +\n            \"   <thead style=\\\"font-weight: bold;color: #ffffff;background-color: #ff8c00;\\\" >\" +\n            \"      <tr>\\n\" +\n            \"         <td width=\\\"20%\\\" >\" + I18nUtil.getString(\"jobinfo_field_jobgroup\") + \"</td>\\n\" +\n            \"         <td width=\\\"10%\\\" >\" + I18nUtil.getString(\"jobinfo_field_id\") + \"</td>\\n\" +\n            \"         <td width=\\\"20%\\\" >\" + I18nUtil.getString(\"jobinfo_field_jobdesc\") + \"</td>\\n\" +\n            \"         <td width=\\\"10%\\\" >\" + I18nUtil.getString(\"jobconf_monitor_alarm_title\") + \"</td>\\n\" +\n            \"         <td width=\\\"40%\\\" >\" + I18nUtil.getString(\"jobconf_monitor_alarm_content\") + \"</td>\\n\" +\n            \"      </tr>\\n\" +\n            \"   </thead>\\n\" +\n            \"   <tbody>\\n\" +\n            \"      <tr>\\n\" +\n            \"         <td>{0}</td>\\n\" +\n            \"         <td>{1}</td>\\n\" +\n            \"         <td>{2}</td>\\n\" +\n            \"         <td>\" + I18nUtil.getString(\"jobconf_monitor_alarm_type\") + \"</td>\\n\" +\n            \"         <td>{3}</td>\\n\" +\n            \"      </tr>\\n\" +\n            \"   </tbody>\\n\" +\n            \"</table>\";\n\n        return mailBodyTemplate;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/complete/XxlJobCompleter.java",
    "content": "package com.xxl.job.admin.core.complete;\n\nimport com.xxl.job.admin.core.conf.XxlJobAdminConfig;\nimport com.xxl.job.admin.core.model.XxlJobInfo;\nimport com.xxl.job.admin.core.model.XxlJobLog;\nimport com.xxl.job.admin.core.thread.JobTriggerPoolHelper;\nimport com.xxl.job.admin.core.trigger.TriggerTypeEnum;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.context.XxlJobContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.text.MessageFormat;\n\n/**\n * @author xuxueli 2020-10-30 20:43:10\n */\npublic class XxlJobCompleter {\n    private static Logger logger = LoggerFactory.getLogger(XxlJobCompleter.class);\n\n    /**\n     * common fresh handle entrance (limit only once)\n     *\n     * @param xxlJobLog\n     * @return\n     */\n    public static int updateHandleInfoAndFinish(XxlJobLog xxlJobLog) {\n\n        // finish\n        finishJob(xxlJobLog);\n\n        // text最大64kb 避免长度过长\n        if (xxlJobLog.getHandleMsg().length() > 15000) {\n            xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg().substring(0, 15000));\n        }\n\n        // fresh handle\n        return XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateHandleInfo(xxlJobLog);\n    }\n\n\n    /**\n     * do somethind to finish job\n     */\n    private static void finishJob(XxlJobLog xxlJobLog) {\n\n        // 1、handle success, to trigger child job\n        String triggerChildMsg = null;\n        if (XxlJobContext.HANDLE_CODE_SUCCESS == xxlJobLog.getHandleCode()) {\n            XxlJobInfo xxlJobInfo = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(xxlJobLog.getJobId());\n            if (xxlJobInfo != null && xxlJobInfo.getChildJobId() != null && xxlJobInfo.getChildJobId().trim().length() > 0) {\n                triggerChildMsg = \"<br><br><span style=\\\"color:#00c0ef;\\\" > >>>>>>>>>>>\" + I18nUtil.getString(\"jobconf_trigger_child_run\") + \"<<<<<<<<<<< </span><br>\";\n\n                String[] childJobIds = xxlJobInfo.getChildJobId().split(\",\");\n                for (int i = 0; i < childJobIds.length; i++) {\n                    int childJobId = (childJobIds[i] != null && childJobIds[i].trim().length() > 0 && isNumeric(childJobIds[i])) ? Integer.valueOf(childJobIds[i]) : -1;\n                    if (childJobId > 0) {\n\n                        JobTriggerPoolHelper.trigger(childJobId, TriggerTypeEnum.PARENT, -1, null, null, null);\n                        ReturnT<String> triggerChildResult = ReturnT.SUCCESS;\n\n                        // add msg\n                        triggerChildMsg += MessageFormat.format(I18nUtil.getString(\"jobconf_callback_child_msg1\"),\n                            (i + 1),\n                            childJobIds.length,\n                            childJobIds[i],\n                            (triggerChildResult.getCode() == ReturnT.SUCCESS_CODE ? I18nUtil.getString(\"system_success\") : I18nUtil.getString(\"system_fail\")),\n                            triggerChildResult.getMsg());\n                    } else {\n                        triggerChildMsg += MessageFormat.format(I18nUtil.getString(\"jobconf_callback_child_msg2\"),\n                            (i + 1),\n                            childJobIds.length,\n                            childJobIds[i]);\n                    }\n                }\n\n            }\n        }\n\n        if (triggerChildMsg != null) {\n            xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg() + triggerChildMsg);\n        }\n\n        // 2、fix_delay trigger next\n        // on the way\n\n    }\n\n    private static boolean isNumeric(String str) {\n        try {\n            int result = Integer.valueOf(str);\n            return true;\n        } catch (NumberFormatException e) {\n            return false;\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java",
    "content": "package com.xxl.job.admin.core.conf;\n\nimport com.xxl.job.admin.core.alarm.JobAlarmer;\nimport com.xxl.job.admin.core.scheduler.XxlJobScheduler;\nimport com.xxl.job.admin.dao.*;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.mail.javamail.JavaMailSender;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport javax.sql.DataSource;\nimport java.util.Arrays;\n\n/**\n * xxl-job config\n *\n * @author xuxueli 2017-04-28\n */\n\n@Component\npublic class XxlJobAdminConfig implements InitializingBean, DisposableBean {\n\n    private static XxlJobAdminConfig adminConfig = null;\n\n    public static XxlJobAdminConfig getAdminConfig() {\n        return adminConfig;\n    }\n\n\n    // ---------------------- XxlJobScheduler ----------------------\n\n    private XxlJobScheduler xxlJobScheduler;\n\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        adminConfig = this;\n\n        xxlJobScheduler = new XxlJobScheduler();\n        xxlJobScheduler.init();\n    }\n\n    @Override\n    public void destroy() throws Exception {\n        xxlJobScheduler.destroy();\n    }\n\n\n    // ---------------------- XxlJobScheduler ----------------------\n\n    // conf\n    @Value(\"${xxl.job.i18n}\")\n    private String i18n;\n\n    @Value(\"${xxl.job.accessToken}\")\n    private String accessToken;\n\n    @Value(\"${spring.mail.from}\")\n    private String emailFrom;\n\n    @Value(\"${xxl.job.triggerpool.fast.max}\")\n    private int triggerPoolFastMax;\n\n    @Value(\"${xxl.job.triggerpool.slow.max}\")\n    private int triggerPoolSlowMax;\n\n    @Value(\"${xxl.job.logretentiondays}\")\n    private int logretentiondays;\n\n    // dao, service\n\n    @Resource\n    private XxlJobLogDao xxlJobLogDao;\n    @Resource\n    private XxlJobInfoDao xxlJobInfoDao;\n    @Resource\n    private XxlJobRegistryDao xxlJobRegistryDao;\n    @Resource\n    private XxlJobGroupDao xxlJobGroupDao;\n    @Resource\n    private XxlJobLogReportDao xxlJobLogReportDao;\n    @Resource\n    private JavaMailSender mailSender;\n    @Resource\n    private DataSource dataSource;\n    @Resource\n    private JobAlarmer jobAlarmer;\n\n\n    public String getI18n() {\n        if (!Arrays.asList(\"zh_CN\", \"zh_TC\", \"en\").contains(i18n)) {\n            return \"zh_CN\";\n        }\n        return i18n;\n    }\n\n    public String getAccessToken() {\n        return accessToken;\n    }\n\n    public String getEmailFrom() {\n        return emailFrom;\n    }\n\n    public int getTriggerPoolFastMax() {\n        if (triggerPoolFastMax < 200) {\n            return 200;\n        }\n        return triggerPoolFastMax;\n    }\n\n    public int getTriggerPoolSlowMax() {\n        if (triggerPoolSlowMax < 100) {\n            return 100;\n        }\n        return triggerPoolSlowMax;\n    }\n\n    public int getLogretentiondays() {\n        if (logretentiondays < 7) {\n            return -1;  // Limit greater than or equal to 7, otherwise close\n        }\n        return logretentiondays;\n    }\n\n    public XxlJobLogDao getXxlJobLogDao() {\n        return xxlJobLogDao;\n    }\n\n    public XxlJobInfoDao getXxlJobInfoDao() {\n        return xxlJobInfoDao;\n    }\n\n    public XxlJobRegistryDao getXxlJobRegistryDao() {\n        return xxlJobRegistryDao;\n    }\n\n    public XxlJobGroupDao getXxlJobGroupDao() {\n        return xxlJobGroupDao;\n    }\n\n    public XxlJobLogReportDao getXxlJobLogReportDao() {\n        return xxlJobLogReportDao;\n    }\n\n    public JavaMailSender getMailSender() {\n        return mailSender;\n    }\n\n    public DataSource getDataSource() {\n        return dataSource;\n    }\n\n    public JobAlarmer getJobAlarmer() {\n        return jobAlarmer;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/cron/CronExpression.java",
    "content": "/*\n * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy\n * of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations\n * under the License.\n *\n */\n\npackage com.xxl.job.admin.core.cron;\n\nimport java.io.Serializable;\nimport java.text.ParseException;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.SortedSet;\nimport java.util.StringTokenizer;\nimport java.util.TimeZone;\nimport java.util.TreeSet;\n\n/**\n * Provides a parser and evaluator for unix-like cron expressions. Cron\n * expressions provide the ability to specify complex time combinations such as\n * &quot;At 8:00am every Monday through Friday&quot; or &quot;At 1:30am every\n * last Friday of the month&quot;.\n * <P>\n * Cron expressions are comprised of 6 required fields and one optional field\n * separated by white space. The fields respectively are described as follows:\n *\n * <table cellspacing=\"8\">\n * <tr>\n * <th align=\"left\">Field Name</th>\n * <th align=\"left\">&nbsp;</th>\n * <th align=\"left\">Allowed Values</th>\n * <th align=\"left\">&nbsp;</th>\n * <th align=\"left\">Allowed Special Characters</th>\n * </tr>\n * <tr>\n * <td align=\"left\"><code>Seconds</code></td>\n * <td align=\"left\">&nbsp;</th>\n * <td align=\"left\"><code>0-59</code></td>\n * <td align=\"left\">&nbsp;</th>\n * <td align=\"left\"><code>, - * /</code></td>\n * </tr>\n * <tr>\n * <td align=\"left\"><code>Minutes</code></td>\n * <td align=\"left\">&nbsp;</th>\n * <td align=\"left\"><code>0-59</code></td>\n * <td align=\"left\">&nbsp;</th>\n * <td align=\"left\"><code>, - * /</code></td>\n * </tr>\n * <tr>\n * <td align=\"left\"><code>Hours</code></td>\n * <td align=\"left\">&nbsp;</th>\n * <td align=\"left\"><code>0-23</code></td>\n * <td align=\"left\">&nbsp;</th>\n * <td align=\"left\"><code>, - * /</code></td>\n * </tr>\n * <tr>\n * <td align=\"left\"><code>Day-of-month</code></td>\n * <td align=\"left\">&nbsp;</th>\n * <td align=\"left\"><code>1-31</code></td>\n * <td align=\"left\">&nbsp;</th>\n * <td align=\"left\"><code>, - * ? / L W</code></td>\n * </tr>\n * <tr>\n * <td align=\"left\"><code>Month</code></td>\n * <td align=\"left\">&nbsp;</th>\n * <td align=\"left\"><code>0-11 or JAN-DEC</code></td>\n * <td align=\"left\">&nbsp;</th>\n * <td align=\"left\"><code>, - * /</code></td>\n * </tr>\n * <tr>\n * <td align=\"left\"><code>Day-of-Week</code></td>\n * <td align=\"left\">&nbsp;</th>\n * <td align=\"left\"><code>1-7 or SUN-SAT</code></td>\n * <td align=\"left\">&nbsp;</th>\n * <td align=\"left\"><code>, - * ? / L #</code></td>\n * </tr>\n * <tr>\n * <td align=\"left\"><code>Year (Optional)</code></td>\n * <td align=\"left\">&nbsp;</th>\n * <td align=\"left\"><code>empty, 1970-2199</code></td>\n * <td align=\"left\">&nbsp;</th>\n * <td align=\"left\"><code>, - * /</code></td>\n * </tr>\n * </table>\n * <P>\n * The '*' character is used to specify all values. For example, &quot;*&quot;\n * in the minute field means &quot;every minute&quot;.\n * <P>\n * The '?' character is allowed for the day-of-month and day-of-week fields. It\n * is used to specify 'no specific value'. This is useful when you need to\n * specify something in one of the two fields, but not the other.\n * <P>\n * The '-' character is used to specify ranges For example &quot;10-12&quot; in\n * the hour field means &quot;the hours 10, 11 and 12&quot;.\n * <P>\n * The ',' character is used to specify additional values. For example\n * &quot;MON,WED,FRI&quot; in the day-of-week field means &quot;the days Monday,\n * Wednesday, and Friday&quot;.\n * <P>\n * The '/' character is used to specify increments. For example &quot;0/15&quot;\n * in the seconds field means &quot;the seconds 0, 15, 30, and 45&quot;. And\n * &quot;5/15&quot; in the seconds field means &quot;the seconds 5, 20, 35, and\n * 50&quot;.  Specifying '*' before the  '/' is equivalent to specifying 0 is\n * the value to start with. Essentially, for each field in the expression, there\n * is a set of numbers that can be turned on or off. For seconds and minutes,\n * the numbers range from 0 to 59. For hours 0 to 23, for days of the month 0 to\n * 31, and for months 0 to 11 (JAN to DEC). The &quot;/&quot; character simply helps you turn\n * on every &quot;nth&quot; value in the given set. Thus &quot;7/6&quot; in the\n * month field only turns on month &quot;7&quot;, it does NOT mean every 6th\n * month, please note that subtlety.\n * <P>\n * The 'L' character is allowed for the day-of-month and day-of-week fields.\n * This character is short-hand for &quot;last&quot;, but it has different\n * meaning in each of the two fields. For example, the value &quot;L&quot; in\n * the day-of-month field means &quot;the last day of the month&quot; - day 31\n * for January, day 28 for February on non-leap years. If used in the\n * day-of-week field by itself, it simply means &quot;7&quot; or\n * &quot;SAT&quot;. But if used in the day-of-week field after another value, it\n * means &quot;the last xxx day of the month&quot; - for example &quot;6L&quot;\n * means &quot;the last friday of the month&quot;. You can also specify an offset\n * from the last day of the month, such as \"L-3\" which would mean the third-to-last\n * day of the calendar month. <i>When using the 'L' option, it is important not to\n * specify lists, or ranges of values, as you'll get confusing/unexpected results.</i>\n * <P>\n * The 'W' character is allowed for the day-of-month field.  This character\n * is used to specify the weekday (Monday-Friday) nearest the given day.  As an\n * example, if you were to specify &quot;15W&quot; as the value for the\n * day-of-month field, the meaning is: &quot;the nearest weekday to the 15th of\n * the month&quot;. So if the 15th is a Saturday, the trigger will fire on\n * Friday the 14th. If the 15th is a Sunday, the trigger will fire on Monday the\n * 16th. If the 15th is a Tuesday, then it will fire on Tuesday the 15th.\n * However if you specify &quot;1W&quot; as the value for day-of-month, and the\n * 1st is a Saturday, the trigger will fire on Monday the 3rd, as it will not\n * 'jump' over the boundary of a month's days.  The 'W' character can only be\n * specified when the day-of-month is a single day, not a range or list of days.\n * <P>\n * The 'L' and 'W' characters can also be combined for the day-of-month\n * expression to yield 'LW', which translates to &quot;last weekday of the\n * month&quot;.\n * <P>\n * The '#' character is allowed for the day-of-week field. This character is\n * used to specify &quot;the nth&quot; XXX day of the month. For example, the\n * value of &quot;6#3&quot; in the day-of-week field means the third Friday of\n * the month (day 6 = Friday and &quot;#3&quot; = the 3rd one in the month).\n * Other examples: &quot;2#1&quot; = the first Monday of the month and\n * &quot;4#5&quot; = the fifth Wednesday of the month. Note that if you specify\n * &quot;#5&quot; and there is not 5 of the given day-of-week in the month, then\n * no firing will occur that month.  If the '#' character is used, there can\n * only be one expression in the day-of-week field (&quot;3#1,6#3&quot; is\n * not valid, since there are two expressions).\n * <P>\n * <!--The 'C' character is allowed for the day-of-month and day-of-week fields.\n * This character is short-hand for \"calendar\". This means values are\n * calculated against the associated calendar, if any. If no calendar is\n * associated, then it is equivalent to having an all-inclusive calendar. A\n * value of \"5C\" in the day-of-month field means \"the first day included by the\n * calendar on or after the 5th\". A value of \"1C\" in the day-of-week field\n * means \"the first day included by the calendar on or after Sunday\".-->\n * <P>\n * The legal characters and the names of months and days of the week are not\n * case sensitive.\n *\n * <p>\n * <b>NOTES:</b>\n * <ul>\n * <li>Support for specifying both a day-of-week and a day-of-month value is\n * not complete (you'll need to use the '?' character in one of these fields).\n * </li>\n * <li>Overflowing ranges is supported - that is, having a larger number on\n * the left hand side than the right. You might do 22-2 to catch 10 o'clock\n * at night until 2 o'clock in the morning, or you might have NOV-FEB. It is\n * very important to note that overuse of overflowing ranges creates ranges\n * that don't make sense and no effort has been made to determine which\n * interpretation CronExpression chooses. An example would be\n * \"0 0 14-6 ? * FRI-MON\". </li>\n * </ul>\n * </p>\n *\n * @author Sharada Jambula, James House\n * @author Contributions from Mads Henderson\n * @author Refactoring from CronTrigger to CronExpression by Aaron Craven\n * <p>\n * Borrowed from quartz v2.3.1\n */\npublic final class CronExpression implements Serializable, Cloneable {\n\n    private static final long serialVersionUID = 12423409423L;\n\n    protected static final int SECOND = 0;\n    protected static final int MINUTE = 1;\n    protected static final int HOUR = 2;\n    protected static final int DAY_OF_MONTH = 3;\n    protected static final int MONTH = 4;\n    protected static final int DAY_OF_WEEK = 5;\n    protected static final int YEAR = 6;\n    protected static final int ALL_SPEC_INT = 99; // '*'\n    protected static final int NO_SPEC_INT = 98; // '?'\n    protected static final Integer ALL_SPEC = ALL_SPEC_INT;\n    protected static final Integer NO_SPEC = NO_SPEC_INT;\n\n    protected static final Map<String, Integer> monthMap = new HashMap<String, Integer>(20);\n    protected static final Map<String, Integer> dayMap = new HashMap<String, Integer>(60);\n\n    static {\n        monthMap.put(\"JAN\", 0);\n        monthMap.put(\"FEB\", 1);\n        monthMap.put(\"MAR\", 2);\n        monthMap.put(\"APR\", 3);\n        monthMap.put(\"MAY\", 4);\n        monthMap.put(\"JUN\", 5);\n        monthMap.put(\"JUL\", 6);\n        monthMap.put(\"AUG\", 7);\n        monthMap.put(\"SEP\", 8);\n        monthMap.put(\"OCT\", 9);\n        monthMap.put(\"NOV\", 10);\n        monthMap.put(\"DEC\", 11);\n\n        dayMap.put(\"SUN\", 1);\n        dayMap.put(\"MON\", 2);\n        dayMap.put(\"TUE\", 3);\n        dayMap.put(\"WED\", 4);\n        dayMap.put(\"THU\", 5);\n        dayMap.put(\"FRI\", 6);\n        dayMap.put(\"SAT\", 7);\n    }\n\n    private final String cronExpression;\n    private TimeZone timeZone = null;\n    protected transient TreeSet<Integer> seconds;\n    protected transient TreeSet<Integer> minutes;\n    protected transient TreeSet<Integer> hours;\n    protected transient TreeSet<Integer> daysOfMonth;\n    protected transient TreeSet<Integer> months;\n    protected transient TreeSet<Integer> daysOfWeek;\n    protected transient TreeSet<Integer> years;\n\n    protected transient boolean lastdayOfWeek = false;\n    protected transient int nthdayOfWeek = 0;\n    protected transient boolean lastdayOfMonth = false;\n    protected transient boolean nearestWeekday = false;\n    protected transient int lastdayOffset = 0;\n    protected transient boolean expressionParsed = false;\n\n    public static final int MAX_YEAR = Calendar.getInstance().get(Calendar.YEAR) + 100;\n\n    /**\n     * Constructs a new <CODE>CronExpression</CODE> based on the specified\n     * parameter.\n     *\n     * @param cronExpression String representation of the cron expression the\n     *                       new object should represent\n     * @throws ParseException if the string expression cannot be parsed into a valid\n     *                        <CODE>CronExpression</CODE>\n     */\n    public CronExpression(String cronExpression) throws ParseException {\n        if (cronExpression == null) {\n            throw new IllegalArgumentException(\"cronExpression cannot be null\");\n        }\n\n        this.cronExpression = cronExpression.toUpperCase(Locale.US);\n\n        buildExpression(this.cronExpression);\n    }\n\n    /**\n     * Constructs a new {@code CronExpression} as a copy of an existing\n     * instance.\n     *\n     * @param expression The existing cron expression to be copied\n     */\n    public CronExpression(CronExpression expression) {\n        /*\n         * We don't call the other constructor here since we need to swallow the\n         * ParseException. We also elide some of the sanity checking as it is\n         * not logically trippable.\n         */\n        this.cronExpression = expression.getCronExpression();\n        try {\n            buildExpression(cronExpression);\n        } catch (ParseException ex) {\n            throw new AssertionError();\n        }\n        if (expression.getTimeZone() != null) {\n            setTimeZone((TimeZone) expression.getTimeZone().clone());\n        }\n    }\n\n    /**\n     * Indicates whether the given date satisfies the cron expression. Note that\n     * milliseconds are ignored, so two Dates falling on different milliseconds\n     * of the same second will always have the same result here.\n     *\n     * @param date the date to evaluate\n     * @return a boolean indicating whether the given date satisfies the cron\n     * expression\n     */\n    public boolean isSatisfiedBy(Date date) {\n        Calendar testDateCal = Calendar.getInstance(getTimeZone());\n        testDateCal.setTime(date);\n        testDateCal.set(Calendar.MILLISECOND, 0);\n        Date originalDate = testDateCal.getTime();\n\n        testDateCal.add(Calendar.SECOND, -1);\n\n        Date timeAfter = getTimeAfter(testDateCal.getTime());\n\n        return ((timeAfter != null) && (timeAfter.equals(originalDate)));\n    }\n\n    /**\n     * Returns the next date/time <I>after</I> the given date/time which\n     * satisfies the cron expression.\n     *\n     * @param date the date/time at which to begin the search for the next valid\n     *             date/time\n     * @return the next valid date/time\n     */\n    public Date getNextValidTimeAfter(Date date) {\n        return getTimeAfter(date);\n    }\n\n    /**\n     * Returns the next date/time <I>after</I> the given date/time which does\n     * <I>not</I> satisfy the expression\n     *\n     * @param date the date/time at which to begin the search for the next\n     *             invalid date/time\n     * @return the next valid date/time\n     */\n    public Date getNextInvalidTimeAfter(Date date) {\n        long difference = 1000;\n\n        //move back to the nearest second so differences will be accurate\n        Calendar adjustCal = Calendar.getInstance(getTimeZone());\n        adjustCal.setTime(date);\n        adjustCal.set(Calendar.MILLISECOND, 0);\n        Date lastDate = adjustCal.getTime();\n\n        Date newDate;\n\n        //FUTURE_TODO: (QUARTZ-481) IMPROVE THIS! The following is a BAD solution to this problem. Performance will be very bad here, depending on the cron expression. It is, however A solution.\n\n        //keep getting the next included time until it's farther than one second\n        // apart. At that point, lastDate is the last valid fire time. We return\n        // the second immediately following it.\n        while (difference == 1000) {\n            newDate = getTimeAfter(lastDate);\n            if (newDate == null)\n                break;\n\n            difference = newDate.getTime() - lastDate.getTime();\n\n            if (difference == 1000) {\n                lastDate = newDate;\n            }\n        }\n\n        return new Date(lastDate.getTime() + 1000);\n    }\n\n    /**\n     * Returns the time zone for which this <code>CronExpression</code>\n     * will be resolved.\n     */\n    public TimeZone getTimeZone() {\n        if (timeZone == null) {\n            timeZone = TimeZone.getDefault();\n        }\n\n        return timeZone;\n    }\n\n    /**\n     * Sets the time zone for which  this <code>CronExpression</code>\n     * will be resolved.\n     */\n    public void setTimeZone(TimeZone timeZone) {\n        this.timeZone = timeZone;\n    }\n\n    /**\n     * Returns the string representation of the <CODE>CronExpression</CODE>\n     *\n     * @return a string representation of the <CODE>CronExpression</CODE>\n     */\n    @Override\n    public String toString() {\n        return cronExpression;\n    }\n\n    /**\n     * Indicates whether the specified cron expression can be parsed into a\n     * valid cron expression\n     *\n     * @param cronExpression the expression to evaluate\n     * @return a boolean indicating whether the given expression is a valid cron\n     * expression\n     */\n    public static boolean isValidExpression(String cronExpression) {\n\n        try {\n            new CronExpression(cronExpression);\n        } catch (ParseException pe) {\n            return false;\n        }\n\n        return true;\n    }\n\n    public static void validateExpression(String cronExpression) throws ParseException {\n\n        new CronExpression(cronExpression);\n    }\n\n\n    ////////////////////////////////////////////////////////////////////////////\n    //\n    // Expression Parsing Functions\n    //\n    ////////////////////////////////////////////////////////////////////////////\n\n    protected void buildExpression(String expression) throws ParseException {\n        expressionParsed = true;\n\n        try {\n\n            if (seconds == null) {\n                seconds = new TreeSet<Integer>();\n            }\n            if (minutes == null) {\n                minutes = new TreeSet<Integer>();\n            }\n            if (hours == null) {\n                hours = new TreeSet<Integer>();\n            }\n            if (daysOfMonth == null) {\n                daysOfMonth = new TreeSet<Integer>();\n            }\n            if (months == null) {\n                months = new TreeSet<Integer>();\n            }\n            if (daysOfWeek == null) {\n                daysOfWeek = new TreeSet<Integer>();\n            }\n            if (years == null) {\n                years = new TreeSet<Integer>();\n            }\n\n            int exprOn = SECOND;\n\n            StringTokenizer exprsTok = new StringTokenizer(expression, \" \\t\",\n                false);\n\n            while (exprsTok.hasMoreTokens() && exprOn <= YEAR) {\n                String expr = exprsTok.nextToken().trim();\n\n                // throw an exception if L is used with other days of the month\n                if (exprOn == DAY_OF_MONTH && expr.indexOf('L') != -1 && expr.length() > 1 && expr.contains(\",\")) {\n                    throw new ParseException(\"Support for specifying 'L' and 'LW' with other days of the month is not implemented\", -1);\n                }\n                // throw an exception if L is used with other days of the week\n                if (exprOn == DAY_OF_WEEK && expr.indexOf('L') != -1 && expr.length() > 1 && expr.contains(\",\")) {\n                    throw new ParseException(\"Support for specifying 'L' with other days of the week is not implemented\", -1);\n                }\n                if (exprOn == DAY_OF_WEEK && expr.indexOf('#') != -1 && expr.indexOf('#', expr.indexOf('#') + 1) != -1) {\n                    throw new ParseException(\"Support for specifying multiple \\\"nth\\\" days is not implemented.\", -1);\n                }\n\n                StringTokenizer vTok = new StringTokenizer(expr, \",\");\n                while (vTok.hasMoreTokens()) {\n                    String v = vTok.nextToken();\n                    storeExpressionVals(0, v, exprOn);\n                }\n\n                exprOn++;\n            }\n\n            if (exprOn <= DAY_OF_WEEK) {\n                throw new ParseException(\"Unexpected end of expression.\",\n                    expression.length());\n            }\n\n            if (exprOn <= YEAR) {\n                storeExpressionVals(0, \"*\", YEAR);\n            }\n\n            TreeSet<Integer> dow = getSet(DAY_OF_WEEK);\n            TreeSet<Integer> dom = getSet(DAY_OF_MONTH);\n\n            // Copying the logic from the UnsupportedOperationException below\n            boolean dayOfMSpec = !dom.contains(NO_SPEC);\n            boolean dayOfWSpec = !dow.contains(NO_SPEC);\n\n            if (!dayOfMSpec || dayOfWSpec) {\n                if (!dayOfWSpec || dayOfMSpec) {\n                    throw new ParseException(\n                        \"Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.\", 0);\n                }\n            }\n        } catch (ParseException pe) {\n            throw pe;\n        } catch (Exception e) {\n            throw new ParseException(\"Illegal cron expression format (\"\n                + e.toString() + \")\", 0);\n        }\n    }\n\n    protected int storeExpressionVals(int pos, String s, int type)\n        throws ParseException {\n\n        int incr = 0;\n        int i = skipWhiteSpace(pos, s);\n        if (i >= s.length()) {\n            return i;\n        }\n        char c = s.charAt(i);\n        if ((c >= 'A') && (c <= 'Z') && (!s.equals(\"L\")) && (!s.equals(\"LW\")) && (!s.matches(\"^L-[0-9]*[W]?\"))) {\n            String sub = s.substring(i, i + 3);\n            int sval = -1;\n            int eval = -1;\n            if (type == MONTH) {\n                sval = getMonthNumber(sub) + 1;\n                if (sval <= 0) {\n                    throw new ParseException(\"Invalid Month value: '\" + sub + \"'\", i);\n                }\n                if (s.length() > i + 3) {\n                    c = s.charAt(i + 3);\n                    if (c == '-') {\n                        i += 4;\n                        sub = s.substring(i, i + 3);\n                        eval = getMonthNumber(sub) + 1;\n                        if (eval <= 0) {\n                            throw new ParseException(\"Invalid Month value: '\" + sub + \"'\", i);\n                        }\n                    }\n                }\n            } else if (type == DAY_OF_WEEK) {\n                sval = getDayOfWeekNumber(sub);\n                if (sval < 0) {\n                    throw new ParseException(\"Invalid Day-of-Week value: '\"\n                        + sub + \"'\", i);\n                }\n                if (s.length() > i + 3) {\n                    c = s.charAt(i + 3);\n                    if (c == '-') {\n                        i += 4;\n                        sub = s.substring(i, i + 3);\n                        eval = getDayOfWeekNumber(sub);\n                        if (eval < 0) {\n                            throw new ParseException(\n                                \"Invalid Day-of-Week value: '\" + sub\n                                    + \"'\", i);\n                        }\n                    } else if (c == '#') {\n                        try {\n                            i += 4;\n                            nthdayOfWeek = Integer.parseInt(s.substring(i));\n                            if (nthdayOfWeek < 1 || nthdayOfWeek > 5) {\n                                throw new Exception();\n                            }\n                        } catch (Exception e) {\n                            throw new ParseException(\n                                \"A numeric value between 1 and 5 must follow the '#' option\",\n                                i);\n                        }\n                    } else if (c == 'L') {\n                        lastdayOfWeek = true;\n                        i++;\n                    }\n                }\n\n            } else {\n                throw new ParseException(\n                    \"Illegal characters for this position: '\" + sub + \"'\",\n                    i);\n            }\n            if (eval != -1) {\n                incr = 1;\n            }\n            addToSet(sval, eval, incr, type);\n            return (i + 3);\n        }\n\n        if (c == '?') {\n            i++;\n            if ((i + 1) < s.length()\n                && (s.charAt(i) != ' ' && s.charAt(i + 1) != '\\t')) {\n                throw new ParseException(\"Illegal character after '?': \"\n                    + s.charAt(i), i);\n            }\n            if (type != DAY_OF_WEEK && type != DAY_OF_MONTH) {\n                throw new ParseException(\n                    \"'?' can only be specified for Day-of-Month or Day-of-Week.\",\n                    i);\n            }\n            if (type == DAY_OF_WEEK && !lastdayOfMonth) {\n                int val = daysOfMonth.last();\n                if (val == NO_SPEC_INT) {\n                    throw new ParseException(\n                        \"'?' can only be specified for Day-of-Month -OR- Day-of-Week.\",\n                        i);\n                }\n            }\n\n            addToSet(NO_SPEC_INT, -1, 0, type);\n            return i;\n        }\n\n        if (c == '*' || c == '/') {\n            if (c == '*' && (i + 1) >= s.length()) {\n                addToSet(ALL_SPEC_INT, -1, incr, type);\n                return i + 1;\n            } else if (c == '/'\n                && ((i + 1) >= s.length() || s.charAt(i + 1) == ' ' || s\n                .charAt(i + 1) == '\\t')) {\n                throw new ParseException(\"'/' must be followed by an integer.\", i);\n            } else if (c == '*') {\n                i++;\n            }\n            c = s.charAt(i);\n            if (c == '/') { // is an increment specified?\n                i++;\n                if (i >= s.length()) {\n                    throw new ParseException(\"Unexpected end of string.\", i);\n                }\n\n                incr = getNumericValue(s, i);\n\n                i++;\n                if (incr > 10) {\n                    i++;\n                }\n                checkIncrementRange(incr, type, i);\n            } else {\n                incr = 1;\n            }\n\n            addToSet(ALL_SPEC_INT, -1, incr, type);\n            return i;\n        } else if (c == 'L') {\n            i++;\n            if (type == DAY_OF_MONTH) {\n                lastdayOfMonth = true;\n            }\n            if (type == DAY_OF_WEEK) {\n                addToSet(7, 7, 0, type);\n            }\n            if (type == DAY_OF_MONTH && s.length() > i) {\n                c = s.charAt(i);\n                if (c == '-') {\n                    ValueSet vs = getValue(0, s, i + 1);\n                    lastdayOffset = vs.value;\n                    if (lastdayOffset > 30)\n                        throw new ParseException(\"Offset from last day must be <= 30\", i + 1);\n                    i = vs.pos;\n                }\n                if (s.length() > i) {\n                    c = s.charAt(i);\n                    if (c == 'W') {\n                        nearestWeekday = true;\n                        i++;\n                    }\n                }\n            }\n            return i;\n        } else if (c >= '0' && c <= '9') {\n            int val = Integer.parseInt(String.valueOf(c));\n            i++;\n            if (i >= s.length()) {\n                addToSet(val, -1, -1, type);\n            } else {\n                c = s.charAt(i);\n                if (c >= '0' && c <= '9') {\n                    ValueSet vs = getValue(val, s, i);\n                    val = vs.value;\n                    i = vs.pos;\n                }\n                i = checkNext(i, s, val, type);\n                return i;\n            }\n        } else {\n            throw new ParseException(\"Unexpected character: \" + c, i);\n        }\n\n        return i;\n    }\n\n    private void checkIncrementRange(int incr, int type, int idxPos) throws ParseException {\n        if (incr > 59 && (type == SECOND || type == MINUTE)) {\n            throw new ParseException(\"Increment > 60 : \" + incr, idxPos);\n        } else if (incr > 23 && (type == HOUR)) {\n            throw new ParseException(\"Increment > 24 : \" + incr, idxPos);\n        } else if (incr > 31 && (type == DAY_OF_MONTH)) {\n            throw new ParseException(\"Increment > 31 : \" + incr, idxPos);\n        } else if (incr > 7 && (type == DAY_OF_WEEK)) {\n            throw new ParseException(\"Increment > 7 : \" + incr, idxPos);\n        } else if (incr > 12 && (type == MONTH)) {\n            throw new ParseException(\"Increment > 12 : \" + incr, idxPos);\n        }\n    }\n\n    protected int checkNext(int pos, String s, int val, int type)\n        throws ParseException {\n\n        int end = -1;\n        int i = pos;\n\n        if (i >= s.length()) {\n            addToSet(val, end, -1, type);\n            return i;\n        }\n\n        char c = s.charAt(pos);\n\n        if (c == 'L') {\n            if (type == DAY_OF_WEEK) {\n                if (val < 1 || val > 7)\n                    throw new ParseException(\"Day-of-Week values must be between 1 and 7\", -1);\n                lastdayOfWeek = true;\n            } else {\n                throw new ParseException(\"'L' option is not valid here. (pos=\" + i + \")\", i);\n            }\n            TreeSet<Integer> set = getSet(type);\n            set.add(val);\n            i++;\n            return i;\n        }\n\n        if (c == 'W') {\n            if (type == DAY_OF_MONTH) {\n                nearestWeekday = true;\n            } else {\n                throw new ParseException(\"'W' option is not valid here. (pos=\" + i + \")\", i);\n            }\n            if (val > 31)\n                throw new ParseException(\"The 'W' option does not make sense with values larger than 31 (max number of days in a month)\", i);\n            TreeSet<Integer> set = getSet(type);\n            set.add(val);\n            i++;\n            return i;\n        }\n\n        if (c == '#') {\n            if (type != DAY_OF_WEEK) {\n                throw new ParseException(\"'#' option is not valid here. (pos=\" + i + \")\", i);\n            }\n            i++;\n            try {\n                nthdayOfWeek = Integer.parseInt(s.substring(i));\n                if (nthdayOfWeek < 1 || nthdayOfWeek > 5) {\n                    throw new Exception();\n                }\n            } catch (Exception e) {\n                throw new ParseException(\n                    \"A numeric value between 1 and 5 must follow the '#' option\",\n                    i);\n            }\n\n            TreeSet<Integer> set = getSet(type);\n            set.add(val);\n            i++;\n            return i;\n        }\n\n        if (c == '-') {\n            i++;\n            c = s.charAt(i);\n            int v = Integer.parseInt(String.valueOf(c));\n            end = v;\n            i++;\n            if (i >= s.length()) {\n                addToSet(val, end, 1, type);\n                return i;\n            }\n            c = s.charAt(i);\n            if (c >= '0' && c <= '9') {\n                ValueSet vs = getValue(v, s, i);\n                end = vs.value;\n                i = vs.pos;\n            }\n            if (i < s.length() && ((c = s.charAt(i)) == '/')) {\n                i++;\n                c = s.charAt(i);\n                int v2 = Integer.parseInt(String.valueOf(c));\n                i++;\n                if (i >= s.length()) {\n                    addToSet(val, end, v2, type);\n                    return i;\n                }\n                c = s.charAt(i);\n                if (c >= '0' && c <= '9') {\n                    ValueSet vs = getValue(v2, s, i);\n                    int v3 = vs.value;\n                    addToSet(val, end, v3, type);\n                    i = vs.pos;\n                    return i;\n                } else {\n                    addToSet(val, end, v2, type);\n                    return i;\n                }\n            } else {\n                addToSet(val, end, 1, type);\n                return i;\n            }\n        }\n\n        if (c == '/') {\n            if ((i + 1) >= s.length() || s.charAt(i + 1) == ' ' || s.charAt(i + 1) == '\\t') {\n                throw new ParseException(\"'/' must be followed by an integer.\", i);\n            }\n\n            i++;\n            c = s.charAt(i);\n            int v2 = Integer.parseInt(String.valueOf(c));\n            i++;\n            if (i >= s.length()) {\n                checkIncrementRange(v2, type, i);\n                addToSet(val, end, v2, type);\n                return i;\n            }\n            c = s.charAt(i);\n            if (c >= '0' && c <= '9') {\n                ValueSet vs = getValue(v2, s, i);\n                int v3 = vs.value;\n                checkIncrementRange(v3, type, i);\n                addToSet(val, end, v3, type);\n                i = vs.pos;\n                return i;\n            } else {\n                throw new ParseException(\"Unexpected character '\" + c + \"' after '/'\", i);\n            }\n        }\n\n        addToSet(val, end, 0, type);\n        i++;\n        return i;\n    }\n\n    public String getCronExpression() {\n        return cronExpression;\n    }\n\n    public String getExpressionSummary() {\n        StringBuilder buf = new StringBuilder();\n\n        buf.append(\"seconds: \");\n        buf.append(getExpressionSetSummary(seconds));\n        buf.append(\"\\n\");\n        buf.append(\"minutes: \");\n        buf.append(getExpressionSetSummary(minutes));\n        buf.append(\"\\n\");\n        buf.append(\"hours: \");\n        buf.append(getExpressionSetSummary(hours));\n        buf.append(\"\\n\");\n        buf.append(\"daysOfMonth: \");\n        buf.append(getExpressionSetSummary(daysOfMonth));\n        buf.append(\"\\n\");\n        buf.append(\"months: \");\n        buf.append(getExpressionSetSummary(months));\n        buf.append(\"\\n\");\n        buf.append(\"daysOfWeek: \");\n        buf.append(getExpressionSetSummary(daysOfWeek));\n        buf.append(\"\\n\");\n        buf.append(\"lastdayOfWeek: \");\n        buf.append(lastdayOfWeek);\n        buf.append(\"\\n\");\n        buf.append(\"nearestWeekday: \");\n        buf.append(nearestWeekday);\n        buf.append(\"\\n\");\n        buf.append(\"NthDayOfWeek: \");\n        buf.append(nthdayOfWeek);\n        buf.append(\"\\n\");\n        buf.append(\"lastdayOfMonth: \");\n        buf.append(lastdayOfMonth);\n        buf.append(\"\\n\");\n        buf.append(\"years: \");\n        buf.append(getExpressionSetSummary(years));\n        buf.append(\"\\n\");\n\n        return buf.toString();\n    }\n\n    protected String getExpressionSetSummary(java.util.Set<Integer> set) {\n\n        if (set.contains(NO_SPEC)) {\n            return \"?\";\n        }\n        if (set.contains(ALL_SPEC)) {\n            return \"*\";\n        }\n\n        StringBuilder buf = new StringBuilder();\n\n        Iterator<Integer> itr = set.iterator();\n        boolean first = true;\n        while (itr.hasNext()) {\n            Integer iVal = itr.next();\n            String val = iVal.toString();\n            if (!first) {\n                buf.append(\",\");\n            }\n            buf.append(val);\n            first = false;\n        }\n\n        return buf.toString();\n    }\n\n    protected String getExpressionSetSummary(java.util.ArrayList<Integer> list) {\n\n        if (list.contains(NO_SPEC)) {\n            return \"?\";\n        }\n        if (list.contains(ALL_SPEC)) {\n            return \"*\";\n        }\n\n        StringBuilder buf = new StringBuilder();\n\n        Iterator<Integer> itr = list.iterator();\n        boolean first = true;\n        while (itr.hasNext()) {\n            Integer iVal = itr.next();\n            String val = iVal.toString();\n            if (!first) {\n                buf.append(\",\");\n            }\n            buf.append(val);\n            first = false;\n        }\n\n        return buf.toString();\n    }\n\n    protected int skipWhiteSpace(int i, String s) {\n        for (; i < s.length() && (s.charAt(i) == ' ' || s.charAt(i) == '\\t'); i++) {\n        }\n\n        return i;\n    }\n\n    protected int findNextWhiteSpace(int i, String s) {\n        for (; i < s.length() && (s.charAt(i) != ' ' || s.charAt(i) != '\\t'); i++) {\n        }\n\n        return i;\n    }\n\n    protected void addToSet(int val, int end, int incr, int type)\n        throws ParseException {\n\n        TreeSet<Integer> set = getSet(type);\n\n        if (type == SECOND || type == MINUTE) {\n            if ((val < 0 || val > 59 || end > 59) && (val != ALL_SPEC_INT)) {\n                throw new ParseException(\n                    \"Minute and Second values must be between 0 and 59\",\n                    -1);\n            }\n        } else if (type == HOUR) {\n            if ((val < 0 || val > 23 || end > 23) && (val != ALL_SPEC_INT)) {\n                throw new ParseException(\n                    \"Hour values must be between 0 and 23\", -1);\n            }\n        } else if (type == DAY_OF_MONTH) {\n            if ((val < 1 || val > 31 || end > 31) && (val != ALL_SPEC_INT)\n                && (val != NO_SPEC_INT)) {\n                throw new ParseException(\n                    \"Day of month values must be between 1 and 31\", -1);\n            }\n        } else if (type == MONTH) {\n            if ((val < 1 || val > 12 || end > 12) && (val != ALL_SPEC_INT)) {\n                throw new ParseException(\n                    \"Month values must be between 1 and 12\", -1);\n            }\n        } else if (type == DAY_OF_WEEK) {\n            if ((val == 0 || val > 7 || end > 7) && (val != ALL_SPEC_INT)\n                && (val != NO_SPEC_INT)) {\n                throw new ParseException(\n                    \"Day-of-Week values must be between 1 and 7\", -1);\n            }\n        }\n\n        if ((incr == 0 || incr == -1) && val != ALL_SPEC_INT) {\n            if (val != -1) {\n                set.add(val);\n            } else {\n                set.add(NO_SPEC);\n            }\n\n            return;\n        }\n\n        int startAt = val;\n        int stopAt = end;\n\n        if (val == ALL_SPEC_INT && incr <= 0) {\n            incr = 1;\n            set.add(ALL_SPEC); // put in a marker, but also fill values\n        }\n\n        if (type == SECOND || type == MINUTE) {\n            if (stopAt == -1) {\n                stopAt = 59;\n            }\n            if (startAt == -1 || startAt == ALL_SPEC_INT) {\n                startAt = 0;\n            }\n        } else if (type == HOUR) {\n            if (stopAt == -1) {\n                stopAt = 23;\n            }\n            if (startAt == -1 || startAt == ALL_SPEC_INT) {\n                startAt = 0;\n            }\n        } else if (type == DAY_OF_MONTH) {\n            if (stopAt == -1) {\n                stopAt = 31;\n            }\n            if (startAt == -1 || startAt == ALL_SPEC_INT) {\n                startAt = 1;\n            }\n        } else if (type == MONTH) {\n            if (stopAt == -1) {\n                stopAt = 12;\n            }\n            if (startAt == -1 || startAt == ALL_SPEC_INT) {\n                startAt = 1;\n            }\n        } else if (type == DAY_OF_WEEK) {\n            if (stopAt == -1) {\n                stopAt = 7;\n            }\n            if (startAt == -1 || startAt == ALL_SPEC_INT) {\n                startAt = 1;\n            }\n        } else if (type == YEAR) {\n            if (stopAt == -1) {\n                stopAt = MAX_YEAR;\n            }\n            if (startAt == -1 || startAt == ALL_SPEC_INT) {\n                startAt = 1970;\n            }\n        }\n\n        // if the end of the range is before the start, then we need to overflow into\n        // the next day, month etc. This is done by adding the maximum amount for that\n        // type, and using modulus max to determine the value being added.\n        int max = -1;\n        if (stopAt < startAt) {\n            switch (type) {\n                case SECOND:\n                    max = 60;\n                    break;\n                case MINUTE:\n                    max = 60;\n                    break;\n                case HOUR:\n                    max = 24;\n                    break;\n                case MONTH:\n                    max = 12;\n                    break;\n                case DAY_OF_WEEK:\n                    max = 7;\n                    break;\n                case DAY_OF_MONTH:\n                    max = 31;\n                    break;\n                case YEAR:\n                    throw new IllegalArgumentException(\"Start year must be less than stop year\");\n                default:\n                    throw new IllegalArgumentException(\"Unexpected type encountered\");\n            }\n            stopAt += max;\n        }\n\n        for (int i = startAt; i <= stopAt; i += incr) {\n            if (max == -1) {\n                // ie: there's no max to overflow over\n                set.add(i);\n            } else {\n                // take the modulus to get the real value\n                int i2 = i % max;\n\n                // 1-indexed ranges should not include 0, and should include their max\n                if (i2 == 0 && (type == MONTH || type == DAY_OF_WEEK || type == DAY_OF_MONTH)) {\n                    i2 = max;\n                }\n\n                set.add(i2);\n            }\n        }\n    }\n\n    TreeSet<Integer> getSet(int type) {\n        switch (type) {\n            case SECOND:\n                return seconds;\n            case MINUTE:\n                return minutes;\n            case HOUR:\n                return hours;\n            case DAY_OF_MONTH:\n                return daysOfMonth;\n            case MONTH:\n                return months;\n            case DAY_OF_WEEK:\n                return daysOfWeek;\n            case YEAR:\n                return years;\n            default:\n                return null;\n        }\n    }\n\n    protected ValueSet getValue(int v, String s, int i) {\n        char c = s.charAt(i);\n        StringBuilder s1 = new StringBuilder(String.valueOf(v));\n        while (c >= '0' && c <= '9') {\n            s1.append(c);\n            i++;\n            if (i >= s.length()) {\n                break;\n            }\n            c = s.charAt(i);\n        }\n        ValueSet val = new ValueSet();\n\n        val.pos = (i < s.length()) ? i : i + 1;\n        val.value = Integer.parseInt(s1.toString());\n        return val;\n    }\n\n    protected int getNumericValue(String s, int i) {\n        int endOfVal = findNextWhiteSpace(i, s);\n        String val = s.substring(i, endOfVal);\n        return Integer.parseInt(val);\n    }\n\n    protected int getMonthNumber(String s) {\n        Integer integer = monthMap.get(s);\n\n        if (integer == null) {\n            return -1;\n        }\n\n        return integer;\n    }\n\n    protected int getDayOfWeekNumber(String s) {\n        Integer integer = dayMap.get(s);\n\n        if (integer == null) {\n            return -1;\n        }\n\n        return integer;\n    }\n\n    ////////////////////////////////////////////////////////////////////////////\n    //\n    // Computation Functions\n    //\n    ////////////////////////////////////////////////////////////////////////////\n\n    public Date getTimeAfter(Date afterTime) {\n\n        // Computation is based on Gregorian year only.\n        Calendar cl = new java.util.GregorianCalendar(getTimeZone());\n\n        // move ahead one second, since we're computing the time *after* the\n        // given time\n        afterTime = new Date(afterTime.getTime() + 1000);\n        // CronTrigger does not deal with milliseconds\n        cl.setTime(afterTime);\n        cl.set(Calendar.MILLISECOND, 0);\n\n        boolean gotOne = false;\n        // loop until we've computed the next time, or we've past the endTime\n        while (!gotOne) {\n\n            //if (endTime != null && cl.getTime().after(endTime)) return null;\n            if (cl.get(Calendar.YEAR) > 2999) { // prevent endless loop...\n                return null;\n            }\n\n            SortedSet<Integer> st = null;\n            int t = 0;\n\n            int sec = cl.get(Calendar.SECOND);\n            int min = cl.get(Calendar.MINUTE);\n\n            // get second.................................................\n            st = seconds.tailSet(sec);\n            if (st != null && st.size() != 0) {\n                sec = st.first();\n            } else {\n                sec = seconds.first();\n                min++;\n                cl.set(Calendar.MINUTE, min);\n            }\n            cl.set(Calendar.SECOND, sec);\n\n            min = cl.get(Calendar.MINUTE);\n            int hr = cl.get(Calendar.HOUR_OF_DAY);\n            t = -1;\n\n            // get minute.................................................\n            st = minutes.tailSet(min);\n            if (st != null && st.size() != 0) {\n                t = min;\n                min = st.first();\n            } else {\n                min = minutes.first();\n                hr++;\n            }\n            if (min != t) {\n                cl.set(Calendar.SECOND, 0);\n                cl.set(Calendar.MINUTE, min);\n                setCalendarHour(cl, hr);\n                continue;\n            }\n            cl.set(Calendar.MINUTE, min);\n\n            hr = cl.get(Calendar.HOUR_OF_DAY);\n            int day = cl.get(Calendar.DAY_OF_MONTH);\n            t = -1;\n\n            // get hour...................................................\n            st = hours.tailSet(hr);\n            if (st != null && st.size() != 0) {\n                t = hr;\n                hr = st.first();\n            } else {\n                hr = hours.first();\n                day++;\n            }\n            if (hr != t) {\n                cl.set(Calendar.SECOND, 0);\n                cl.set(Calendar.MINUTE, 0);\n                cl.set(Calendar.DAY_OF_MONTH, day);\n                setCalendarHour(cl, hr);\n                continue;\n            }\n            cl.set(Calendar.HOUR_OF_DAY, hr);\n\n            day = cl.get(Calendar.DAY_OF_MONTH);\n            int mon = cl.get(Calendar.MONTH) + 1;\n            // '+ 1' because calendar is 0-based for this field, and we are\n            // 1-based\n            t = -1;\n            int tmon = mon;\n\n            // get day...................................................\n            boolean dayOfMSpec = !daysOfMonth.contains(NO_SPEC);\n            boolean dayOfWSpec = !daysOfWeek.contains(NO_SPEC);\n            if (dayOfMSpec && !dayOfWSpec) { // get day by day of month rule\n                st = daysOfMonth.tailSet(day);\n                if (lastdayOfMonth) {\n                    if (!nearestWeekday) {\n                        t = day;\n                        day = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));\n                        day -= lastdayOffset;\n                        if (t > day) {\n                            mon++;\n                            if (mon > 12) {\n                                mon = 1;\n                                tmon = 3333; // ensure test of mon != tmon further below fails\n                                cl.add(Calendar.YEAR, 1);\n                            }\n                            day = 1;\n                        }\n                    } else {\n                        t = day;\n                        day = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));\n                        day -= lastdayOffset;\n\n                        Calendar tcal = Calendar.getInstance(getTimeZone());\n                        tcal.set(Calendar.SECOND, 0);\n                        tcal.set(Calendar.MINUTE, 0);\n                        tcal.set(Calendar.HOUR_OF_DAY, 0);\n                        tcal.set(Calendar.DAY_OF_MONTH, day);\n                        tcal.set(Calendar.MONTH, mon - 1);\n                        tcal.set(Calendar.YEAR, cl.get(Calendar.YEAR));\n\n                        int ldom = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));\n                        int dow = tcal.get(Calendar.DAY_OF_WEEK);\n\n                        if (dow == Calendar.SATURDAY && day == 1) {\n                            day += 2;\n                        } else if (dow == Calendar.SATURDAY) {\n                            day -= 1;\n                        } else if (dow == Calendar.SUNDAY && day == ldom) {\n                            day -= 2;\n                        } else if (dow == Calendar.SUNDAY) {\n                            day += 1;\n                        }\n\n                        tcal.set(Calendar.SECOND, sec);\n                        tcal.set(Calendar.MINUTE, min);\n                        tcal.set(Calendar.HOUR_OF_DAY, hr);\n                        tcal.set(Calendar.DAY_OF_MONTH, day);\n                        tcal.set(Calendar.MONTH, mon - 1);\n                        Date nTime = tcal.getTime();\n                        if (nTime.before(afterTime)) {\n                            day = 1;\n                            mon++;\n                        }\n                    }\n                } else if (nearestWeekday) {\n                    t = day;\n                    day = daysOfMonth.first();\n\n                    Calendar tcal = Calendar.getInstance(getTimeZone());\n                    tcal.set(Calendar.SECOND, 0);\n                    tcal.set(Calendar.MINUTE, 0);\n                    tcal.set(Calendar.HOUR_OF_DAY, 0);\n                    tcal.set(Calendar.DAY_OF_MONTH, day);\n                    tcal.set(Calendar.MONTH, mon - 1);\n                    tcal.set(Calendar.YEAR, cl.get(Calendar.YEAR));\n\n                    int ldom = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));\n                    int dow = tcal.get(Calendar.DAY_OF_WEEK);\n\n                    if (dow == Calendar.SATURDAY && day == 1) {\n                        day += 2;\n                    } else if (dow == Calendar.SATURDAY) {\n                        day -= 1;\n                    } else if (dow == Calendar.SUNDAY && day == ldom) {\n                        day -= 2;\n                    } else if (dow == Calendar.SUNDAY) {\n                        day += 1;\n                    }\n\n\n                    tcal.set(Calendar.SECOND, sec);\n                    tcal.set(Calendar.MINUTE, min);\n                    tcal.set(Calendar.HOUR_OF_DAY, hr);\n                    tcal.set(Calendar.DAY_OF_MONTH, day);\n                    tcal.set(Calendar.MONTH, mon - 1);\n                    Date nTime = tcal.getTime();\n                    if (nTime.before(afterTime)) {\n                        day = daysOfMonth.first();\n                        mon++;\n                    }\n                } else if (st != null && st.size() != 0) {\n                    t = day;\n                    day = st.first();\n                    // make sure we don't over-run a short month, such as february\n                    int lastDay = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));\n                    if (day > lastDay) {\n                        day = daysOfMonth.first();\n                        mon++;\n                    }\n                } else {\n                    day = daysOfMonth.first();\n                    mon++;\n                }\n\n                if (day != t || mon != tmon) {\n                    cl.set(Calendar.SECOND, 0);\n                    cl.set(Calendar.MINUTE, 0);\n                    cl.set(Calendar.HOUR_OF_DAY, 0);\n                    cl.set(Calendar.DAY_OF_MONTH, day);\n                    cl.set(Calendar.MONTH, mon - 1);\n                    // '- 1' because calendar is 0-based for this field, and we\n                    // are 1-based\n                    continue;\n                }\n            } else if (dayOfWSpec && !dayOfMSpec) { // get day by day of week rule\n                if (lastdayOfWeek) { // are we looking for the last XXX day of\n                    // the month?\n                    int dow = daysOfWeek.first(); // desired\n                    // d-o-w\n                    int cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w\n                    int daysToAdd = 0;\n                    if (cDow < dow) {\n                        daysToAdd = dow - cDow;\n                    }\n                    if (cDow > dow) {\n                        daysToAdd = dow + (7 - cDow);\n                    }\n\n                    int lDay = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));\n\n                    if (day + daysToAdd > lDay) { // did we already miss the\n                        // last one?\n                        cl.set(Calendar.SECOND, 0);\n                        cl.set(Calendar.MINUTE, 0);\n                        cl.set(Calendar.HOUR_OF_DAY, 0);\n                        cl.set(Calendar.DAY_OF_MONTH, 1);\n                        cl.set(Calendar.MONTH, mon);\n                        // no '- 1' here because we are promoting the month\n                        continue;\n                    }\n\n                    // find date of last occurrence of this day in this month...\n                    while ((day + daysToAdd + 7) <= lDay) {\n                        daysToAdd += 7;\n                    }\n\n                    day += daysToAdd;\n\n                    if (daysToAdd > 0) {\n                        cl.set(Calendar.SECOND, 0);\n                        cl.set(Calendar.MINUTE, 0);\n                        cl.set(Calendar.HOUR_OF_DAY, 0);\n                        cl.set(Calendar.DAY_OF_MONTH, day);\n                        cl.set(Calendar.MONTH, mon - 1);\n                        // '- 1' here because we are not promoting the month\n                        continue;\n                    }\n\n                } else if (nthdayOfWeek != 0) {\n                    // are we looking for the Nth XXX day in the month?\n                    int dow = daysOfWeek.first(); // desired\n                    // d-o-w\n                    int cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w\n                    int daysToAdd = 0;\n                    if (cDow < dow) {\n                        daysToAdd = dow - cDow;\n                    } else if (cDow > dow) {\n                        daysToAdd = dow + (7 - cDow);\n                    }\n\n                    boolean dayShifted = false;\n                    if (daysToAdd > 0) {\n                        dayShifted = true;\n                    }\n\n                    day += daysToAdd;\n                    int weekOfMonth = day / 7;\n                    if (day % 7 > 0) {\n                        weekOfMonth++;\n                    }\n\n                    daysToAdd = (nthdayOfWeek - weekOfMonth) * 7;\n                    day += daysToAdd;\n                    if (daysToAdd < 0\n                        || day > getLastDayOfMonth(mon, cl\n                        .get(Calendar.YEAR))) {\n                        cl.set(Calendar.SECOND, 0);\n                        cl.set(Calendar.MINUTE, 0);\n                        cl.set(Calendar.HOUR_OF_DAY, 0);\n                        cl.set(Calendar.DAY_OF_MONTH, 1);\n                        cl.set(Calendar.MONTH, mon);\n                        // no '- 1' here because we are promoting the month\n                        continue;\n                    } else if (daysToAdd > 0 || dayShifted) {\n                        cl.set(Calendar.SECOND, 0);\n                        cl.set(Calendar.MINUTE, 0);\n                        cl.set(Calendar.HOUR_OF_DAY, 0);\n                        cl.set(Calendar.DAY_OF_MONTH, day);\n                        cl.set(Calendar.MONTH, mon - 1);\n                        // '- 1' here because we are NOT promoting the month\n                        continue;\n                    }\n                } else {\n                    int cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w\n                    int dow = daysOfWeek.first(); // desired\n                    // d-o-w\n                    st = daysOfWeek.tailSet(cDow);\n                    if (st != null && st.size() > 0) {\n                        dow = st.first();\n                    }\n\n                    int daysToAdd = 0;\n                    if (cDow < dow) {\n                        daysToAdd = dow - cDow;\n                    }\n                    if (cDow > dow) {\n                        daysToAdd = dow + (7 - cDow);\n                    }\n\n                    int lDay = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));\n\n                    if (day + daysToAdd > lDay) { // will we pass the end of\n                        // the month?\n                        cl.set(Calendar.SECOND, 0);\n                        cl.set(Calendar.MINUTE, 0);\n                        cl.set(Calendar.HOUR_OF_DAY, 0);\n                        cl.set(Calendar.DAY_OF_MONTH, 1);\n                        cl.set(Calendar.MONTH, mon);\n                        // no '- 1' here because we are promoting the month\n                        continue;\n                    } else if (daysToAdd > 0) { // are we swithing days?\n                        cl.set(Calendar.SECOND, 0);\n                        cl.set(Calendar.MINUTE, 0);\n                        cl.set(Calendar.HOUR_OF_DAY, 0);\n                        cl.set(Calendar.DAY_OF_MONTH, day + daysToAdd);\n                        cl.set(Calendar.MONTH, mon - 1);\n                        // '- 1' because calendar is 0-based for this field,\n                        // and we are 1-based\n                        continue;\n                    }\n                }\n            } else { // dayOfWSpec && !dayOfMSpec\n                throw new UnsupportedOperationException(\n                    \"Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.\");\n            }\n            cl.set(Calendar.DAY_OF_MONTH, day);\n\n            mon = cl.get(Calendar.MONTH) + 1;\n            // '+ 1' because calendar is 0-based for this field, and we are\n            // 1-based\n            int year = cl.get(Calendar.YEAR);\n            t = -1;\n\n            // test for expressions that never generate a valid fire date,\n            // but keep looping...\n            if (year > MAX_YEAR) {\n                return null;\n            }\n\n            // get month...................................................\n            st = months.tailSet(mon);\n            if (st != null && st.size() != 0) {\n                t = mon;\n                mon = st.first();\n            } else {\n                mon = months.first();\n                year++;\n            }\n            if (mon != t) {\n                cl.set(Calendar.SECOND, 0);\n                cl.set(Calendar.MINUTE, 0);\n                cl.set(Calendar.HOUR_OF_DAY, 0);\n                cl.set(Calendar.DAY_OF_MONTH, 1);\n                cl.set(Calendar.MONTH, mon - 1);\n                // '- 1' because calendar is 0-based for this field, and we are\n                // 1-based\n                cl.set(Calendar.YEAR, year);\n                continue;\n            }\n            cl.set(Calendar.MONTH, mon - 1);\n            // '- 1' because calendar is 0-based for this field, and we are\n            // 1-based\n\n            year = cl.get(Calendar.YEAR);\n            t = -1;\n\n            // get year...................................................\n            st = years.tailSet(year);\n            if (st != null && st.size() != 0) {\n                t = year;\n                year = st.first();\n            } else {\n                return null; // ran out of years...\n            }\n\n            if (year != t) {\n                cl.set(Calendar.SECOND, 0);\n                cl.set(Calendar.MINUTE, 0);\n                cl.set(Calendar.HOUR_OF_DAY, 0);\n                cl.set(Calendar.DAY_OF_MONTH, 1);\n                cl.set(Calendar.MONTH, 0);\n                // '- 1' because calendar is 0-based for this field, and we are\n                // 1-based\n                cl.set(Calendar.YEAR, year);\n                continue;\n            }\n            cl.set(Calendar.YEAR, year);\n\n            gotOne = true;\n        } // while( !done )\n\n        return cl.getTime();\n    }\n\n    /**\n     * Advance the calendar to the particular hour paying particular attention\n     * to daylight saving problems.\n     *\n     * @param cal  the calendar to operate on\n     * @param hour the hour to set\n     */\n    protected void setCalendarHour(Calendar cal, int hour) {\n        cal.set(Calendar.HOUR_OF_DAY, hour);\n        if (cal.get(Calendar.HOUR_OF_DAY) != hour && hour != 24) {\n            cal.set(Calendar.HOUR_OF_DAY, hour + 1);\n        }\n    }\n\n    /**\n     * NOT YET IMPLEMENTED: Returns the time before the given time\n     * that the <code>CronExpression</code> matches.\n     */\n    public Date getTimeBefore(Date endTime) {\n        // FUTURE_TODO: implement QUARTZ-423\n        return null;\n    }\n\n    /**\n     * NOT YET IMPLEMENTED: Returns the final time that the\n     * <code>CronExpression</code> will match.\n     */\n    public Date getFinalFireTime() {\n        // FUTURE_TODO: implement QUARTZ-423\n        return null;\n    }\n\n    protected boolean isLeapYear(int year) {\n        return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));\n    }\n\n    protected int getLastDayOfMonth(int monthNum, int year) {\n\n        switch (monthNum) {\n            case 1:\n                return 31;\n            case 2:\n                return (isLeapYear(year)) ? 29 : 28;\n            case 3:\n                return 31;\n            case 4:\n                return 30;\n            case 5:\n                return 31;\n            case 6:\n                return 30;\n            case 7:\n                return 31;\n            case 8:\n                return 31;\n            case 9:\n                return 30;\n            case 10:\n                return 31;\n            case 11:\n                return 30;\n            case 12:\n                return 31;\n            default:\n                throw new IllegalArgumentException(\"Illegal month number: \"\n                    + monthNum);\n        }\n    }\n\n\n    private void readObject(java.io.ObjectInputStream stream)\n        throws java.io.IOException, ClassNotFoundException {\n\n        stream.defaultReadObject();\n        try {\n            buildExpression(cronExpression);\n        } catch (Exception ignore) {\n        } // never happens\n    }\n\n    @Override\n    @Deprecated\n    public Object clone() {\n        return new CronExpression(this);\n    }\n}\n\nclass ValueSet {\n    public int value;\n\n    public int pos;\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/exception/XxlJobException.java",
    "content": "package com.xxl.job.admin.core.exception;\n\n/**\n * @author xuxueli 2019-05-04 23:19:29\n */\npublic class XxlJobException extends RuntimeException {\n\n    public XxlJobException() {\n    }\n\n    public XxlJobException(String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobGroup.java",
    "content": "package com.xxl.job.admin.core.model;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * Created by xuxueli on 16/9/30.\n */\npublic class XxlJobGroup {\n\n    private int id;\n    private String appname;\n    private String title;\n    private int addressType;        // 执行器地址类型：0=自动注册、1=手动录入\n    private String addressList;     // 执行器地址列表，多地址逗号分隔(手动录入)\n    private Date updateTime;\n\n    // registry list\n    private List<String> registryList;  // 执行器地址列表(系统注册)\n\n    public List<String> getRegistryList() {\n        if (addressList != null && addressList.trim().length() > 0) {\n            registryList = new ArrayList<String>(Arrays.asList(addressList.split(\",\")));\n        }\n        return registryList;\n    }\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n\n    public String getAppname() {\n        return appname;\n    }\n\n    public void setAppname(String appname) {\n        this.appname = appname;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public void setTitle(String title) {\n        this.title = title;\n    }\n\n    public int getAddressType() {\n        return addressType;\n    }\n\n    public void setAddressType(int addressType) {\n        this.addressType = addressType;\n    }\n\n    public String getAddressList() {\n        return addressList;\n    }\n\n    public Date getUpdateTime() {\n        return updateTime;\n    }\n\n    public void setUpdateTime(Date updateTime) {\n        this.updateTime = updateTime;\n    }\n\n    public void setAddressList(String addressList) {\n        this.addressList = addressList;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobInfo.java",
    "content": "package com.xxl.job.admin.core.model;\n\nimport java.util.Date;\n\n/**\n * xxl-job info\n *\n * @author xuxueli  2016-1-12 18:25:49\n */\npublic class XxlJobInfo {\n\n    private int id;                // 主键ID\n\n    private int jobGroup;        // 执行器主键ID\n    private String jobDesc;\n\n    private Date addTime;\n    private Date updateTime;\n\n    private String author;        // 负责人\n    private String alarmEmail;    // 报警邮件\n\n    private String scheduleType;            // 调度类型\n    private String scheduleConf;            // 调度配置，值含义取决于调度类型\n    private String misfireStrategy;            // 调度过期策略\n\n    private String executorRouteStrategy;    // 执行器路由策略\n    private String executorHandler;            // 执行器，任务Handler名称\n    private String executorParam;            // 执行器，任务参数\n    private String executorBlockStrategy;    // 阻塞处理策略\n    private int executorTimeout;            // 任务执行超时时间，单位秒\n    private int executorFailRetryCount;        // 失败重试次数\n\n    private String glueType;        // GLUE类型\t#com.xxl.job.core.glue.GlueTypeEnum\n    private String glueSource;        // GLUE源代码\n    private String glueRemark;        // GLUE备注\n    private Date glueUpdatetime;    // GLUE更新时间\n\n    private String childJobId;        // 子任务ID，多个逗号分隔\n\n    private int triggerStatus;        // 调度状态：0-停止，1-运行\n    private long triggerLastTime;    // 上次调度时间\n    private long triggerNextTime;    // 下次调度时间\n\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n\n    public int getJobGroup() {\n        return jobGroup;\n    }\n\n    public void setJobGroup(int jobGroup) {\n        this.jobGroup = jobGroup;\n    }\n\n    public String getJobDesc() {\n        return jobDesc;\n    }\n\n    public void setJobDesc(String jobDesc) {\n        this.jobDesc = jobDesc;\n    }\n\n    public Date getAddTime() {\n        return addTime;\n    }\n\n    public void setAddTime(Date addTime) {\n        this.addTime = addTime;\n    }\n\n    public Date getUpdateTime() {\n        return updateTime;\n    }\n\n    public void setUpdateTime(Date updateTime) {\n        this.updateTime = updateTime;\n    }\n\n    public String getAuthor() {\n        return author;\n    }\n\n    public void setAuthor(String author) {\n        this.author = author;\n    }\n\n    public String getAlarmEmail() {\n        return alarmEmail;\n    }\n\n    public void setAlarmEmail(String alarmEmail) {\n        this.alarmEmail = alarmEmail;\n    }\n\n    public String getScheduleType() {\n        return scheduleType;\n    }\n\n    public void setScheduleType(String scheduleType) {\n        this.scheduleType = scheduleType;\n    }\n\n    public String getScheduleConf() {\n        return scheduleConf;\n    }\n\n    public void setScheduleConf(String scheduleConf) {\n        this.scheduleConf = scheduleConf;\n    }\n\n    public String getMisfireStrategy() {\n        return misfireStrategy;\n    }\n\n    public void setMisfireStrategy(String misfireStrategy) {\n        this.misfireStrategy = misfireStrategy;\n    }\n\n    public String getExecutorRouteStrategy() {\n        return executorRouteStrategy;\n    }\n\n    public void setExecutorRouteStrategy(String executorRouteStrategy) {\n        this.executorRouteStrategy = executorRouteStrategy;\n    }\n\n    public String getExecutorHandler() {\n        return executorHandler;\n    }\n\n    public void setExecutorHandler(String executorHandler) {\n        this.executorHandler = executorHandler;\n    }\n\n    public String getExecutorParam() {\n        return executorParam;\n    }\n\n    public void setExecutorParam(String executorParam) {\n        this.executorParam = executorParam;\n    }\n\n    public String getExecutorBlockStrategy() {\n        return executorBlockStrategy;\n    }\n\n    public void setExecutorBlockStrategy(String executorBlockStrategy) {\n        this.executorBlockStrategy = executorBlockStrategy;\n    }\n\n    public int getExecutorTimeout() {\n        return executorTimeout;\n    }\n\n    public void setExecutorTimeout(int executorTimeout) {\n        this.executorTimeout = executorTimeout;\n    }\n\n    public int getExecutorFailRetryCount() {\n        return executorFailRetryCount;\n    }\n\n    public void setExecutorFailRetryCount(int executorFailRetryCount) {\n        this.executorFailRetryCount = executorFailRetryCount;\n    }\n\n    public String getGlueType() {\n        return glueType;\n    }\n\n    public void setGlueType(String glueType) {\n        this.glueType = glueType;\n    }\n\n    public String getGlueSource() {\n        return glueSource;\n    }\n\n    public void setGlueSource(String glueSource) {\n        this.glueSource = glueSource;\n    }\n\n    public String getGlueRemark() {\n        return glueRemark;\n    }\n\n    public void setGlueRemark(String glueRemark) {\n        this.glueRemark = glueRemark;\n    }\n\n    public Date getGlueUpdatetime() {\n        return glueUpdatetime;\n    }\n\n    public void setGlueUpdatetime(Date glueUpdatetime) {\n        this.glueUpdatetime = glueUpdatetime;\n    }\n\n    public String getChildJobId() {\n        return childJobId;\n    }\n\n    public void setChildJobId(String childJobId) {\n        this.childJobId = childJobId;\n    }\n\n    public int getTriggerStatus() {\n        return triggerStatus;\n    }\n\n    public void setTriggerStatus(int triggerStatus) {\n        this.triggerStatus = triggerStatus;\n    }\n\n    public long getTriggerLastTime() {\n        return triggerLastTime;\n    }\n\n    public void setTriggerLastTime(long triggerLastTime) {\n        this.triggerLastTime = triggerLastTime;\n    }\n\n    public long getTriggerNextTime() {\n        return triggerNextTime;\n    }\n\n    public void setTriggerNextTime(long triggerNextTime) {\n        this.triggerNextTime = triggerNextTime;\n    }\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobLog.java",
    "content": "package com.xxl.job.admin.core.model;\n\nimport java.util.Date;\n\n/**\n * xxl-job log, used to track trigger process\n *\n * @author xuxueli  2015-12-19 23:19:09\n */\npublic class XxlJobLog {\n\n    private long id;\n\n    // job info\n    private int jobGroup;\n    private int jobId;\n\n    // execute info\n    private String executorAddress;\n    private String executorHandler;\n    private String executorParam;\n    private String executorShardingParam;\n    private int executorFailRetryCount;\n\n    // trigger info\n    private Date triggerTime;\n    private int triggerCode;\n    private String triggerMsg;\n\n    // handle info\n    private Date handleTime;\n    private int handleCode;\n    private String handleMsg;\n\n    // alarm info\n    private int alarmStatus;\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public int getJobGroup() {\n        return jobGroup;\n    }\n\n    public void setJobGroup(int jobGroup) {\n        this.jobGroup = jobGroup;\n    }\n\n    public int getJobId() {\n        return jobId;\n    }\n\n    public void setJobId(int jobId) {\n        this.jobId = jobId;\n    }\n\n    public String getExecutorAddress() {\n        return executorAddress;\n    }\n\n    public void setExecutorAddress(String executorAddress) {\n        this.executorAddress = executorAddress;\n    }\n\n    public String getExecutorHandler() {\n        return executorHandler;\n    }\n\n    public void setExecutorHandler(String executorHandler) {\n        this.executorHandler = executorHandler;\n    }\n\n    public String getExecutorParam() {\n        return executorParam;\n    }\n\n    public void setExecutorParam(String executorParam) {\n        this.executorParam = executorParam;\n    }\n\n    public String getExecutorShardingParam() {\n        return executorShardingParam;\n    }\n\n    public void setExecutorShardingParam(String executorShardingParam) {\n        this.executorShardingParam = executorShardingParam;\n    }\n\n    public int getExecutorFailRetryCount() {\n        return executorFailRetryCount;\n    }\n\n    public void setExecutorFailRetryCount(int executorFailRetryCount) {\n        this.executorFailRetryCount = executorFailRetryCount;\n    }\n\n    public Date getTriggerTime() {\n        return triggerTime;\n    }\n\n    public void setTriggerTime(Date triggerTime) {\n        this.triggerTime = triggerTime;\n    }\n\n    public int getTriggerCode() {\n        return triggerCode;\n    }\n\n    public void setTriggerCode(int triggerCode) {\n        this.triggerCode = triggerCode;\n    }\n\n    public String getTriggerMsg() {\n        return triggerMsg;\n    }\n\n    public void setTriggerMsg(String triggerMsg) {\n        this.triggerMsg = triggerMsg;\n    }\n\n    public Date getHandleTime() {\n        return handleTime;\n    }\n\n    public void setHandleTime(Date handleTime) {\n        this.handleTime = handleTime;\n    }\n\n    public int getHandleCode() {\n        return handleCode;\n    }\n\n    public void setHandleCode(int handleCode) {\n        this.handleCode = handleCode;\n    }\n\n    public String getHandleMsg() {\n        return handleMsg;\n    }\n\n    public void setHandleMsg(String handleMsg) {\n        this.handleMsg = handleMsg;\n    }\n\n    public int getAlarmStatus() {\n        return alarmStatus;\n    }\n\n    public void setAlarmStatus(int alarmStatus) {\n        this.alarmStatus = alarmStatus;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobLogGlue.java",
    "content": "package com.xxl.job.admin.core.model;\n\nimport java.util.Date;\n\n/**\n * xxl-job log for glue, used to track job code process\n *\n * @author xuxueli 2016-5-19 17:57:46\n */\npublic class XxlJobLogGlue {\n\n    private int id;\n    private int jobId;                // 任务主键ID\n    private String glueType;        // GLUE类型\t#com.xxl.job.core.glue.GlueTypeEnum\n    private String glueSource;\n    private String glueRemark;\n    private Date addTime;\n    private Date updateTime;\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n\n    public int getJobId() {\n        return jobId;\n    }\n\n    public void setJobId(int jobId) {\n        this.jobId = jobId;\n    }\n\n    public String getGlueType() {\n        return glueType;\n    }\n\n    public void setGlueType(String glueType) {\n        this.glueType = glueType;\n    }\n\n    public String getGlueSource() {\n        return glueSource;\n    }\n\n    public void setGlueSource(String glueSource) {\n        this.glueSource = glueSource;\n    }\n\n    public String getGlueRemark() {\n        return glueRemark;\n    }\n\n    public void setGlueRemark(String glueRemark) {\n        this.glueRemark = glueRemark;\n    }\n\n    public Date getAddTime() {\n        return addTime;\n    }\n\n    public void setAddTime(Date addTime) {\n        this.addTime = addTime;\n    }\n\n    public Date getUpdateTime() {\n        return updateTime;\n    }\n\n    public void setUpdateTime(Date updateTime) {\n        this.updateTime = updateTime;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobLogReport.java",
    "content": "package com.xxl.job.admin.core.model;\n\nimport java.util.Date;\n\npublic class XxlJobLogReport {\n\n    private int id;\n\n    private Date triggerDay;\n\n    private int runningCount;\n    private int sucCount;\n    private int failCount;\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n\n    public Date getTriggerDay() {\n        return triggerDay;\n    }\n\n    public void setTriggerDay(Date triggerDay) {\n        this.triggerDay = triggerDay;\n    }\n\n    public int getRunningCount() {\n        return runningCount;\n    }\n\n    public void setRunningCount(int runningCount) {\n        this.runningCount = runningCount;\n    }\n\n    public int getSucCount() {\n        return sucCount;\n    }\n\n    public void setSucCount(int sucCount) {\n        this.sucCount = sucCount;\n    }\n\n    public int getFailCount() {\n        return failCount;\n    }\n\n    public void setFailCount(int failCount) {\n        this.failCount = failCount;\n    }\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobRegistry.java",
    "content": "package com.xxl.job.admin.core.model;\n\nimport java.util.Date;\n\n/**\n * Created by xuxueli on 16/9/30.\n */\npublic class XxlJobRegistry {\n\n    private int id;\n    private String registryGroup;\n    private String registryKey;\n    private String registryValue;\n    private Date updateTime;\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n\n    public String getRegistryGroup() {\n        return registryGroup;\n    }\n\n    public void setRegistryGroup(String registryGroup) {\n        this.registryGroup = registryGroup;\n    }\n\n    public String getRegistryKey() {\n        return registryKey;\n    }\n\n    public void setRegistryKey(String registryKey) {\n        this.registryKey = registryKey;\n    }\n\n    public String getRegistryValue() {\n        return registryValue;\n    }\n\n    public void setRegistryValue(String registryValue) {\n        this.registryValue = registryValue;\n    }\n\n    public Date getUpdateTime() {\n        return updateTime;\n    }\n\n    public void setUpdateTime(Date updateTime) {\n        this.updateTime = updateTime;\n    }\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobUser.java",
    "content": "package com.xxl.job.admin.core.model;\n\nimport org.springframework.util.StringUtils;\n\n/**\n * @author xuxueli 2019-05-04 16:43:12\n */\npublic class XxlJobUser {\n\n    private int id;\n    private String username;        // 账号\n    private String password;        // 密码\n    private int role;                // 角色：0-普通用户、1-管理员\n    private String permission;    // 权限：执行器ID列表，多个逗号分割\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public int getRole() {\n        return role;\n    }\n\n    public void setRole(int role) {\n        this.role = role;\n    }\n\n    public String getPermission() {\n        return permission;\n    }\n\n    public void setPermission(String permission) {\n        this.permission = permission;\n    }\n\n    // plugin\n    public boolean validPermission(int jobGroup) {\n        if (this.role == 1) {\n            return true;\n        } else {\n            if (StringUtils.hasText(this.permission)) {\n                for (String permissionItem : this.permission.split(\",\")) {\n                    if (String.valueOf(jobGroup).equals(permissionItem)) {\n                        return true;\n                    }\n                }\n            }\n            return false;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/old/RemoteHttpJobBean.java",
    "content": "package com.xxl.job.admin.core.old;//package com.xxl.job.admin.core.jobbean;\n//\n//import com.xxl.job.admin.core.thread.JobTriggerPoolHelper;\n//import com.xxl.job.admin.core.trigger.TriggerTypeEnum;\n//import org.quartz.JobExecutionContext;\n//import org.quartz.JobExecutionException;\n//import org.quartz.JobKey;\n//import org.slf4j.Logger;\n//import org.slf4j.LoggerFactory;\n//import org.springframework.scheduling.quartz.QuartzJobBean;\n//\n///**\n// * http job bean\n// * “@DisallowConcurrentExecution” disable concurrent, thread size can not be only one, better given more\n// * @author xuxueli 2015-12-17 18:20:34\n// */\n////@DisallowConcurrentExecution\n//public class RemoteHttpJobBean extends QuartzJobBean {\n//\tprivate static Logger logger = LoggerFactory.getLogger(RemoteHttpJobBean.class);\n//\n//\t@Override\n//\tprotected void executeInternal(JobExecutionContext context)\n//\t\t\tthrows JobExecutionException {\n//\n//\t\t// load jobId\n//\t\tJobKey jobKey = context.getTrigger().getJobKey();\n//\t\tInteger jobId = Integer.valueOf(jobKey.getName());\n//\n//\n//\t}\n//\n//}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/old/XxlJobDynamicScheduler.java",
    "content": "package com.xxl.job.admin.core.old;//package com.xxl.job.admin.core.schedule;\n//\n//import com.xxl.job.admin.core.conf.XxlJobAdminConfig;\n//import com.xxl.job.admin.core.jobbean.RemoteHttpJobBean;\n//import com.xxl.job.admin.core.model.XxlJobInfo;\n//import com.xxl.job.admin.core.thread.JobFailMonitorHelper;\n//import com.xxl.job.admin.core.thread.JobRegistryMonitorHelper;\n//import com.xxl.job.admin.core.thread.JobTriggerPoolHelper;\n//import com.xxl.job.admin.core.util.I18nUtil;\n//import com.xxl.job.core.biz.AdminBiz;\n//import com.xxl.job.core.biz.ExecutorBiz;\n//import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;\n//import com.xxl.rpc.remoting.invoker.XxlRpcInvokerFactory;\n//import com.xxl.rpc.remoting.invoker.call.CallType;\n//import com.xxl.rpc.remoting.invoker.reference.XxlRpcReferenceBean;\n//import com.xxl.rpc.remoting.invoker.route.LoadBalance;\n//import com.xxl.rpc.remoting.net.NetEnum;\n//import com.xxl.rpc.remoting.net.impl.servlet.server.ServletServerHandler;\n//import com.xxl.rpc.remoting.provider.XxlRpcProviderFactory;\n//import com.xxl.rpc.serialize.Serializer;\n//import org.quartz.*;\n//import org.quartz.Trigger.TriggerState;\n//import org.quartz.impl.triggers.CronTriggerImpl;\n//import org.slf4j.Logger;\n//import org.slf4j.LoggerFactory;\n//import org.springframework.util.Assert;\n//\n//import javax.servlet.ServletException;\n//import javax.servlet.http.HttpServletRequest;\n//import javax.servlet.http.HttpServletResponse;\n//import java.io.IOException;\n//import java.util.Date;\n//import java.util.concurrent.ConcurrentHashMap;\n//\n///**\n// * base quartz scheduler util\n// * @author xuxueli 2015-12-19 16:13:53\n// */\n//public final class XxlJobDynamicScheduler {\n//    private static final Logger logger = LoggerFactory.getLogger(XxlJobDynamicScheduler_old.class);\n//\n//    // ---------------------- param ----------------------\n//\n//    // scheduler\n//    private static Scheduler scheduler;\n//    public void setScheduler(Scheduler scheduler) {\n//\t\tXxlJobDynamicScheduler_old.scheduler = scheduler;\n//\t}\n//\n//\n//    // ---------------------- init + destroy ----------------------\n//    public void start() throws Exception {\n//        // valid\n//        Assert.notNull(scheduler, \"quartz scheduler is null\");\n//\n//        // init i18n\n//        initI18n();\n//\n//        // admin registry monitor run\n//        JobRegistryMonitorHelper.getInstance().start();\n//\n//        // admin monitor run\n//        JobFailMonitorHelper.getInstance().start();\n//\n//        // admin-server\n//        initRpcProvider();\n//\n//        logger.info(\">>>>>>>>> init xxl-job admin success.\");\n//    }\n//\n//\n//    public void destroy() throws Exception {\n//        // admin trigger pool stop\n//        JobTriggerPoolHelper.toStop();\n//\n//        // admin registry stop\n//        JobRegistryMonitorHelper.getInstance().toStop();\n//\n//        // admin monitor stop\n//        JobFailMonitorHelper.getInstance().toStop();\n//\n//        // admin-server\n//        stopRpcProvider();\n//    }\n//\n//\n//    // ---------------------- I18n ----------------------\n//\n//    private void initI18n(){\n//        for (ExecutorBlockStrategyEnum item:ExecutorBlockStrategyEnum.values()) {\n//            item.setTitle(I18nUtil.getString(\"jobconf_block_\".concat(item.name())));\n//        }\n//    }\n//\n//\n//    // ---------------------- admin rpc provider (no server version) ----------------------\n//    private static ServletServerHandler servletServerHandler;\n//    private void initRpcProvider(){\n//        // init\n//        XxlRpcProviderFactory xxlRpcProviderFactory = new XxlRpcProviderFactory();\n//        xxlRpcProviderFactory.initConfig(\n//                NetEnum.NETTY_HTTP,\n//                Serializer.SerializeEnum.HESSIAN.getSerializer(),\n//                null,\n//                0,\n//                XxlJobAdminConfig.getAdminConfig().getAccessToken(),\n//                null,\n//                null);\n//\n//        // add services\n//        xxlRpcProviderFactory.addService(AdminBiz.class.getName(), null, XxlJobAdminConfig.getAdminConfig().getAdminBiz());\n//\n//        // servlet handler\n//        servletServerHandler = new ServletServerHandler(xxlRpcProviderFactory);\n//    }\n//    private void stopRpcProvider() throws Exception {\n//        XxlRpcInvokerFactory.getInstance().stop();\n//    }\n//    public static void invokeAdminService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {\n//        servletServerHandler.handle(null, request, response);\n//    }\n//\n//\n//    // ---------------------- executor-client ----------------------\n//    private static ConcurrentHashMap<String, ExecutorBiz> executorBizRepository = new ConcurrentHashMap<String, ExecutorBiz>();\n//    public static ExecutorBiz getExecutorBiz(String address) throws Exception {\n//        // valid\n//        if (address==null || address.trim().length()==0) {\n//            return null;\n//        }\n//\n//        // load-cache\n//        address = address.trim();\n//        ExecutorBiz executorBiz = executorBizRepository.get(address);\n//        if (executorBiz != null) {\n//            return executorBiz;\n//        }\n//\n//        // set-cache\n//        executorBiz = (ExecutorBiz) new XxlRpcReferenceBean(\n//                NetEnum.NETTY_HTTP,\n//                Serializer.SerializeEnum.HESSIAN.getSerializer(),\n//                CallType.SYNC,\n//                LoadBalance.ROUND,\n//                ExecutorBiz.class,\n//                null,\n//                5000,\n//                address,\n//                XxlJobAdminConfig.getAdminConfig().getAccessToken(),\n//                null,\n//                null).getObject();\n//\n//        executorBizRepository.put(address, executorBiz);\n//        return executorBiz;\n//    }\n//\n//\n//    // ---------------------- schedule util ----------------------\n//\n//    /**\n//     * fill job info\n//     *\n//     * @param jobInfo\n//     */\n//\tpublic static void fillJobInfo(XxlJobInfo jobInfo) {\n//\n//        String name = String.valueOf(jobInfo.getId());\n//\n//        // trigger key\n//        TriggerKey triggerKey = TriggerKey.triggerKey(name);\n//        try {\n//\n//            // trigger cron\n//\t\t\tTrigger trigger = scheduler.getTrigger(triggerKey);\n//\t\t\tif (trigger!=null && trigger instanceof CronTriggerImpl) {\n//\t\t\t\tString cronExpression = ((CronTriggerImpl) trigger).getCronExpression();\n//\t\t\t\tjobInfo.setJobCron(cronExpression);\n//\t\t\t}\n//\n//            // trigger state\n//            TriggerState triggerState = scheduler.getTriggerState(triggerKey);\n//\t\t\tif (triggerState!=null) {\n//\t\t\t\tjobInfo.setJobStatus(triggerState.name());\n//\t\t\t}\n//\n//            //JobKey jobKey = new JobKey(jobInfo.getJobName(), String.valueOf(jobInfo.getJobGroup()));\n//            //JobDetail jobDetail = scheduler.getJobDetail(jobKey);\n//            //String jobClass = jobDetail.getJobClass().getName();\n//\n//\t\t} catch (SchedulerException e) {\n//\t\t\tlogger.error(e.getMessage(), e);\n//\t\t}\n//\t}\n//\n//\n//    /**\n//     * add trigger + job\n//     *\n//     * @param jobName\n//     * @param cronExpression\n//     * @return\n//     * @throws SchedulerException\n//     */\n//\tpublic static boolean addJob(String jobName, String cronExpression) throws SchedulerException {\n//    \t// 1、job key\n//        TriggerKey triggerKey = TriggerKey.triggerKey(jobName);\n//        JobKey jobKey = new JobKey(jobName);\n//\n//        // 2、valid\n//        if (scheduler.checkExists(triggerKey)) {\n//            return true;    // PASS\n//        }\n//\n//        // 3、corn trigger\n//        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();   // withMisfireHandlingInstructionDoNothing 忽略掉调度终止过程中忽略的调度\n//        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();\n//\n//        // 4、job detail\n//\t\tClass<? extends Job> jobClass_ = RemoteHttpJobBean.class;   // Class.forName(jobInfo.getJobClass());\n//\t\tJobDetail jobDetail = JobBuilder.newJob(jobClass_).withIdentity(jobKey).build();\n//\n//        /*if (jobInfo.getJobData()!=null) {\n//        \tJobDataMap jobDataMap = jobDetail.getJobDataMap();\n//        \tjobDataMap.putAll(JacksonUtil.readValue(jobInfo.getJobData(), Map.class));\n//        \t// JobExecutionContext context.getMergedJobDataMap().get(\"mailGuid\");\n//\t\t}*/\n//\n//        // 5、schedule job\n//        Date date = scheduler.scheduleJob(jobDetail, cronTrigger);\n//\n//        logger.info(\">>>>>>>>>>> addJob success(quartz), jobDetail:{}, cronTrigger:{}, date:{}\", jobDetail, cronTrigger, date);\n//        return true;\n//    }\n//\n//\n//    /**\n//     * remove trigger + job\n//     *\n//     * @param jobName\n//     * @return\n//     * @throws SchedulerException\n//     */\n//    public static boolean removeJob(String jobName) throws SchedulerException {\n//\n//        JobKey jobKey = new JobKey(jobName);\n//        scheduler.deleteJob(jobKey);\n//\n//        /*TriggerKey triggerKey = TriggerKey.triggerKey(jobName);\n//        if (scheduler.checkExists(triggerKey)) {\n//            scheduler.unscheduleJob(triggerKey);    // trigger + job\n//        }*/\n//\n//        logger.info(\">>>>>>>>>>> removeJob success(quartz), jobKey:{}\", jobKey);\n//        return true;\n//    }\n//\n//\n//    /**\n//     * updateJobCron\n//     *\n//     * @param jobName\n//     * @param cronExpression\n//     * @return\n//     * @throws SchedulerException\n//     */\n//\tpublic static boolean updateJobCron(String jobName, String cronExpression) throws SchedulerException {\n//\n//        // 1、job key\n//        TriggerKey triggerKey = TriggerKey.triggerKey(jobName);\n//\n//        // 2、valid\n//        if (!scheduler.checkExists(triggerKey)) {\n//            return true;    // PASS\n//        }\n//\n//        CronTrigger oldTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);\n//\n//        // 3、avoid repeat cron\n//        String oldCron = oldTrigger.getCronExpression();\n//        if (oldCron.equals(cronExpression)){\n//            return true;    // PASS\n//        }\n//\n//        // 4、new cron trigger\n//        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();\n//        oldTrigger = oldTrigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();\n//\n//        // 5、rescheduleJob\n//        scheduler.rescheduleJob(triggerKey, oldTrigger);\n//\n//        /*\n//        JobKey jobKey = new JobKey(jobName);\n//\n//        // old job detail\n//        JobDetail jobDetail = scheduler.getJobDetail(jobKey);\n//\n//        // new trigger\n//        HashSet<Trigger> triggerSet = new HashSet<Trigger>();\n//        triggerSet.add(cronTrigger);\n//        // cover trigger of job detail\n//        scheduler.scheduleJob(jobDetail, triggerSet, true);*/\n//\n//        logger.info(\">>>>>>>>>>> resumeJob success, JobName:{}\", jobName);\n//        return true;\n//    }\n//\n//\n//    /**\n//     * pause\n//     *\n//     * @param jobName\n//     * @return\n//     * @throws SchedulerException\n//     */\n//    /*public static boolean pauseJob(String jobName) throws SchedulerException {\n//\n//    \tTriggerKey triggerKey = TriggerKey.triggerKey(jobName);\n//\n//        boolean result = false;\n//        if (scheduler.checkExists(triggerKey)) {\n//            scheduler.pauseTrigger(triggerKey);\n//            result =  true;\n//        }\n//\n//        logger.info(\">>>>>>>>>>> pauseJob {}, triggerKey:{}\", (result?\"success\":\"fail\"),triggerKey);\n//        return result;\n//    }*/\n//\n//\n//    /**\n//     * resume\n//     *\n//     * @param jobName\n//     * @return\n//     * @throws SchedulerException\n//     */\n//    /*public static boolean resumeJob(String jobName) throws SchedulerException {\n//\n//        TriggerKey triggerKey = TriggerKey.triggerKey(jobName);\n//\n//        boolean result = false;\n//        if (scheduler.checkExists(triggerKey)) {\n//            scheduler.resumeTrigger(triggerKey);\n//            result = true;\n//        }\n//\n//        logger.info(\">>>>>>>>>>> resumeJob {}, triggerKey:{}\", (result?\"success\":\"fail\"), triggerKey);\n//        return result;\n//    }*/\n//\n//\n//    /**\n//     * run\n//     *\n//     * @param jobName\n//     * @return\n//     * @throws SchedulerException\n//     */\n//    /*public static boolean triggerJob(String jobName) throws SchedulerException {\n//    \t// TriggerKey : name + group\n//    \tJobKey jobKey = new JobKey(jobName);\n//        TriggerKey triggerKey = TriggerKey.triggerKey(jobName);\n//\n//        boolean result = false;\n//        if (scheduler.checkExists(triggerKey)) {\n//            scheduler.triggerJob(jobKey);\n//            result = true;\n//            logger.info(\">>>>>>>>>>> runJob success, jobKey:{}\", jobKey);\n//        } else {\n//        \tlogger.info(\">>>>>>>>>>> runJob fail, jobKey:{}\", jobKey);\n//        }\n//        return result;\n//    }*/\n//\n//\n//    /**\n//     * finaAllJobList\n//     *\n//     * @return\n//     *//*\n//    @Deprecated\n//    public static List<Map<String, Object>> finaAllJobList(){\n//        List<Map<String, Object>> jobList = new ArrayList<Map<String,Object>>();\n//\n//        try {\n//            if (scheduler.getJobGroupNames()==null || scheduler.getJobGroupNames().size()==0) {\n//                return null;\n//            }\n//            String groupName = scheduler.getJobGroupNames().get(0);\n//            Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName));\n//            if (jobKeys!=null && jobKeys.size()>0) {\n//                for (JobKey jobKey : jobKeys) {\n//                    TriggerKey triggerKey = TriggerKey.triggerKey(jobKey.getName(), Scheduler.DEFAULT_GROUP);\n//                    Trigger trigger = scheduler.getTrigger(triggerKey);\n//                    JobDetail jobDetail = scheduler.getJobDetail(jobKey);\n//                    TriggerState triggerState = scheduler.getTriggerState(triggerKey);\n//                    Map<String, Object> jobMap = new HashMap<String, Object>();\n//                    jobMap.put(\"TriggerKey\", triggerKey);\n//                    jobMap.put(\"Trigger\", trigger);\n//                    jobMap.put(\"JobDetail\", jobDetail);\n//                    jobMap.put(\"TriggerState\", triggerState);\n//                    jobList.add(jobMap);\n//                }\n//            }\n//\n//        } catch (SchedulerException e) {\n//            logger.error(e.getMessage(), e);\n//            return null;\n//        }\n//        return jobList;\n//    }*/\n//\n//}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/old/XxlJobThreadPool.java",
    "content": "package com.xxl.job.admin.core.old;//package com.xxl.job.admin.core.quartz;\n//\n//import org.quartz.SchedulerConfigException;\n//import org.quartz.spi.ThreadPool;\n//\n///**\n// * single thread pool, for async trigger\n// *\n// * @author xuxueli 2019-03-06\n// */\n//public class XxlJobThreadPool implements ThreadPool {\n//\n//    @Override\n//    public boolean runInThread(Runnable runnable) {\n//\n//        // async run\n//        runnable.run();\n//        return true;\n//\n//        //return false;\n//    }\n//\n//    @Override\n//    public int blockForAvailableThreads() {\n//        return 1;\n//    }\n//\n//    @Override\n//    public void initialize() throws SchedulerConfigException {\n//\n//    }\n//\n//    @Override\n//    public void shutdown(boolean waitForJobsToComplete) {\n//\n//    }\n//\n//    @Override\n//    public int getPoolSize() {\n//        return 1;\n//    }\n//\n//    @Override\n//    public void setInstanceId(String schedInstId) {\n//\n//    }\n//\n//    @Override\n//    public void setInstanceName(String schedName) {\n//\n//    }\n//\n//    // support\n//    public void setThreadCount(int count) {\n//        //\n//    }\n//\n//}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/ExecutorRouteStrategyEnum.java",
    "content": "package com.xxl.job.admin.core.route;\n\nimport com.xxl.job.admin.core.route.strategy.*;\nimport com.xxl.job.admin.core.util.I18nUtil;\n\n/**\n * Created by xuxueli on 17/3/10.\n */\npublic enum ExecutorRouteStrategyEnum {\n\n    FIRST(I18nUtil.getString(\"jobconf_route_first\"), new ExecutorRouteFirst()),\n    LAST(I18nUtil.getString(\"jobconf_route_last\"), new ExecutorRouteLast()),\n    ROUND(I18nUtil.getString(\"jobconf_route_round\"), new ExecutorRouteRound()),\n    RANDOM(I18nUtil.getString(\"jobconf_route_random\"), new ExecutorRouteRandom()),\n    CONSISTENT_HASH(I18nUtil.getString(\"jobconf_route_consistenthash\"), new ExecutorRouteConsistentHash()),\n    LEAST_FREQUENTLY_USED(I18nUtil.getString(\"jobconf_route_lfu\"), new ExecutorRouteLFU()),\n    LEAST_RECENTLY_USED(I18nUtil.getString(\"jobconf_route_lru\"), new ExecutorRouteLRU()),\n    FAILOVER(I18nUtil.getString(\"jobconf_route_failover\"), new ExecutorRouteFailover()),\n    BUSYOVER(I18nUtil.getString(\"jobconf_route_busyover\"), new ExecutorRouteBusyover()),\n    SHARDING_BROADCAST(I18nUtil.getString(\"jobconf_route_shard\"), null);\n\n    ExecutorRouteStrategyEnum(String title, ExecutorRouter router) {\n        this.title = title;\n        this.router = router;\n    }\n\n    private String title;\n    private ExecutorRouter router;\n\n    public String getTitle() {\n        return title;\n    }\n\n    public ExecutorRouter getRouter() {\n        return router;\n    }\n\n    public static ExecutorRouteStrategyEnum match(String name, ExecutorRouteStrategyEnum defaultItem) {\n        if (name != null) {\n            for (ExecutorRouteStrategyEnum item : ExecutorRouteStrategyEnum.values()) {\n                if (item.name().equals(name)) {\n                    return item;\n                }\n            }\n        }\n        return defaultItem;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/ExecutorRouter.java",
    "content": "package com.xxl.job.admin.core.route;\n\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.biz.model.TriggerParam;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.List;\n\n/**\n * Created by xuxueli on 17/3/10.\n */\npublic abstract class ExecutorRouter {\n    protected static Logger logger = LoggerFactory.getLogger(ExecutorRouter.class);\n\n    /**\n     * route address\n     *\n     * @param addressList\n     * @return ReturnT.content=address\n     */\n    public abstract ReturnT<String> route(TriggerParam triggerParam, List<String> addressList);\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteBusyover.java",
    "content": "package com.xxl.job.admin.core.route.strategy;\n\nimport com.xxl.job.admin.core.scheduler.XxlJobScheduler;\nimport com.xxl.job.admin.core.route.ExecutorRouter;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport com.xxl.job.core.biz.ExecutorBiz;\nimport com.xxl.job.core.biz.model.IdleBeatParam;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.biz.model.TriggerParam;\n\nimport java.util.List;\n\n/**\n * Created by xuxueli on 17/3/10.\n */\npublic class ExecutorRouteBusyover extends ExecutorRouter {\n\n    @Override\n    public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {\n        StringBuffer idleBeatResultSB = new StringBuffer();\n        for (String address : addressList) {\n            // beat\n            ReturnT<String> idleBeatResult = null;\n            try {\n                ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(address);\n                idleBeatResult = executorBiz.idleBeat(new IdleBeatParam(triggerParam.getJobId()));\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n                idleBeatResult = new ReturnT<String>(ReturnT.FAIL_CODE, \"\" + e);\n            }\n            idleBeatResultSB.append((idleBeatResultSB.length() > 0) ? \"<br><br>\" : \"\")\n                .append(I18nUtil.getString(\"jobconf_idleBeat\") + \"：\")\n                .append(\"<br>address：\").append(address)\n                .append(\"<br>code：\").append(idleBeatResult.getCode())\n                .append(\"<br>msg：\").append(idleBeatResult.getMsg());\n\n            // beat success\n            if (idleBeatResult.getCode() == ReturnT.SUCCESS_CODE) {\n                idleBeatResult.setMsg(idleBeatResultSB.toString());\n                idleBeatResult.setContent(address);\n                return idleBeatResult;\n            }\n        }\n\n        return new ReturnT<String>(ReturnT.FAIL_CODE, idleBeatResultSB.toString());\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteConsistentHash.java",
    "content": "package com.xxl.job.admin.core.route.strategy;\n\nimport com.xxl.job.admin.core.route.ExecutorRouter;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.biz.model.TriggerParam;\n\nimport java.io.UnsupportedEncodingException;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.List;\nimport java.util.SortedMap;\nimport java.util.TreeMap;\n\n/**\n * 分组下机器地址相同，不同JOB均匀散列在不同机器上，保证分组下机器分配JOB平均；且每个JOB固定调度其中一台机器；\n * a、virtual node：解决不均衡问题\n * b、hash method replace hashCode：String的hashCode可能重复，需要进一步扩大hashCode的取值范围\n * Created by xuxueli on 17/3/10.\n */\npublic class ExecutorRouteConsistentHash extends ExecutorRouter {\n\n    private static int VIRTUAL_NODE_NUM = 100;\n\n    /**\n     * get hash code on 2^32 ring (md5散列的方式计算hash值)\n     *\n     * @param key\n     * @return\n     */\n    private static long hash(String key) {\n\n        // md5 byte\n        MessageDigest md5;\n        try {\n            md5 = MessageDigest.getInstance(\"MD5\");\n        } catch (NoSuchAlgorithmException e) {\n            throw new RuntimeException(\"MD5 not supported\", e);\n        }\n        md5.reset();\n        byte[] keyBytes = null;\n        try {\n            keyBytes = key.getBytes(\"UTF-8\");\n        } catch (UnsupportedEncodingException e) {\n            throw new RuntimeException(\"Unknown string :\" + key, e);\n        }\n\n        md5.update(keyBytes);\n        byte[] digest = md5.digest();\n\n        // hash code, Truncate to 32-bits\n        long hashCode = ((long) (digest[3] & 0xFF) << 24)\n            | ((long) (digest[2] & 0xFF) << 16)\n            | ((long) (digest[1] & 0xFF) << 8)\n            | (digest[0] & 0xFF);\n\n        long truncateHashCode = hashCode & 0xffffffffL;\n        return truncateHashCode;\n    }\n\n    public String hashJob(int jobId, List<String> addressList) {\n\n        // ------A1------A2-------A3------\n        // -----------J1------------------\n        TreeMap<Long, String> addressRing = new TreeMap<Long, String>();\n        for (String address : addressList) {\n            for (int i = 0; i < VIRTUAL_NODE_NUM; i++) {\n                long addressHash = hash(\"SHARD-\" + address + \"-NODE-\" + i);\n                addressRing.put(addressHash, address);\n            }\n        }\n\n        long jobHash = hash(String.valueOf(jobId));\n        SortedMap<Long, String> lastRing = addressRing.tailMap(jobHash);\n        if (!lastRing.isEmpty()) {\n            return lastRing.get(lastRing.firstKey());\n        }\n        return addressRing.firstEntry().getValue();\n    }\n\n    @Override\n    public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {\n        String address = hashJob(triggerParam.getJobId(), addressList);\n        return new ReturnT<String>(address);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteFailover.java",
    "content": "package com.xxl.job.admin.core.route.strategy;\n\nimport com.xxl.job.admin.core.scheduler.XxlJobScheduler;\nimport com.xxl.job.admin.core.route.ExecutorRouter;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport com.xxl.job.core.biz.ExecutorBiz;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.biz.model.TriggerParam;\n\nimport java.util.List;\n\n/**\n * Created by xuxueli on 17/3/10.\n */\npublic class ExecutorRouteFailover extends ExecutorRouter {\n\n    @Override\n    public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {\n\n        StringBuffer beatResultSB = new StringBuffer();\n        for (String address : addressList) {\n            // beat\n            ReturnT<String> beatResult = null;\n            try {\n                ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(address);\n                beatResult = executorBiz.beat();\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n                beatResult = new ReturnT<String>(ReturnT.FAIL_CODE, \"\" + e);\n            }\n            beatResultSB.append((beatResultSB.length() > 0) ? \"<br><br>\" : \"\")\n                .append(I18nUtil.getString(\"jobconf_beat\") + \"：\")\n                .append(\"<br>address：\").append(address)\n                .append(\"<br>code：\").append(beatResult.getCode())\n                .append(\"<br>msg：\").append(beatResult.getMsg());\n\n            // beat success\n            if (beatResult.getCode() == ReturnT.SUCCESS_CODE) {\n\n                beatResult.setMsg(beatResultSB.toString());\n                beatResult.setContent(address);\n                return beatResult;\n            }\n        }\n        return new ReturnT<String>(ReturnT.FAIL_CODE, beatResultSB.toString());\n\n    }\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteFirst.java",
    "content": "package com.xxl.job.admin.core.route.strategy;\n\nimport com.xxl.job.admin.core.route.ExecutorRouter;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.biz.model.TriggerParam;\n\nimport java.util.List;\n\n/**\n * Created by xuxueli on 17/3/10.\n */\npublic class ExecutorRouteFirst extends ExecutorRouter {\n\n    @Override\n    public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {\n        return new ReturnT<String>(addressList.get(0));\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteLFU.java",
    "content": "package com.xxl.job.admin.core.route.strategy;\n\nimport com.xxl.job.admin.core.route.ExecutorRouter;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.biz.model.TriggerParam;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\n/**\n * 单个JOB对应的每个执行器，使用频率最低的优先被选举\n * a(*)、LFU(Least Frequently Used)：最不经常使用，频率/次数\n * b、LRU(Least Recently Used)：最近最久未使用，时间\n * <p>\n * Created by xuxueli on 17/3/10.\n */\npublic class ExecutorRouteLFU extends ExecutorRouter {\n\n    private static ConcurrentMap<Integer, HashMap<String, Integer>> jobLfuMap = new ConcurrentHashMap<Integer, HashMap<String, Integer>>();\n    private static long CACHE_VALID_TIME = 0;\n\n    public String route(int jobId, List<String> addressList) {\n\n        // cache clear\n        if (System.currentTimeMillis() > CACHE_VALID_TIME) {\n            jobLfuMap.clear();\n            CACHE_VALID_TIME = System.currentTimeMillis() + 1000 * 60 * 60 * 24;\n        }\n\n        // lfu item init\n        HashMap<String, Integer> lfuItemMap = jobLfuMap.get(jobId);     // Key排序可以用TreeMap+构造入参Compare；Value排序暂时只能通过ArrayList；\n        if (lfuItemMap == null) {\n            lfuItemMap = new HashMap<String, Integer>();\n            jobLfuMap.putIfAbsent(jobId, lfuItemMap);   // 避免重复覆盖\n        }\n\n        // put new\n        for (String address : addressList) {\n            if (!lfuItemMap.containsKey(address) || lfuItemMap.get(address) > 1000000) {\n                lfuItemMap.put(address, new Random().nextInt(addressList.size()));  // 初始化时主动Random一次，缓解首次压力\n            }\n        }\n        // remove old\n        List<String> delKeys = new ArrayList<>();\n        for (String existKey : lfuItemMap.keySet()) {\n            if (!addressList.contains(existKey)) {\n                delKeys.add(existKey);\n            }\n        }\n        if (delKeys.size() > 0) {\n            for (String delKey : delKeys) {\n                lfuItemMap.remove(delKey);\n            }\n        }\n\n        // load least userd count address\n        List<Map.Entry<String, Integer>> lfuItemList = new ArrayList<Map.Entry<String, Integer>>(lfuItemMap.entrySet());\n        Collections.sort(lfuItemList, new Comparator<Map.Entry<String, Integer>>() {\n            @Override\n            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {\n                return o1.getValue().compareTo(o2.getValue());\n            }\n        });\n\n        Map.Entry<String, Integer> addressItem = lfuItemList.get(0);\n        String minAddress = addressItem.getKey();\n        addressItem.setValue(addressItem.getValue() + 1);\n\n        return addressItem.getKey();\n    }\n\n    @Override\n    public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {\n        String address = route(triggerParam.getJobId(), addressList);\n        return new ReturnT<String>(address);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteLRU.java",
    "content": "package com.xxl.job.admin.core.route.strategy;\n\nimport com.xxl.job.admin.core.route.ExecutorRouter;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.biz.model.TriggerParam;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\n/**\n * 单个JOB对应的每个执行器，最久为使用的优先被选举\n * a、LFU(Least Frequently Used)：最不经常使用，频率/次数\n * b(*)、LRU(Least Recently Used)：最近最久未使用，时间\n * <p>\n * Created by xuxueli on 17/3/10.\n */\npublic class ExecutorRouteLRU extends ExecutorRouter {\n\n    private static ConcurrentMap<Integer, LinkedHashMap<String, String>> jobLRUMap = new ConcurrentHashMap<Integer, LinkedHashMap<String, String>>();\n    private static long CACHE_VALID_TIME = 0;\n\n    public String route(int jobId, List<String> addressList) {\n\n        // cache clear\n        if (System.currentTimeMillis() > CACHE_VALID_TIME) {\n            jobLRUMap.clear();\n            CACHE_VALID_TIME = System.currentTimeMillis() + 1000 * 60 * 60 * 24;\n        }\n\n        // init lru\n        LinkedHashMap<String, String> lruItem = jobLRUMap.get(jobId);\n        if (lruItem == null) {\n            /**\n             * LinkedHashMap\n             *      a、accessOrder：true=访问顺序排序（get/put时排序）；false=插入顺序排期；\n             *      b、removeEldestEntry：新增元素时将会调用，返回true时会删除最老元素；可封装LinkedHashMap并重写该方法，比如定义最大容量，超出是返回true即可实现固定长度的LRU算法；\n             */\n            lruItem = new LinkedHashMap<String, String>(16, 0.75f, true);\n            jobLRUMap.putIfAbsent(jobId, lruItem);\n        }\n\n        // put new\n        for (String address : addressList) {\n            if (!lruItem.containsKey(address)) {\n                lruItem.put(address, address);\n            }\n        }\n        // remove old\n        List<String> delKeys = new ArrayList<>();\n        for (String existKey : lruItem.keySet()) {\n            if (!addressList.contains(existKey)) {\n                delKeys.add(existKey);\n            }\n        }\n        if (delKeys.size() > 0) {\n            for (String delKey : delKeys) {\n                lruItem.remove(delKey);\n            }\n        }\n\n        // load\n        String eldestKey = lruItem.entrySet().iterator().next().getKey();\n        String eldestValue = lruItem.get(eldestKey);\n        return eldestValue;\n    }\n\n    @Override\n    public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {\n        String address = route(triggerParam.getJobId(), addressList);\n        return new ReturnT<String>(address);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteLast.java",
    "content": "package com.xxl.job.admin.core.route.strategy;\n\nimport com.xxl.job.admin.core.route.ExecutorRouter;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.biz.model.TriggerParam;\n\nimport java.util.List;\n\n/**\n * Created by xuxueli on 17/3/10.\n */\npublic class ExecutorRouteLast extends ExecutorRouter {\n\n    @Override\n    public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {\n        return new ReturnT<String>(addressList.get(addressList.size() - 1));\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteRandom.java",
    "content": "package com.xxl.job.admin.core.route.strategy;\n\nimport com.xxl.job.admin.core.route.ExecutorRouter;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.biz.model.TriggerParam;\n\nimport java.util.List;\nimport java.util.Random;\n\n/**\n * Created by xuxueli on 17/3/10.\n */\npublic class ExecutorRouteRandom extends ExecutorRouter {\n\n    private static Random localRandom = new Random();\n\n    @Override\n    public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {\n        String address = addressList.get(localRandom.nextInt(addressList.size()));\n        return new ReturnT<String>(address);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteRound.java",
    "content": "package com.xxl.job.admin.core.route.strategy;\n\nimport com.xxl.job.admin.core.route.ExecutorRouter;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.biz.model.TriggerParam;\n\nimport java.util.List;\nimport java.util.Random;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * Created by xuxueli on 17/3/10.\n */\npublic class ExecutorRouteRound extends ExecutorRouter {\n\n    private static ConcurrentMap<Integer, AtomicInteger> routeCountEachJob = new ConcurrentHashMap<>();\n    private static long CACHE_VALID_TIME = 0;\n\n    private static int count(int jobId) {\n        // cache clear\n        if (System.currentTimeMillis() > CACHE_VALID_TIME) {\n            routeCountEachJob.clear();\n            CACHE_VALID_TIME = System.currentTimeMillis() + 1000 * 60 * 60 * 24;\n        }\n\n        AtomicInteger count = routeCountEachJob.get(jobId);\n        if (count == null || count.get() > 1000000) {\n            // 初始化时主动Random一次，缓解首次压力\n            count = new AtomicInteger(new Random().nextInt(100));\n        } else {\n            // count++\n            count.addAndGet(1);\n        }\n        routeCountEachJob.put(jobId, count);\n        return count.get();\n    }\n\n    @Override\n    public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {\n        String address = addressList.get(count(triggerParam.getJobId()) % addressList.size());\n        return new ReturnT<String>(address);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/scheduler/MisfireStrategyEnum.java",
    "content": "package com.xxl.job.admin.core.scheduler;\n\nimport com.xxl.job.admin.core.util.I18nUtil;\n\n/**\n * @author xuxueli 2020-10-29 21:11:23\n */\npublic enum MisfireStrategyEnum {\n\n    /**\n     * do nothing\n     */\n    DO_NOTHING(I18nUtil.getString(\"misfire_strategy_do_nothing\")),\n\n    /**\n     * fire once now\n     */\n    FIRE_ONCE_NOW(I18nUtil.getString(\"misfire_strategy_fire_once_now\"));\n\n    private String title;\n\n    MisfireStrategyEnum(String title) {\n        this.title = title;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public static MisfireStrategyEnum match(String name, MisfireStrategyEnum defaultItem) {\n        for (MisfireStrategyEnum item : MisfireStrategyEnum.values()) {\n            if (item.name().equals(name)) {\n                return item;\n            }\n        }\n        return defaultItem;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/scheduler/ScheduleTypeEnum.java",
    "content": "package com.xxl.job.admin.core.scheduler;\n\nimport com.xxl.job.admin.core.util.I18nUtil;\n\n/**\n * @author xuxueli 2020-10-29 21:11:23\n */\npublic enum ScheduleTypeEnum {\n\n    NONE(I18nUtil.getString(\"schedule_type_none\")),\n\n    /**\n     * schedule by cron\n     */\n    CRON(I18nUtil.getString(\"schedule_type_cron\")),\n\n    /**\n     * schedule by fixed rate (in seconds)\n     */\n    FIX_RATE(I18nUtil.getString(\"schedule_type_fix_rate\")),\n\n    /**\n     * schedule by fix delay (in seconds)， after the last time\n     */\n    /*FIX_DELAY(I18nUtil.getString(\"schedule_type_fix_delay\"))*/;\n\n    private String title;\n\n    ScheduleTypeEnum(String title) {\n        this.title = title;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public static ScheduleTypeEnum match(String name, ScheduleTypeEnum defaultItem) {\n        for (ScheduleTypeEnum item : ScheduleTypeEnum.values()) {\n            if (item.name().equals(name)) {\n                return item;\n            }\n        }\n        return defaultItem;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/scheduler/XxlJobScheduler.java",
    "content": "package com.xxl.job.admin.core.scheduler;\n\nimport com.xxl.job.admin.core.conf.XxlJobAdminConfig;\nimport com.xxl.job.admin.core.thread.*;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport com.xxl.job.core.biz.ExecutorBiz;\nimport com.xxl.job.core.biz.client.ExecutorBizClient;\nimport com.xxl.job.core.enums.ExecutorBlockStrategyEnum;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\n/**\n * @author xuxueli 2018-10-28 00:18:17\n */\n\npublic class XxlJobScheduler {\n    private static final Logger logger = LoggerFactory.getLogger(XxlJobScheduler.class);\n\n\n    public void init() throws Exception {\n        // init i18n\n        initI18n();\n\n        // admin trigger pool start\n        JobTriggerPoolHelper.toStart();\n\n        // admin registry monitor run\n        JobRegistryHelper.getInstance().start();\n\n        // admin fail-monitor run\n        JobFailMonitorHelper.getInstance().start();\n\n        // admin lose-monitor run ( depend on JobTriggerPoolHelper )\n        JobCompleteHelper.getInstance().start();\n\n        // admin log report start\n        JobLogReportHelper.getInstance().start();\n\n        // start-schedule  ( depend on JobTriggerPoolHelper )\n        JobScheduleHelper.getInstance().start();\n\n        logger.info(\">>>>>>>>> init xxl-job admin success.\");\n    }\n\n\n    public void destroy() throws Exception {\n\n        // stop-schedule\n        JobScheduleHelper.getInstance().toStop();\n\n        // admin log report stop\n        JobLogReportHelper.getInstance().toStop();\n\n        // admin lose-monitor stop\n        JobCompleteHelper.getInstance().toStop();\n\n        // admin fail-monitor stop\n        JobFailMonitorHelper.getInstance().toStop();\n\n        // admin registry stop\n        JobRegistryHelper.getInstance().toStop();\n\n        // admin trigger pool stop\n        JobTriggerPoolHelper.toStop();\n\n    }\n\n    // ---------------------- I18n ----------------------\n\n    private void initI18n() {\n        for (ExecutorBlockStrategyEnum item : ExecutorBlockStrategyEnum.values()) {\n            item.setTitle(I18nUtil.getString(\"jobconf_block_\".concat(item.name())));\n        }\n    }\n\n    // ---------------------- executor-client ----------------------\n    private static ConcurrentMap<String, ExecutorBiz> executorBizRepository = new ConcurrentHashMap<String, ExecutorBiz>();\n\n    public static ExecutorBiz getExecutorBiz(String address) throws Exception {\n        // valid\n        if (address == null || address.trim().length() == 0) {\n            return null;\n        }\n\n        // load-cache\n        address = address.trim();\n        ExecutorBiz executorBiz = executorBizRepository.get(address);\n        if (executorBiz != null) {\n            return executorBiz;\n        }\n\n        // set-cache\n        executorBiz = new ExecutorBizClient(address, XxlJobAdminConfig.getAdminConfig().getAccessToken());\n\n        executorBizRepository.put(address, executorBiz);\n        return executorBiz;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobCompleteHelper.java",
    "content": "package com.xxl.job.admin.core.thread;\n\nimport com.xxl.job.admin.core.complete.XxlJobCompleter;\nimport com.xxl.job.admin.core.conf.XxlJobAdminConfig;\nimport com.xxl.job.admin.core.model.XxlJobLog;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport com.xxl.job.core.biz.model.HandleCallbackParam;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.util.DateUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.concurrent.*;\n\n/**\n * job lose-monitor instance\n *\n * @author xuxueli 2015-9-1 18:05:56\n */\npublic class JobCompleteHelper {\n    private static Logger logger = LoggerFactory.getLogger(JobCompleteHelper.class);\n\n    private static JobCompleteHelper instance = new JobCompleteHelper();\n\n    public static JobCompleteHelper getInstance() {\n        return instance;\n    }\n\n    // ---------------------- monitor ----------------------\n\n    private ThreadPoolExecutor callbackThreadPool = null;\n    private Thread monitorThread;\n    private volatile boolean toStop = false;\n\n    public void start() {\n\n        // for callback\n        callbackThreadPool = new ThreadPoolExecutor(\n            2,\n            20,\n            30L,\n            TimeUnit.SECONDS,\n            new LinkedBlockingQueue<Runnable>(3000),\n            new ThreadFactory() {\n                @Override\n                public Thread newThread(Runnable r) {\n                    return new Thread(r, \"xxl-job, admin JobLosedMonitorHelper-callbackThreadPool-\" + r.hashCode());\n                }\n            },\n            new RejectedExecutionHandler() {\n                @Override\n                public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {\n                    r.run();\n                    logger.warn(\">>>>>>>>>>> xxl-job, callback too fast, match threadpool rejected handler(run now).\");\n                }\n            });\n\n\n        // for monitor\n        monitorThread = new Thread(new Runnable() {\n\n            @Override\n            public void run() {\n\n                // wait for JobTriggerPoolHelper-init\n                try {\n                    TimeUnit.MILLISECONDS.sleep(50);\n                } catch (InterruptedException e) {\n                    if (!toStop) {\n                        logger.error(e.getMessage(), e);\n                    }\n                }\n\n                // monitor\n                while (!toStop) {\n                    try {\n                        // 任务结果丢失处理：调度记录停留在 \"运行中\" 状态超过10min，且对应执行器心跳注册失败不在线，则将本地调度主动标记失败；\n                        Date losedTime = DateUtil.addMinutes(new Date(), -10);\n                        List<Long> losedJobIds = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().findLostJobIds(losedTime);\n\n                        if (losedJobIds != null && losedJobIds.size() > 0) {\n                            for (Long logId : losedJobIds) {\n\n                                XxlJobLog jobLog = new XxlJobLog();\n                                jobLog.setId(logId);\n\n                                jobLog.setHandleTime(new Date());\n                                jobLog.setHandleCode(ReturnT.FAIL_CODE);\n                                jobLog.setHandleMsg(I18nUtil.getString(\"joblog_lost_fail\"));\n\n                                XxlJobCompleter.updateHandleInfoAndFinish(jobLog);\n                            }\n\n                        }\n                    } catch (Exception e) {\n                        if (!toStop) {\n                            logger.error(\">>>>>>>>>>> xxl-job, job fail monitor thread error:{}\", e);\n                        }\n                    }\n\n                    try {\n                        TimeUnit.SECONDS.sleep(60);\n                    } catch (Exception e) {\n                        if (!toStop) {\n                            logger.error(e.getMessage(), e);\n                        }\n                    }\n\n                }\n\n                logger.info(\">>>>>>>>>>> xxl-job, JobLosedMonitorHelper stop\");\n\n            }\n        });\n        monitorThread.setDaemon(true);\n        monitorThread.setName(\"xxl-job, admin JobLosedMonitorHelper\");\n        monitorThread.start();\n    }\n\n    public void toStop() {\n        toStop = true;\n\n        // stop registryOrRemoveThreadPool\n        callbackThreadPool.shutdownNow();\n\n        // stop monitorThread (interrupt and wait)\n        monitorThread.interrupt();\n        try {\n            monitorThread.join();\n        } catch (InterruptedException e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n\n\n    // ---------------------- helper ----------------------\n\n    public ReturnT<String> callback(List<HandleCallbackParam> callbackParamList) {\n\n        callbackThreadPool.execute(new Runnable() {\n            @Override\n            public void run() {\n                for (HandleCallbackParam handleCallbackParam : callbackParamList) {\n                    ReturnT<String> callbackResult = callback(handleCallbackParam);\n                    logger.debug(\">>>>>>>>> JobApiController.callback {}, handleCallbackParam={}, callbackResult={}\",\n                        (callbackResult.getCode() == ReturnT.SUCCESS_CODE ? \"success\" : \"fail\"), handleCallbackParam, callbackResult);\n                }\n            }\n        });\n\n        return ReturnT.SUCCESS;\n    }\n\n    private ReturnT<String> callback(HandleCallbackParam handleCallbackParam) {\n        // valid log item\n        XxlJobLog log = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().load(handleCallbackParam.getLogId());\n        if (log == null) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, \"log item not found.\");\n        }\n        if (log.getHandleCode() > 0) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, \"log repeate callback.\");     // avoid repeat callback, trigger child job etc\n        }\n\n        // handle msg\n        StringBuffer handleMsg = new StringBuffer();\n        if (log.getHandleMsg() != null) {\n            handleMsg.append(log.getHandleMsg()).append(\"<br>\");\n        }\n        if (handleCallbackParam.getHandleMsg() != null) {\n            handleMsg.append(handleCallbackParam.getHandleMsg());\n        }\n\n        // success, save log\n        log.setHandleTime(new Date());\n        log.setHandleCode(handleCallbackParam.getHandleCode());\n        log.setHandleMsg(handleMsg.toString());\n        XxlJobCompleter.updateHandleInfoAndFinish(log);\n\n        return ReturnT.SUCCESS;\n    }\n\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java",
    "content": "package com.xxl.job.admin.core.thread;\n\nimport com.xxl.job.admin.core.conf.XxlJobAdminConfig;\nimport com.xxl.job.admin.core.model.XxlJobInfo;\nimport com.xxl.job.admin.core.model.XxlJobLog;\nimport com.xxl.job.admin.core.trigger.TriggerTypeEnum;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * job monitor instance\n *\n * @author xuxueli 2015-9-1 18:05:56\n */\npublic class JobFailMonitorHelper {\n    private static Logger logger = LoggerFactory.getLogger(JobFailMonitorHelper.class);\n\n    private static JobFailMonitorHelper instance = new JobFailMonitorHelper();\n\n    public static JobFailMonitorHelper getInstance() {\n        return instance;\n    }\n\n    // ---------------------- monitor ----------------------\n\n    private Thread monitorThread;\n    private volatile boolean toStop = false;\n\n    public void start() {\n        monitorThread = new Thread(new Runnable() {\n\n            @Override\n            public void run() {\n\n                // monitor\n                while (!toStop) {\n                    try {\n\n                        List<Long> failLogIds = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().findFailJobLogIds(1000);\n                        if (failLogIds != null && !failLogIds.isEmpty()) {\n                            for (long failLogId : failLogIds) {\n\n                                // lock log\n                                int lockRet = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateAlarmStatus(failLogId, 0, -1);\n                                if (lockRet < 1) {\n                                    continue;\n                                }\n                                XxlJobLog log = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().load(failLogId);\n                                XxlJobInfo info = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(log.getJobId());\n\n                                // 1、fail retry monitor\n                                if (log.getExecutorFailRetryCount() > 0) {\n                                    JobTriggerPoolHelper.trigger(log.getJobId(), TriggerTypeEnum.RETRY, (log.getExecutorFailRetryCount() - 1), log.getExecutorShardingParam(), log.getExecutorParam(), null);\n                                    String retryMsg = \"<br><br><span style=\\\"color:#F39C12;\\\" > >>>>>>>>>>>\" + I18nUtil.getString(\"jobconf_trigger_type_retry\") + \"<<<<<<<<<<< </span><br>\";\n                                    log.setTriggerMsg(log.getTriggerMsg() + retryMsg);\n                                    XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateTriggerInfo(log);\n                                }\n\n                                // 2、fail alarm monitor\n                                int newAlarmStatus = 0;        // 告警状态：0-默认、-1=锁定状态、1-无需告警、2-告警成功、3-告警失败\n                                if (info != null) {\n                                    boolean alarmResult = XxlJobAdminConfig.getAdminConfig().getJobAlarmer().alarm(info, log);\n                                    newAlarmStatus = alarmResult ? 2 : 3;\n                                } else {\n                                    newAlarmStatus = 1;\n                                }\n\n                                XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateAlarmStatus(failLogId, -1, newAlarmStatus);\n                            }\n                        }\n\n                    } catch (Exception e) {\n                        if (!toStop) {\n                            logger.error(\">>>>>>>>>>> xxl-job, job fail monitor thread error:{}\", e);\n                        }\n                    }\n\n                    try {\n                        TimeUnit.SECONDS.sleep(10);\n                    } catch (Exception e) {\n                        if (!toStop) {\n                            logger.error(e.getMessage(), e);\n                        }\n                    }\n\n                }\n\n                logger.info(\">>>>>>>>>>> xxl-job, job fail monitor thread stop\");\n\n            }\n        });\n        monitorThread.setDaemon(true);\n        monitorThread.setName(\"xxl-job, admin JobFailMonitorHelper\");\n        monitorThread.start();\n    }\n\n    public void toStop() {\n        toStop = true;\n        // interrupt and wait\n        monitorThread.interrupt();\n        try {\n            monitorThread.join();\n        } catch (InterruptedException e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobLogReportHelper.java",
    "content": "package com.xxl.job.admin.core.thread;\n\nimport com.xxl.job.admin.core.conf.XxlJobAdminConfig;\nimport com.xxl.job.admin.core.model.XxlJobLogReport;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * job log report helper\n *\n * @author xuxueli 2019-11-22\n */\npublic class JobLogReportHelper {\n    private static Logger logger = LoggerFactory.getLogger(JobLogReportHelper.class);\n\n    private static JobLogReportHelper instance = new JobLogReportHelper();\n\n    public static JobLogReportHelper getInstance() {\n        return instance;\n    }\n\n\n    private Thread logrThread;\n    private volatile boolean toStop = false;\n\n    public void start() {\n        logrThread = new Thread(new Runnable() {\n\n            @Override\n            public void run() {\n\n                // last clean log time\n                long lastCleanLogTime = 0;\n\n\n                while (!toStop) {\n\n                    // 1、log-report refresh: refresh log report in 3 days\n                    try {\n\n                        for (int i = 0; i < 3; i++) {\n\n                            // today\n                            Calendar itemDay = Calendar.getInstance();\n                            itemDay.add(Calendar.DAY_OF_MONTH, -i);\n                            itemDay.set(Calendar.HOUR_OF_DAY, 0);\n                            itemDay.set(Calendar.MINUTE, 0);\n                            itemDay.set(Calendar.SECOND, 0);\n                            itemDay.set(Calendar.MILLISECOND, 0);\n\n                            Date todayFrom = itemDay.getTime();\n\n                            itemDay.set(Calendar.HOUR_OF_DAY, 23);\n                            itemDay.set(Calendar.MINUTE, 59);\n                            itemDay.set(Calendar.SECOND, 59);\n                            itemDay.set(Calendar.MILLISECOND, 999);\n\n                            Date todayTo = itemDay.getTime();\n\n                            // refresh log-report every minute\n                            XxlJobLogReport xxlJobLogReport = new XxlJobLogReport();\n                            xxlJobLogReport.setTriggerDay(todayFrom);\n                            xxlJobLogReport.setRunningCount(0);\n                            xxlJobLogReport.setSucCount(0);\n                            xxlJobLogReport.setFailCount(0);\n\n                            Map<String, Object> triggerCountMap = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().findLogReport(todayFrom, todayTo);\n                            if (triggerCountMap != null && triggerCountMap.size() > 0) {\n                                int triggerDayCount = triggerCountMap.containsKey(\"triggerDayCount\") ? Integer.valueOf(String.valueOf(triggerCountMap.get(\"triggerDayCount\"))) : 0;\n                                int triggerDayCountRunning = triggerCountMap.containsKey(\"triggerDayCountRunning\") ? Integer.valueOf(String.valueOf(triggerCountMap.get(\"triggerDayCountRunning\"))) : 0;\n                                int triggerDayCountSuc = triggerCountMap.containsKey(\"triggerDayCountSuc\") ? Integer.valueOf(String.valueOf(triggerCountMap.get(\"triggerDayCountSuc\"))) : 0;\n                                int triggerDayCountFail = triggerDayCount - triggerDayCountRunning - triggerDayCountSuc;\n\n                                xxlJobLogReport.setRunningCount(triggerDayCountRunning);\n                                xxlJobLogReport.setSucCount(triggerDayCountSuc);\n                                xxlJobLogReport.setFailCount(triggerDayCountFail);\n                            }\n\n                            // do refresh\n                            int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobLogReportDao().update(xxlJobLogReport);\n                            if (ret < 1) {\n                                XxlJobAdminConfig.getAdminConfig().getXxlJobLogReportDao().save(xxlJobLogReport);\n                            }\n                        }\n\n                    } catch (Exception e) {\n                        if (!toStop) {\n                            logger.error(\">>>>>>>>>>> xxl-job, job log report thread error:{}\", e);\n                        }\n                    }\n\n                    // 2、log-clean: switch open & once each day\n                    if (XxlJobAdminConfig.getAdminConfig().getLogretentiondays() > 0\n                        && System.currentTimeMillis() - lastCleanLogTime > 24 * 60 * 60 * 1000) {\n\n                        // expire-time\n                        Calendar expiredDay = Calendar.getInstance();\n                        expiredDay.add(Calendar.DAY_OF_MONTH, -1 * XxlJobAdminConfig.getAdminConfig().getLogretentiondays());\n                        expiredDay.set(Calendar.HOUR_OF_DAY, 0);\n                        expiredDay.set(Calendar.MINUTE, 0);\n                        expiredDay.set(Calendar.SECOND, 0);\n                        expiredDay.set(Calendar.MILLISECOND, 0);\n                        Date clearBeforeTime = expiredDay.getTime();\n\n                        // clean expired log\n                        List<Long> logIds = null;\n                        do {\n                            logIds = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().findClearLogIds(0, 0, clearBeforeTime, 0, 1000);\n                            if (logIds != null && logIds.size() > 0) {\n                                XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().clearLog(logIds);\n                            }\n                        } while (logIds != null && logIds.size() > 0);\n\n                        // update clean time\n                        lastCleanLogTime = System.currentTimeMillis();\n                    }\n\n                    try {\n                        TimeUnit.MINUTES.sleep(1);\n                    } catch (Exception e) {\n                        if (!toStop) {\n                            logger.error(e.getMessage(), e);\n                        }\n                    }\n\n                }\n\n                logger.info(\">>>>>>>>>>> xxl-job, job log report thread stop\");\n\n            }\n        });\n        logrThread.setDaemon(true);\n        logrThread.setName(\"xxl-job, admin JobLogReportHelper\");\n        logrThread.start();\n    }\n\n    public void toStop() {\n        toStop = true;\n        // interrupt and wait\n        logrThread.interrupt();\n        try {\n            logrThread.join();\n        } catch (InterruptedException e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobRegistryHelper.java",
    "content": "package com.xxl.job.admin.core.thread;\n\nimport com.xxl.job.admin.core.conf.XxlJobAdminConfig;\nimport com.xxl.job.admin.core.model.XxlJobGroup;\nimport com.xxl.job.admin.core.model.XxlJobRegistry;\nimport com.xxl.job.core.biz.model.RegistryParam;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.enums.RegistryConfig;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.StringUtils;\n\nimport java.util.*;\nimport java.util.concurrent.*;\n\n/**\n * job registry instance\n *\n * @author xuxueli 2016-10-02 19:10:24\n */\npublic class JobRegistryHelper {\n    private static Logger logger = LoggerFactory.getLogger(JobRegistryHelper.class);\n\n    private static JobRegistryHelper instance = new JobRegistryHelper();\n\n    public static JobRegistryHelper getInstance() {\n        return instance;\n    }\n\n    private ThreadPoolExecutor registryOrRemoveThreadPool = null;\n    private Thread registryMonitorThread;\n    private volatile boolean toStop = false;\n\n    public void start() {\n\n        // for registry or remove\n        registryOrRemoveThreadPool = new ThreadPoolExecutor(\n            2,\n            10,\n            30L,\n            TimeUnit.SECONDS,\n            new LinkedBlockingQueue<Runnable>(2000),\n            new ThreadFactory() {\n                @Override\n                public Thread newThread(Runnable r) {\n                    return new Thread(r, \"xxl-job, admin JobRegistryMonitorHelper-registryOrRemoveThreadPool-\" + r.hashCode());\n                }\n            },\n            new RejectedExecutionHandler() {\n                @Override\n                public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {\n                    r.run();\n                    logger.warn(\">>>>>>>>>>> xxl-job, registry or remove too fast, match threadpool rejected handler(run now).\");\n                }\n            });\n\n        // for monitor\n        registryMonitorThread = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                while (!toStop) {\n                    try {\n                        // auto registry group\n                        List<XxlJobGroup> groupList = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().findByAddressType(0);\n                        if (groupList != null && !groupList.isEmpty()) {\n\n                            // remove dead address (admin/executor)\n                            List<Integer> ids = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().findDead(RegistryConfig.DEAD_TIMEOUT, new Date());\n                            if (ids != null && ids.size() > 0) {\n                                XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().removeDead(ids);\n                            }\n\n                            // fresh online address (admin/executor)\n                            HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>();\n                            List<XxlJobRegistry> list = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().findAll(RegistryConfig.DEAD_TIMEOUT, new Date());\n                            if (list != null) {\n                                for (XxlJobRegistry item : list) {\n                                    if (RegistryConfig.RegistType.EXECUTOR.name().equals(item.getRegistryGroup())) {\n                                        String appname = item.getRegistryKey();\n                                        List<String> registryList = appAddressMap.get(appname);\n                                        if (registryList == null) {\n                                            registryList = new ArrayList<String>();\n                                        }\n\n                                        if (!registryList.contains(item.getRegistryValue())) {\n                                            registryList.add(item.getRegistryValue());\n                                        }\n                                        appAddressMap.put(appname, registryList);\n                                    }\n                                }\n                            }\n\n                            // fresh group address\n                            for (XxlJobGroup group : groupList) {\n                                List<String> registryList = appAddressMap.get(group.getAppname());\n                                String addressListStr = null;\n                                if (registryList != null && !registryList.isEmpty()) {\n                                    Collections.sort(registryList);\n                                    StringBuilder addressListSB = new StringBuilder();\n                                    for (String item : registryList) {\n                                        addressListSB.append(item).append(\",\");\n                                    }\n                                    addressListStr = addressListSB.toString();\n                                    addressListStr = addressListStr.substring(0, addressListStr.length() - 1);\n                                }\n                                group.setAddressList(addressListStr);\n                                group.setUpdateTime(new Date());\n\n                                XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().update(group);\n                            }\n                        }\n                    } catch (Exception e) {\n                        if (!toStop) {\n                            logger.error(\">>>>>>>>>>> xxl-job, job registry monitor thread error:{}\", e);\n                        }\n                    }\n                    try {\n                        TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT);\n                    } catch (InterruptedException e) {\n                        if (!toStop) {\n                            logger.error(\">>>>>>>>>>> xxl-job, job registry monitor thread error:{}\", e);\n                        }\n                    }\n                }\n                logger.info(\">>>>>>>>>>> xxl-job, job registry monitor thread stop\");\n            }\n        });\n        registryMonitorThread.setDaemon(true);\n        registryMonitorThread.setName(\"xxl-job, admin JobRegistryMonitorHelper-registryMonitorThread\");\n        registryMonitorThread.start();\n    }\n\n    public void toStop() {\n        toStop = true;\n\n        // stop registryOrRemoveThreadPool\n        registryOrRemoveThreadPool.shutdownNow();\n\n        // stop monitir (interrupt and wait)\n        registryMonitorThread.interrupt();\n        try {\n            registryMonitorThread.join();\n        } catch (InterruptedException e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n\n\n    // ---------------------- helper ----------------------\n\n    public ReturnT<String> registry(RegistryParam registryParam) {\n\n        // valid\n        if (!StringUtils.hasText(registryParam.getRegistryGroup())\n            || !StringUtils.hasText(registryParam.getRegistryKey())\n            || !StringUtils.hasText(registryParam.getRegistryValue())) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, \"Illegal Argument.\");\n        }\n\n        // async execute\n        registryOrRemoveThreadPool.execute(new Runnable() {\n            @Override\n            public void run() {\n                int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registryUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());\n                if (ret < 1) {\n                    XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registrySave(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());\n\n                    // fresh\n                    freshGroupRegistryInfo(registryParam);\n                }\n            }\n        });\n\n        return ReturnT.SUCCESS;\n    }\n\n    public ReturnT<String> registryRemove(RegistryParam registryParam) {\n\n        // valid\n        if (!StringUtils.hasText(registryParam.getRegistryGroup())\n            || !StringUtils.hasText(registryParam.getRegistryKey())\n            || !StringUtils.hasText(registryParam.getRegistryValue())) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, \"Illegal Argument.\");\n        }\n\n        // async execute\n        registryOrRemoveThreadPool.execute(new Runnable() {\n            @Override\n            public void run() {\n                int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registryDelete(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue());\n                if (ret > 0) {\n                    // fresh\n                    freshGroupRegistryInfo(registryParam);\n                }\n            }\n        });\n\n        return ReturnT.SUCCESS;\n    }\n\n    private void freshGroupRegistryInfo(RegistryParam registryParam) {\n        // Under consideration, prevent affecting core tables\n    }\n\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobScheduleHelper.java",
    "content": "package com.xxl.job.admin.core.thread;\n\nimport com.xxl.job.admin.core.conf.XxlJobAdminConfig;\nimport com.xxl.job.admin.core.cron.CronExpression;\nimport com.xxl.job.admin.core.model.XxlJobInfo;\nimport com.xxl.job.admin.core.scheduler.MisfireStrategyEnum;\nimport com.xxl.job.admin.core.scheduler.ScheduleTypeEnum;\nimport com.xxl.job.admin.core.trigger.TriggerTypeEnum;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * @author xuxueli 2019-05-21\n */\npublic class JobScheduleHelper {\n    private static Logger logger = LoggerFactory.getLogger(JobScheduleHelper.class);\n\n    private static JobScheduleHelper instance = new JobScheduleHelper();\n\n    public static JobScheduleHelper getInstance() {\n        return instance;\n    }\n\n    public static final long PRE_READ_MS = 5000;    // pre read\n\n    private Thread scheduleThread;\n    private Thread ringThread;\n    private volatile boolean scheduleThreadToStop = false;\n    private volatile boolean ringThreadToStop = false;\n    private volatile static Map<Integer, List<Integer>> ringData = new ConcurrentHashMap<>();\n\n    public void start() {\n\n        // schedule thread\n        scheduleThread = new Thread(new Runnable() {\n            @Override\n            public void run() {\n\n                try {\n                    TimeUnit.MILLISECONDS.sleep(5000 - System.currentTimeMillis() % 1000);\n                } catch (InterruptedException e) {\n                    if (!scheduleThreadToStop) {\n                        logger.error(e.getMessage(), e);\n                    }\n                }\n                logger.info(\">>>>>>>>> init xxl-job admin scheduler success.\");\n\n                // pre-read count: treadpool-size * trigger-qps (each trigger cost 50ms, qps = 1000/50 = 20)\n                int preReadCount = (XxlJobAdminConfig.getAdminConfig().getTriggerPoolFastMax() + XxlJobAdminConfig.getAdminConfig().getTriggerPoolSlowMax()) * 20;\n\n                while (!scheduleThreadToStop) {\n\n                    // Scan Job\n                    long start = System.currentTimeMillis();\n\n                    Connection conn = null;\n                    Boolean connAutoCommit = null;\n                    PreparedStatement preparedStatement = null;\n\n                    boolean preReadSuc = true;\n                    try {\n\n                        conn = XxlJobAdminConfig.getAdminConfig().getDataSource().getConnection();\n                        connAutoCommit = conn.getAutoCommit();\n                        conn.setAutoCommit(false);\n\n                        preparedStatement = conn.prepareStatement(\"select * from xxl_job_lock where lock_name = 'schedule_lock' for update\");\n                        preparedStatement.execute();\n\n                        // tx start\n\n                        // 1、pre read\n                        long nowTime = System.currentTimeMillis();\n                        List<XxlJobInfo> scheduleList = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().scheduleJobQuery(nowTime + PRE_READ_MS, preReadCount);\n                        if (scheduleList != null && scheduleList.size() > 0) {\n                            // 2、push time-ring\n                            for (XxlJobInfo jobInfo : scheduleList) {\n\n                                // time-ring jump\n                                if (nowTime > jobInfo.getTriggerNextTime() + PRE_READ_MS) {\n                                    // 2.1、trigger-expire > 5s：pass && make next-trigger-time\n                                    logger.warn(\">>>>>>>>>>> xxl-job, schedule misfire, jobId = \" + jobInfo.getId());\n\n                                    // 1、misfire match\n                                    MisfireStrategyEnum misfireStrategyEnum = MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), MisfireStrategyEnum.DO_NOTHING);\n                                    if (MisfireStrategyEnum.FIRE_ONCE_NOW == misfireStrategyEnum) {\n                                        // FIRE_ONCE_NOW 》 trigger\n                                        JobTriggerPoolHelper.trigger(jobInfo.getId(), TriggerTypeEnum.MISFIRE, -1, null, null, null);\n                                        logger.debug(\">>>>>>>>>>> xxl-job, schedule push trigger : jobId = \" + jobInfo.getId());\n                                    }\n\n                                    // 2、fresh next\n                                    refreshNextValidTime(jobInfo, new Date());\n\n                                } else if (nowTime > jobInfo.getTriggerNextTime()) {\n                                    // 2.2、trigger-expire < 5s：direct-trigger && make next-trigger-time\n\n                                    // 1、trigger\n                                    JobTriggerPoolHelper.trigger(jobInfo.getId(), TriggerTypeEnum.CRON, -1, null, null, null);\n                                    logger.debug(\">>>>>>>>>>> xxl-job, schedule push trigger : jobId = \" + jobInfo.getId());\n\n                                    // 2、fresh next\n                                    refreshNextValidTime(jobInfo, new Date());\n\n                                    // next-trigger-time in 5s, pre-read again\n                                    if (jobInfo.getTriggerStatus() == 1 && nowTime + PRE_READ_MS > jobInfo.getTriggerNextTime()) {\n\n                                        // 1、make ring second\n                                        int ringSecond = (int) ((jobInfo.getTriggerNextTime() / 1000) % 60);\n\n                                        // 2、push time ring\n                                        pushTimeRing(ringSecond, jobInfo.getId());\n\n                                        // 3、fresh next\n                                        refreshNextValidTime(jobInfo, new Date(jobInfo.getTriggerNextTime()));\n\n                                    }\n\n                                } else {\n                                    // 2.3、trigger-pre-read：time-ring trigger && make next-trigger-time\n\n                                    // 1、make ring second\n                                    int ringSecond = (int) ((jobInfo.getTriggerNextTime() / 1000) % 60);\n\n                                    // 2、push time ring\n                                    pushTimeRing(ringSecond, jobInfo.getId());\n\n                                    // 3、fresh next\n                                    refreshNextValidTime(jobInfo, new Date(jobInfo.getTriggerNextTime()));\n\n                                }\n\n                            }\n\n                            // 3、update trigger info\n                            for (XxlJobInfo jobInfo : scheduleList) {\n                                XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().scheduleUpdate(jobInfo);\n                            }\n\n                        } else {\n                            preReadSuc = false;\n                        }\n\n                        // tx stop\n\n\n                    } catch (Exception e) {\n                        if (!scheduleThreadToStop) {\n                            logger.error(\">>>>>>>>>>> xxl-job, JobScheduleHelper#scheduleThread error:{}\", e);\n                        }\n                    } finally {\n\n                        // commit\n                        if (conn != null) {\n                            try {\n                                conn.commit();\n                            } catch (SQLException e) {\n                                if (!scheduleThreadToStop) {\n                                    logger.error(e.getMessage(), e);\n                                }\n                            }\n                            try {\n                                conn.setAutoCommit(connAutoCommit);\n                            } catch (SQLException e) {\n                                if (!scheduleThreadToStop) {\n                                    logger.error(e.getMessage(), e);\n                                }\n                            }\n                            try {\n                                conn.close();\n                            } catch (SQLException e) {\n                                if (!scheduleThreadToStop) {\n                                    logger.error(e.getMessage(), e);\n                                }\n                            }\n                        }\n\n                        // close PreparedStatement\n                        if (null != preparedStatement) {\n                            try {\n                                preparedStatement.close();\n                            } catch (SQLException e) {\n                                if (!scheduleThreadToStop) {\n                                    logger.error(e.getMessage(), e);\n                                }\n                            }\n                        }\n                    }\n                    long cost = System.currentTimeMillis() - start;\n\n\n                    // Wait seconds, align second\n                    if (cost < 1000) {  // scan-overtime, not wait\n                        try {\n                            // pre-read period: success > scan each second; fail > skip this period;\n                            TimeUnit.MILLISECONDS.sleep((preReadSuc ? 1000 : PRE_READ_MS) - System.currentTimeMillis() % 1000);\n                        } catch (InterruptedException e) {\n                            if (!scheduleThreadToStop) {\n                                logger.error(e.getMessage(), e);\n                            }\n                        }\n                    }\n\n                }\n\n                logger.info(\">>>>>>>>>>> xxl-job, JobScheduleHelper#scheduleThread stop\");\n            }\n        });\n        scheduleThread.setDaemon(true);\n        scheduleThread.setName(\"xxl-job, admin JobScheduleHelper#scheduleThread\");\n        scheduleThread.start();\n\n\n        // ring thread\n        ringThread = new Thread(new Runnable() {\n            @Override\n            public void run() {\n\n                while (!ringThreadToStop) {\n\n                    // align second\n                    try {\n                        TimeUnit.MILLISECONDS.sleep(1000 - System.currentTimeMillis() % 1000);\n                    } catch (InterruptedException e) {\n                        if (!ringThreadToStop) {\n                            logger.error(e.getMessage(), e);\n                        }\n                    }\n\n                    try {\n                        // second data\n                        List<Integer> ringItemData = new ArrayList<>();\n                        int nowSecond = Calendar.getInstance().get(Calendar.SECOND);   // 避免处理耗时太长，跨过刻度，向前校验一个刻度；\n                        for (int i = 0; i < 2; i++) {\n                            List<Integer> tmpData = ringData.remove((nowSecond + 60 - i) % 60);\n                            if (tmpData != null) {\n                                ringItemData.addAll(tmpData);\n                            }\n                        }\n\n                        // ring trigger\n                        logger.debug(\">>>>>>>>>>> xxl-job, time-ring beat : \" + nowSecond + \" = \" + Arrays.asList(ringItemData));\n                        if (ringItemData.size() > 0) {\n                            // do trigger\n                            for (int jobId : ringItemData) {\n                                // do trigger\n                                JobTriggerPoolHelper.trigger(jobId, TriggerTypeEnum.CRON, -1, null, null, null);\n                            }\n                            // clear\n                            ringItemData.clear();\n                        }\n                    } catch (Exception e) {\n                        if (!ringThreadToStop) {\n                            logger.error(\">>>>>>>>>>> xxl-job, JobScheduleHelper#ringThread error:{}\", e);\n                        }\n                    }\n                }\n                logger.info(\">>>>>>>>>>> xxl-job, JobScheduleHelper#ringThread stop\");\n            }\n        });\n        ringThread.setDaemon(true);\n        ringThread.setName(\"xxl-job, admin JobScheduleHelper#ringThread\");\n        ringThread.start();\n    }\n\n    private void refreshNextValidTime(XxlJobInfo jobInfo, Date fromTime) throws Exception {\n        Date nextValidTime = generateNextValidTime(jobInfo, fromTime);\n        if (nextValidTime != null) {\n            jobInfo.setTriggerLastTime(jobInfo.getTriggerNextTime());\n            jobInfo.setTriggerNextTime(nextValidTime.getTime());\n        } else {\n            jobInfo.setTriggerStatus(0);\n            jobInfo.setTriggerLastTime(0);\n            jobInfo.setTriggerNextTime(0);\n            logger.warn(\">>>>>>>>>>> xxl-job, refreshNextValidTime fail for job: jobId={}, scheduleType={}, scheduleConf={}\",\n                jobInfo.getId(), jobInfo.getScheduleType(), jobInfo.getScheduleConf());\n        }\n    }\n\n    private void pushTimeRing(int ringSecond, int jobId) {\n        // push async ring\n        List<Integer> ringItemData = ringData.get(ringSecond);\n        if (ringItemData == null) {\n            ringItemData = new ArrayList<Integer>();\n            ringData.put(ringSecond, ringItemData);\n        }\n        ringItemData.add(jobId);\n\n        logger.debug(\">>>>>>>>>>> xxl-job, schedule push time-ring : \" + ringSecond + \" = \" + Arrays.asList(ringItemData));\n    }\n\n    public void toStop() {\n\n        // 1、stop schedule\n        scheduleThreadToStop = true;\n        try {\n            TimeUnit.SECONDS.sleep(1);  // wait\n        } catch (InterruptedException e) {\n            logger.error(e.getMessage(), e);\n        }\n        if (scheduleThread.getState() != Thread.State.TERMINATED) {\n            // interrupt and wait\n            scheduleThread.interrupt();\n            try {\n                scheduleThread.join();\n            } catch (InterruptedException e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n\n        // if has ring data\n        boolean hasRingData = false;\n        if (!ringData.isEmpty()) {\n            for (int second : ringData.keySet()) {\n                List<Integer> tmpData = ringData.get(second);\n                if (tmpData != null && tmpData.size() > 0) {\n                    hasRingData = true;\n                    break;\n                }\n            }\n        }\n        if (hasRingData) {\n            try {\n                TimeUnit.SECONDS.sleep(8);\n            } catch (InterruptedException e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n\n        // stop ring (wait job-in-memory stop)\n        ringThreadToStop = true;\n        try {\n            TimeUnit.SECONDS.sleep(1);\n        } catch (InterruptedException e) {\n            logger.error(e.getMessage(), e);\n        }\n        if (ringThread.getState() != Thread.State.TERMINATED) {\n            // interrupt and wait\n            ringThread.interrupt();\n            try {\n                ringThread.join();\n            } catch (InterruptedException e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n\n        logger.info(\">>>>>>>>>>> xxl-job, JobScheduleHelper stop\");\n    }\n\n\n    // ---------------------- tools ----------------------\n    public static Date generateNextValidTime(XxlJobInfo jobInfo, Date fromTime) throws Exception {\n        ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), null);\n        if (ScheduleTypeEnum.CRON == scheduleTypeEnum) {\n            Date nextValidTime = new CronExpression(jobInfo.getScheduleConf()).getNextValidTimeAfter(fromTime);\n            return nextValidTime;\n        } else if (ScheduleTypeEnum.FIX_RATE == scheduleTypeEnum /*|| ScheduleTypeEnum.FIX_DELAY == scheduleTypeEnum*/) {\n            return new Date(fromTime.getTime() + Integer.valueOf(jobInfo.getScheduleConf()) * 1000);\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobTriggerPoolHelper.java",
    "content": "package com.xxl.job.admin.core.thread;\n\nimport com.xxl.job.admin.core.conf.XxlJobAdminConfig;\nimport com.xxl.job.admin.core.trigger.TriggerTypeEnum;\nimport com.xxl.job.admin.core.trigger.XxlJobTrigger;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.concurrent.*;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * job trigger thread pool helper\n *\n * @author xuxueli 2018-07-03 21:08:07\n */\npublic class JobTriggerPoolHelper {\n    private static Logger logger = LoggerFactory.getLogger(JobTriggerPoolHelper.class);\n\n\n    // ---------------------- trigger pool ----------------------\n\n    // fast/slow thread pool\n    private ThreadPoolExecutor fastTriggerPool = null;\n    private ThreadPoolExecutor slowTriggerPool = null;\n\n    public void start() {\n        fastTriggerPool = new ThreadPoolExecutor(\n            10,\n            XxlJobAdminConfig.getAdminConfig().getTriggerPoolFastMax(),\n            60L,\n            TimeUnit.SECONDS,\n            new LinkedBlockingQueue<Runnable>(1000),\n            new ThreadFactory() {\n                @Override\n                public Thread newThread(Runnable r) {\n                    return new Thread(r, \"xxl-job, admin JobTriggerPoolHelper-fastTriggerPool-\" + r.hashCode());\n                }\n            });\n\n        slowTriggerPool = new ThreadPoolExecutor(\n            10,\n            XxlJobAdminConfig.getAdminConfig().getTriggerPoolSlowMax(),\n            60L,\n            TimeUnit.SECONDS,\n            new LinkedBlockingQueue<Runnable>(2000),\n            new ThreadFactory() {\n                @Override\n                public Thread newThread(Runnable r) {\n                    return new Thread(r, \"xxl-job, admin JobTriggerPoolHelper-slowTriggerPool-\" + r.hashCode());\n                }\n            });\n    }\n\n\n    public void stop() {\n        //triggerPool.shutdown();\n        fastTriggerPool.shutdownNow();\n        slowTriggerPool.shutdownNow();\n        logger.info(\">>>>>>>>> xxl-job trigger thread pool shutdown success.\");\n    }\n\n\n    // job timeout count\n    private volatile long minTim = System.currentTimeMillis() / 60000;     // ms > min\n    private volatile ConcurrentMap<Integer, AtomicInteger> jobTimeoutCountMap = new ConcurrentHashMap<>();\n\n\n    /**\n     * add trigger\n     */\n    public void addTrigger(final int jobId,\n                           final TriggerTypeEnum triggerType,\n                           final int failRetryCount,\n                           final String executorShardingParam,\n                           final String executorParam,\n                           final String addressList) {\n\n        // choose thread pool\n        ThreadPoolExecutor triggerPool_ = fastTriggerPool;\n        AtomicInteger jobTimeoutCount = jobTimeoutCountMap.get(jobId);\n        if (jobTimeoutCount != null && jobTimeoutCount.get() > 10) {      // job-timeout 10 times in 1 min\n            triggerPool_ = slowTriggerPool;\n        }\n\n        // trigger\n        triggerPool_.execute(new Runnable() {\n            @Override\n            public void run() {\n\n                long start = System.currentTimeMillis();\n\n                try {\n                    // do trigger\n                    XxlJobTrigger.trigger(jobId, triggerType, failRetryCount, executorShardingParam, executorParam, addressList);\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                } finally {\n\n                    // check timeout-count-map\n                    long minTim_now = System.currentTimeMillis() / 60000;\n                    if (minTim != minTim_now) {\n                        minTim = minTim_now;\n                        jobTimeoutCountMap.clear();\n                    }\n\n                    // incr timeout-count-map\n                    long cost = System.currentTimeMillis() - start;\n                    if (cost > 500) {       // ob-timeout threshold 500ms\n                        AtomicInteger timeoutCount = jobTimeoutCountMap.putIfAbsent(jobId, new AtomicInteger(1));\n                        if (timeoutCount != null) {\n                            timeoutCount.incrementAndGet();\n                        }\n                    }\n\n                }\n\n            }\n        });\n    }\n\n\n    // ---------------------- helper ----------------------\n\n    private static JobTriggerPoolHelper helper = new JobTriggerPoolHelper();\n\n    public static void toStart() {\n        helper.start();\n    }\n\n    public static void toStop() {\n        helper.stop();\n    }\n\n    /**\n     * @param jobId\n     * @param triggerType\n     * @param failRetryCount        >=0: use this param\n     *                              <0: use param from job info config\n     * @param executorShardingParam\n     * @param executorParam         null: use job param\n     *                              not null: cover job param\n     */\n    public static void trigger(int jobId, TriggerTypeEnum triggerType, int failRetryCount, String executorShardingParam, String executorParam, String addressList) {\n        helper.addTrigger(jobId, triggerType, failRetryCount, executorShardingParam, executorParam, addressList);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/trigger/TriggerTypeEnum.java",
    "content": "package com.xxl.job.admin.core.trigger;\n\nimport com.xxl.job.admin.core.util.I18nUtil;\n\n/**\n * trigger type enum\n *\n * @author xuxueli 2018-09-16 04:56:41\n */\npublic enum TriggerTypeEnum {\n\n    MANUAL(I18nUtil.getString(\"jobconf_trigger_type_manual\")),\n    CRON(I18nUtil.getString(\"jobconf_trigger_type_cron\")),\n    RETRY(I18nUtil.getString(\"jobconf_trigger_type_retry\")),\n    PARENT(I18nUtil.getString(\"jobconf_trigger_type_parent\")),\n    API(I18nUtil.getString(\"jobconf_trigger_type_api\")),\n    MISFIRE(I18nUtil.getString(\"jobconf_trigger_type_misfire\"));\n\n    private TriggerTypeEnum(String title) {\n        this.title = title;\n    }\n\n    private String title;\n\n    public String getTitle() {\n        return title;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/trigger/XxlJobTrigger.java",
    "content": "package com.xxl.job.admin.core.trigger;\n\nimport com.xxl.job.admin.core.conf.XxlJobAdminConfig;\nimport com.xxl.job.admin.core.model.XxlJobGroup;\nimport com.xxl.job.admin.core.model.XxlJobInfo;\nimport com.xxl.job.admin.core.model.XxlJobLog;\nimport com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;\nimport com.xxl.job.admin.core.scheduler.XxlJobScheduler;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport com.xxl.job.core.biz.ExecutorBiz;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.biz.model.TriggerParam;\nimport com.xxl.job.core.enums.ExecutorBlockStrategyEnum;\nimport com.xxl.job.core.util.IpUtil;\nimport com.xxl.job.core.util.ThrowableUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Date;\n\n/**\n * xxl-job trigger\n * Created by xuxueli on 17/7/13.\n */\npublic class XxlJobTrigger {\n    private static Logger logger = LoggerFactory.getLogger(XxlJobTrigger.class);\n\n    /**\n     * trigger job\n     *\n     * @param jobId\n     * @param triggerType\n     * @param failRetryCount        >=0: use this param\n     *                              <0: use param from job info config\n     * @param executorShardingParam\n     * @param executorParam         null: use job param\n     *                              not null: cover job param\n     * @param addressList           null: use executor addressList\n     *                              not null: cover\n     */\n    public static void trigger(int jobId,\n                               TriggerTypeEnum triggerType,\n                               int failRetryCount,\n                               String executorShardingParam,\n                               String executorParam,\n                               String addressList) {\n\n        // load data\n        XxlJobInfo jobInfo = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(jobId);\n        if (jobInfo == null) {\n            logger.warn(\">>>>>>>>>>>> trigger fail, jobId invalid，jobId={}\", jobId);\n            return;\n        }\n        if (executorParam != null) {\n            jobInfo.setExecutorParam(executorParam);\n        }\n        int finalFailRetryCount = failRetryCount >= 0 ? failRetryCount : jobInfo.getExecutorFailRetryCount();\n        XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(jobInfo.getJobGroup());\n\n        // cover addressList\n        if (addressList != null && addressList.trim().length() > 0) {\n            group.setAddressType(1);\n            group.setAddressList(addressList.trim());\n        }\n\n        // sharding param\n        int[] shardingParam = null;\n        if (executorShardingParam != null) {\n            String[] shardingArr = executorShardingParam.split(\"/\");\n            if (shardingArr.length == 2 && isNumeric(shardingArr[0]) && isNumeric(shardingArr[1])) {\n                shardingParam = new int[2];\n                shardingParam[0] = Integer.valueOf(shardingArr[0]);\n                shardingParam[1] = Integer.valueOf(shardingArr[1]);\n            }\n        }\n        if (ExecutorRouteStrategyEnum.SHARDING_BROADCAST == ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null)\n            && group.getRegistryList() != null && !group.getRegistryList().isEmpty()\n            && shardingParam == null) {\n            for (int i = 0; i < group.getRegistryList().size(); i++) {\n                processTrigger(group, jobInfo, finalFailRetryCount, triggerType, i, group.getRegistryList().size());\n            }\n        } else {\n            if (shardingParam == null) {\n                shardingParam = new int[]{0, 1};\n            }\n            processTrigger(group, jobInfo, finalFailRetryCount, triggerType, shardingParam[0], shardingParam[1]);\n        }\n\n    }\n\n    private static boolean isNumeric(String str) {\n        try {\n            int result = Integer.valueOf(str);\n            return true;\n        } catch (NumberFormatException e) {\n            return false;\n        }\n    }\n\n    /**\n     * @param group               job group, registry list may be empty\n     * @param jobInfo\n     * @param finalFailRetryCount\n     * @param triggerType\n     * @param index               sharding index\n     * @param total               sharding index\n     */\n    private static void processTrigger(XxlJobGroup group, XxlJobInfo jobInfo, int finalFailRetryCount, TriggerTypeEnum triggerType, int index, int total) {\n\n        // param\n        ExecutorBlockStrategyEnum blockStrategy = ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), ExecutorBlockStrategyEnum.SERIAL_EXECUTION);  // block strategy\n        ExecutorRouteStrategyEnum executorRouteStrategyEnum = ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null);    // route strategy\n        String shardingParam = (ExecutorRouteStrategyEnum.SHARDING_BROADCAST == executorRouteStrategyEnum) ? String.valueOf(index).concat(\"/\").concat(String.valueOf(total)) : null;\n\n        // 1、save log-id\n        XxlJobLog jobLog = new XxlJobLog();\n        jobLog.setJobGroup(jobInfo.getJobGroup());\n        jobLog.setJobId(jobInfo.getId());\n        jobLog.setTriggerTime(new Date());\n        XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().save(jobLog);\n        logger.debug(\">>>>>>>>>>> xxl-job trigger start, jobId:{}\", jobLog.getId());\n\n        // 2、init trigger-param\n        TriggerParam triggerParam = new TriggerParam();\n        triggerParam.setJobId(jobInfo.getId());\n        triggerParam.setExecutorHandler(jobInfo.getExecutorHandler());\n        triggerParam.setExecutorParams(jobInfo.getExecutorParam());\n        triggerParam.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy());\n        triggerParam.setExecutorTimeout(jobInfo.getExecutorTimeout());\n        triggerParam.setLogId(jobLog.getId());\n        triggerParam.setLogDateTime(jobLog.getTriggerTime().getTime());\n        triggerParam.setGlueType(jobInfo.getGlueType());\n        triggerParam.setGlueSource(jobInfo.getGlueSource());\n        triggerParam.setGlueUpdatetime(jobInfo.getGlueUpdatetime().getTime());\n        triggerParam.setBroadcastIndex(index);\n        triggerParam.setBroadcastTotal(total);\n\n        // 3、init address\n        String address = null;\n        ReturnT<String> routeAddressResult = null;\n        if (group.getRegistryList() != null && !group.getRegistryList().isEmpty()) {\n            if (ExecutorRouteStrategyEnum.SHARDING_BROADCAST == executorRouteStrategyEnum) {\n                if (index < group.getRegistryList().size()) {\n                    address = group.getRegistryList().get(index);\n                } else {\n                    address = group.getRegistryList().get(0);\n                }\n            } else {\n                routeAddressResult = executorRouteStrategyEnum.getRouter().route(triggerParam, group.getRegistryList());\n                if (routeAddressResult.getCode() == ReturnT.SUCCESS_CODE) {\n                    address = routeAddressResult.getContent();\n                }\n            }\n        } else {\n            routeAddressResult = new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString(\"jobconf_trigger_address_empty\"));\n        }\n\n        // 4、trigger remote executor\n        ReturnT<String> triggerResult = null;\n        if (address != null) {\n            triggerResult = runExecutor(triggerParam, address);\n        } else {\n            triggerResult = new ReturnT<String>(ReturnT.FAIL_CODE, null);\n        }\n\n        // 5、collection trigger info\n        StringBuffer triggerMsgSb = new StringBuffer();\n        triggerMsgSb.append(I18nUtil.getString(\"jobconf_trigger_type\")).append(\"：\").append(triggerType.getTitle());\n        triggerMsgSb.append(\"<br>\").append(I18nUtil.getString(\"jobconf_trigger_admin_adress\")).append(\"：\").append(IpUtil.getIp());\n        triggerMsgSb.append(\"<br>\").append(I18nUtil.getString(\"jobconf_trigger_exe_regtype\")).append(\"：\")\n            .append((group.getAddressType() == 0) ? I18nUtil.getString(\"jobgroup_field_addressType_0\") : I18nUtil.getString(\"jobgroup_field_addressType_1\"));\n        triggerMsgSb.append(\"<br>\").append(I18nUtil.getString(\"jobconf_trigger_exe_regaddress\")).append(\"：\").append(group.getRegistryList());\n        triggerMsgSb.append(\"<br>\").append(I18nUtil.getString(\"jobinfo_field_executorRouteStrategy\")).append(\"：\").append(executorRouteStrategyEnum.getTitle());\n        if (shardingParam != null) {\n            triggerMsgSb.append(\"(\" + shardingParam + \")\");\n        }\n        triggerMsgSb.append(\"<br>\").append(I18nUtil.getString(\"jobinfo_field_executorBlockStrategy\")).append(\"：\").append(blockStrategy.getTitle());\n        triggerMsgSb.append(\"<br>\").append(I18nUtil.getString(\"jobinfo_field_timeout\")).append(\"：\").append(jobInfo.getExecutorTimeout());\n        triggerMsgSb.append(\"<br>\").append(I18nUtil.getString(\"jobinfo_field_executorFailRetryCount\")).append(\"：\").append(finalFailRetryCount);\n\n        triggerMsgSb.append(\"<br><br><span style=\\\"color:#00c0ef;\\\" > >>>>>>>>>>>\" + I18nUtil.getString(\"jobconf_trigger_run\") + \"<<<<<<<<<<< </span><br>\")\n            .append((routeAddressResult != null && routeAddressResult.getMsg() != null) ? routeAddressResult.getMsg() + \"<br><br>\" : \"\").append(triggerResult.getMsg() != null ? triggerResult.getMsg() : \"\");\n\n        // 6、save log trigger-info\n        jobLog.setExecutorAddress(address);\n        jobLog.setExecutorHandler(jobInfo.getExecutorHandler());\n        jobLog.setExecutorParam(jobInfo.getExecutorParam());\n        jobLog.setExecutorShardingParam(shardingParam);\n        jobLog.setExecutorFailRetryCount(finalFailRetryCount);\n        //jobLog.setTriggerTime();\n        jobLog.setTriggerCode(triggerResult.getCode());\n        jobLog.setTriggerMsg(triggerMsgSb.toString());\n        XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateTriggerInfo(jobLog);\n\n        logger.debug(\">>>>>>>>>>> xxl-job trigger end, jobId:{}\", jobLog.getId());\n    }\n\n    /**\n     * run executor\n     *\n     * @param triggerParam\n     * @param address\n     * @return\n     */\n    public static ReturnT<String> runExecutor(TriggerParam triggerParam, String address) {\n        ReturnT<String> runResult = null;\n        try {\n            ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(address);\n            runResult = executorBiz.run(triggerParam);\n        } catch (Exception e) {\n            logger.error(\">>>>>>>>>>> xxl-job trigger error, please check if the executor[{}] is running.\", address, e);\n            runResult = new ReturnT<String>(ReturnT.FAIL_CODE, ThrowableUtil.toString(e));\n        }\n\n        StringBuffer runResultSB = new StringBuffer(I18nUtil.getString(\"jobconf_trigger_run\") + \"：\");\n        runResultSB.append(\"<br>address：\").append(address);\n        runResultSB.append(\"<br>code：\").append(runResult.getCode());\n        runResultSB.append(\"<br>msg：\").append(runResult.getMsg());\n\n        runResult.setMsg(runResultSB.toString());\n        return runResult;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/CookieUtil.java",
    "content": "package com.xxl.job.admin.core.util;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n/**\n * Cookie.Util\n *\n * @author xuxueli 2015-12-12 18:01:06\n */\npublic class CookieUtil {\n\n    // 默认缓存时间,单位/秒, 2H\n    private static final int COOKIE_MAX_AGE = Integer.MAX_VALUE;\n    // 保存路径,根路径\n    private static final String COOKIE_PATH = \"/\";\n\n    /**\n     * 保存\n     *\n     * @param response\n     * @param key\n     * @param value\n     * @param ifRemember\n     */\n    public static void set(HttpServletResponse response, String key, String value, boolean ifRemember) {\n        int age = ifRemember ? COOKIE_MAX_AGE : -1;\n        set(response, key, value, null, COOKIE_PATH, age, true);\n    }\n\n    /**\n     * 保存\n     *\n     * @param response\n     * @param key\n     * @param value\n     * @param maxAge\n     */\n    private static void set(HttpServletResponse response, String key, String value, String domain, String path, int maxAge, boolean isHttpOnly) {\n        Cookie cookie = new Cookie(key, value);\n        if (domain != null) {\n            cookie.setDomain(domain);\n        }\n        cookie.setPath(path);\n        cookie.setMaxAge(maxAge);\n        cookie.setHttpOnly(isHttpOnly);\n        response.addCookie(cookie);\n    }\n\n    /**\n     * 查询value\n     *\n     * @param request\n     * @param key\n     * @return\n     */\n    public static String getValue(HttpServletRequest request, String key) {\n        Cookie cookie = get(request, key);\n        if (cookie != null) {\n            return cookie.getValue();\n        }\n        return null;\n    }\n\n    /**\n     * 查询Cookie\n     *\n     * @param request\n     * @param key\n     */\n    private static Cookie get(HttpServletRequest request, String key) {\n        Cookie[] arr_cookie = request.getCookies();\n        if (arr_cookie != null && arr_cookie.length > 0) {\n            for (Cookie cookie : arr_cookie) {\n                if (cookie.getName().equals(key)) {\n                    return cookie;\n                }\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 删除Cookie\n     *\n     * @param request\n     * @param response\n     * @param key\n     */\n    public static void remove(HttpServletRequest request, HttpServletResponse response, String key) {\n        Cookie cookie = get(request, key);\n        if (cookie != null) {\n            set(response, key, \"\", null, COOKIE_PATH, 0, true);\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/FtlUtil.java",
    "content": "package com.xxl.job.admin.core.util;\n\nimport freemarker.ext.beans.BeansWrapper;\nimport freemarker.ext.beans.BeansWrapperBuilder;\nimport freemarker.template.Configuration;\nimport freemarker.template.TemplateHashModel;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * ftl util\n *\n * @author xuxueli 2018-01-17 20:37:48\n */\npublic class FtlUtil {\n    private static Logger logger = LoggerFactory.getLogger(FtlUtil.class);\n\n    private static BeansWrapper wrapper = new BeansWrapperBuilder(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS).build();     //BeansWrapper.getDefaultInstance();\n\n    public static TemplateHashModel generateStaticModel(String packageName) {\n        try {\n            TemplateHashModel staticModels = wrapper.getStaticModels();\n            TemplateHashModel fileStatics = (TemplateHashModel) staticModels.get(packageName);\n            return fileStatics;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/I18nUtil.java",
    "content": "package com.xxl.job.admin.core.util;\n\nimport com.xxl.job.admin.core.conf.XxlJobAdminConfig;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.support.EncodedResource;\nimport org.springframework.core.io.support.PropertiesLoaderUtils;\n\nimport java.io.IOException;\nimport java.text.MessageFormat;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\n\n/**\n * i18n util\n *\n * @author xuxueli 2018-01-17 20:39:06\n */\npublic class I18nUtil {\n    private static Logger logger = LoggerFactory.getLogger(I18nUtil.class);\n\n    private static Properties prop = null;\n\n    public static Properties loadI18nProp() {\n        if (prop != null) {\n            return prop;\n        }\n        try {\n            // build i18n prop\n            String i18n = XxlJobAdminConfig.getAdminConfig().getI18n();\n            String i18nFile = MessageFormat.format(\"i18n/message_{0}.properties\", i18n);\n\n            // load prop\n            Resource resource = new ClassPathResource(i18nFile);\n            EncodedResource encodedResource = new EncodedResource(resource, \"UTF-8\");\n            prop = PropertiesLoaderUtils.loadProperties(encodedResource);\n        } catch (IOException e) {\n            logger.error(e.getMessage(), e);\n        }\n        return prop;\n    }\n\n    /**\n     * get val of i18n key\n     *\n     * @param key\n     * @return\n     */\n    public static String getString(String key) {\n        return loadI18nProp().getProperty(key);\n    }\n\n    /**\n     * get mult val of i18n mult key, as json\n     *\n     * @param keys\n     * @return\n     */\n    public static String getMultString(String... keys) {\n        Map<String, String> map = new HashMap<String, String>();\n\n        Properties prop = loadI18nProp();\n        if (keys != null && keys.length > 0) {\n            for (String key : keys) {\n                map.put(key, prop.getProperty(key));\n            }\n        } else {\n            for (String key : prop.stringPropertyNames()) {\n                map.put(key, prop.getProperty(key));\n            }\n        }\n\n        String json = JacksonUtil.writeValueAsString(map);\n        return json;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/JacksonUtil.java",
    "content": "package com.xxl.job.admin.core.util;\n\nimport com.fasterxml.jackson.core.JsonGenerationException;\nimport com.fasterxml.jackson.core.JsonParseException;\nimport com.fasterxml.jackson.databind.JavaType;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.IOException;\n\n/**\n * Jackson util\n * <p>\n * 1、obj need private and set/get；\n * 2、do not support inner class；\n *\n * @author xuxueli 2015-9-25 18:02:56\n */\npublic class JacksonUtil {\n    private static Logger logger = LoggerFactory.getLogger(JacksonUtil.class);\n\n    private final static ObjectMapper OBJECT_MAPPER = new ObjectMapper();\n\n    public static ObjectMapper getInstance() {\n        return OBJECT_MAPPER;\n    }\n\n    /**\n     * bean、array、List、Map --> json\n     *\n     * @param obj\n     * @return json string\n     * @throws Exception\n     */\n    public static String writeValueAsString(Object obj) {\n        try {\n            return getInstance().writeValueAsString(obj);\n        } catch (JsonGenerationException e) {\n            logger.error(e.getMessage(), e);\n        } catch (JsonMappingException e) {\n            logger.error(e.getMessage(), e);\n        } catch (IOException e) {\n            logger.error(e.getMessage(), e);\n        }\n        return null;\n    }\n\n    /**\n     * string --> bean、Map、List(array)\n     *\n     * @param jsonStr\n     * @param clazz\n     * @return obj\n     * @throws Exception\n     */\n    public static <T> T readValue(String jsonStr, Class<T> clazz) {\n        try {\n            return getInstance().readValue(jsonStr, clazz);\n        } catch (JsonParseException e) {\n            logger.error(e.getMessage(), e);\n        } catch (JsonMappingException e) {\n            logger.error(e.getMessage(), e);\n        } catch (IOException e) {\n            logger.error(e.getMessage(), e);\n        }\n        return null;\n    }\n\n    /**\n     * string --> List<Bean>...\n     *\n     * @param jsonStr\n     * @param parametrized\n     * @param parameterClasses\n     * @param <T>\n     * @return\n     */\n    public static <T> T readValue(String jsonStr, Class<?> parametrized, Class<?>... parameterClasses) {\n        try {\n            JavaType javaType = getInstance().getTypeFactory().constructParametricType(parametrized, parameterClasses);\n            return getInstance().readValue(jsonStr, javaType);\n        } catch (JsonParseException e) {\n            logger.error(e.getMessage(), e);\n        } catch (JsonMappingException e) {\n            logger.error(e.getMessage(), e);\n        } catch (IOException e) {\n            logger.error(e.getMessage(), e);\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/LocalCacheUtil.java",
    "content": "package com.xxl.job.admin.core.util;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\n/**\n * local cache tool\n *\n * @author xuxueli 2018-01-22 21:37:34\n */\npublic class LocalCacheUtil {\n\n    private static ConcurrentMap<String, LocalCacheData> cacheRepository = new ConcurrentHashMap<String, LocalCacheData>();   // 类型建议用抽象父类，兼容性更好；\n\n    private static class LocalCacheData {\n        private String key;\n        private Object val;\n        private long timeoutTime;\n\n        public LocalCacheData() {\n        }\n\n        public LocalCacheData(String key, Object val, long timeoutTime) {\n            this.key = key;\n            this.val = val;\n            this.timeoutTime = timeoutTime;\n        }\n\n        public String getKey() {\n            return key;\n        }\n\n        public void setKey(String key) {\n            this.key = key;\n        }\n\n        public Object getVal() {\n            return val;\n        }\n\n        public void setVal(Object val) {\n            this.val = val;\n        }\n\n        public long getTimeoutTime() {\n            return timeoutTime;\n        }\n\n        public void setTimeoutTime(long timeoutTime) {\n            this.timeoutTime = timeoutTime;\n        }\n    }\n\n\n    /**\n     * set cache\n     *\n     * @param key\n     * @param val\n     * @param cacheTime\n     * @return\n     */\n    public static boolean set(String key, Object val, long cacheTime) {\n\n        // clean timeout cache, before set new cache (avoid cache too much)\n        cleanTimeoutCache();\n\n        // set new cache\n        if (key == null || key.trim().length() == 0) {\n            return false;\n        }\n        if (val == null) {\n            remove(key);\n        }\n        if (cacheTime <= 0) {\n            remove(key);\n        }\n        long timeoutTime = System.currentTimeMillis() + cacheTime;\n        LocalCacheData localCacheData = new LocalCacheData(key, val, timeoutTime);\n        cacheRepository.put(localCacheData.getKey(), localCacheData);\n        return true;\n    }\n\n    /**\n     * remove cache\n     *\n     * @param key\n     * @return\n     */\n    public static boolean remove(String key) {\n        if (key == null || key.trim().length() == 0) {\n            return false;\n        }\n        cacheRepository.remove(key);\n        return true;\n    }\n\n    /**\n     * get cache\n     *\n     * @param key\n     * @return\n     */\n    public static Object get(String key) {\n        if (key == null || key.trim().length() == 0) {\n            return null;\n        }\n        LocalCacheData localCacheData = cacheRepository.get(key);\n        if (localCacheData != null && System.currentTimeMillis() < localCacheData.getTimeoutTime()) {\n            return localCacheData.getVal();\n        } else {\n            remove(key);\n            return null;\n        }\n    }\n\n    /**\n     * clean timeout cache\n     *\n     * @return\n     */\n    public static boolean cleanTimeoutCache() {\n        if (!cacheRepository.keySet().isEmpty()) {\n            for (String key : cacheRepository.keySet()) {\n                LocalCacheData localCacheData = cacheRepository.get(key);\n                if (localCacheData != null && System.currentTimeMillis() >= localCacheData.getTimeoutTime()) {\n                    cacheRepository.remove(key);\n                }\n            }\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobGroupDao.java",
    "content": "package com.xxl.job.admin.dao;\n\nimport com.xxl.job.admin.core.model.XxlJobGroup;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * Created by xuxueli on 16/9/30.\n */\n@Mapper\npublic interface XxlJobGroupDao {\n\n    public List<XxlJobGroup> findAll();\n\n    public List<XxlJobGroup> findByAddressType(@Param(\"addressType\") int addressType);\n\n    public int save(XxlJobGroup xxlJobGroup);\n\n    public int update(XxlJobGroup xxlJobGroup);\n\n    public int remove(@Param(\"id\") int id);\n\n    public XxlJobGroup load(@Param(\"id\") int id);\n\n    public List<XxlJobGroup> pageList(@Param(\"offset\") int offset,\n                                      @Param(\"pagesize\") int pagesize,\n                                      @Param(\"appname\") String appname,\n                                      @Param(\"title\") String title);\n\n    public int pageListCount(@Param(\"offset\") int offset,\n                             @Param(\"pagesize\") int pagesize,\n                             @Param(\"appname\") String appname,\n                             @Param(\"title\") String title);\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobInfoDao.java",
    "content": "package com.xxl.job.admin.dao;\n\nimport com.xxl.job.admin.core.model.XxlJobInfo;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n\n/**\n * job info\n *\n * @author xuxueli 2016-1-12 18:03:45\n */\n@Mapper\npublic interface XxlJobInfoDao {\n\n    public List<XxlJobInfo> pageList(@Param(\"offset\") int offset,\n                                     @Param(\"pagesize\") int pagesize,\n                                     @Param(\"jobGroup\") int jobGroup,\n                                     @Param(\"triggerStatus\") int triggerStatus,\n                                     @Param(\"jobDesc\") String jobDesc,\n                                     @Param(\"executorHandler\") String executorHandler,\n                                     @Param(\"author\") String author);\n\n    public int pageListCount(@Param(\"offset\") int offset,\n                             @Param(\"pagesize\") int pagesize,\n                             @Param(\"jobGroup\") int jobGroup,\n                             @Param(\"triggerStatus\") int triggerStatus,\n                             @Param(\"jobDesc\") String jobDesc,\n                             @Param(\"executorHandler\") String executorHandler,\n                             @Param(\"author\") String author);\n\n    public int save(XxlJobInfo info);\n\n    public XxlJobInfo loadById(@Param(\"id\") int id);\n\n    public int update(XxlJobInfo xxlJobInfo);\n\n    public int delete(@Param(\"id\") long id);\n\n    public List<XxlJobInfo> getJobsByGroup(@Param(\"jobGroup\") int jobGroup);\n\n    public int findAllCount();\n\n    public List<XxlJobInfo> scheduleJobQuery(@Param(\"maxNextTime\") long maxNextTime, @Param(\"pagesize\") int pagesize);\n\n    public int scheduleUpdate(XxlJobInfo xxlJobInfo);\n\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobLogDao.java",
    "content": "package com.xxl.job.admin.dao;\n\nimport com.xxl.job.admin.core.model.XxlJobLog;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * job log\n *\n * @author xuxueli 2016-1-12 18:03:06\n */\n@Mapper\npublic interface XxlJobLogDao {\n\n    // exist jobId not use jobGroup, not exist use jobGroup\n    public List<XxlJobLog> pageList(@Param(\"offset\") int offset,\n                                    @Param(\"pagesize\") int pagesize,\n                                    @Param(\"jobGroup\") int jobGroup,\n                                    @Param(\"jobId\") int jobId,\n                                    @Param(\"triggerTimeStart\") Date triggerTimeStart,\n                                    @Param(\"triggerTimeEnd\") Date triggerTimeEnd,\n                                    @Param(\"logStatus\") int logStatus);\n\n    public int pageListCount(@Param(\"offset\") int offset,\n                             @Param(\"pagesize\") int pagesize,\n                             @Param(\"jobGroup\") int jobGroup,\n                             @Param(\"jobId\") int jobId,\n                             @Param(\"triggerTimeStart\") Date triggerTimeStart,\n                             @Param(\"triggerTimeEnd\") Date triggerTimeEnd,\n                             @Param(\"logStatus\") int logStatus);\n\n    public XxlJobLog load(@Param(\"id\") long id);\n\n    public long save(XxlJobLog xxlJobLog);\n\n    public int updateTriggerInfo(XxlJobLog xxlJobLog);\n\n    public int updateHandleInfo(XxlJobLog xxlJobLog);\n\n    public int delete(@Param(\"jobId\") int jobId);\n\n    public Map<String, Object> findLogReport(@Param(\"from\") Date from,\n                                             @Param(\"to\") Date to);\n\n    public List<Long> findClearLogIds(@Param(\"jobGroup\") int jobGroup,\n                                      @Param(\"jobId\") int jobId,\n                                      @Param(\"clearBeforeTime\") Date clearBeforeTime,\n                                      @Param(\"clearBeforeNum\") int clearBeforeNum,\n                                      @Param(\"pagesize\") int pagesize);\n\n    public int clearLog(@Param(\"logIds\") List<Long> logIds);\n\n    public List<Long> findFailJobLogIds(@Param(\"pagesize\") int pagesize);\n\n    public int updateAlarmStatus(@Param(\"logId\") long logId,\n                                 @Param(\"oldAlarmStatus\") int oldAlarmStatus,\n                                 @Param(\"newAlarmStatus\") int newAlarmStatus);\n\n    public List<Long> findLostJobIds(@Param(\"losedTime\") Date losedTime);\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobLogGlueDao.java",
    "content": "package com.xxl.job.admin.dao;\n\nimport com.xxl.job.admin.core.model.XxlJobLogGlue;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * job log for glue\n *\n * @author xuxueli 2016-5-19 18:04:56\n */\n@Mapper\npublic interface XxlJobLogGlueDao {\n\n    public int save(XxlJobLogGlue xxlJobLogGlue);\n\n    public List<XxlJobLogGlue> findByJobId(@Param(\"jobId\") int jobId);\n\n    public int removeOld(@Param(\"jobId\") int jobId, @Param(\"limit\") int limit);\n\n    public int deleteByJobId(@Param(\"jobId\") int jobId);\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobLogReportDao.java",
    "content": "package com.xxl.job.admin.dao;\n\nimport com.xxl.job.admin.core.model.XxlJobLogReport;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * job log\n *\n * @author xuxueli 2019-11-22\n */\n@Mapper\npublic interface XxlJobLogReportDao {\n\n    public int save(XxlJobLogReport xxlJobLogReport);\n\n    public int update(XxlJobLogReport xxlJobLogReport);\n\n    public List<XxlJobLogReport> queryLogReport(@Param(\"triggerDayFrom\") Date triggerDayFrom,\n                                                @Param(\"triggerDayTo\") Date triggerDayTo);\n\n    public XxlJobLogReport queryLogReportTotal();\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobRegistryDao.java",
    "content": "package com.xxl.job.admin.dao;\n\nimport com.xxl.job.admin.core.model.XxlJobRegistry;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * Created by xuxueli on 16/9/30.\n */\n@Mapper\npublic interface XxlJobRegistryDao {\n\n    public List<Integer> findDead(@Param(\"timeout\") int timeout,\n                                  @Param(\"nowTime\") Date nowTime);\n\n    public int removeDead(@Param(\"ids\") List<Integer> ids);\n\n    public List<XxlJobRegistry> findAll(@Param(\"timeout\") int timeout,\n                                        @Param(\"nowTime\") Date nowTime);\n\n    public int registryUpdate(@Param(\"registryGroup\") String registryGroup,\n                              @Param(\"registryKey\") String registryKey,\n                              @Param(\"registryValue\") String registryValue,\n                              @Param(\"updateTime\") Date updateTime);\n\n    public int registrySave(@Param(\"registryGroup\") String registryGroup,\n                            @Param(\"registryKey\") String registryKey,\n                            @Param(\"registryValue\") String registryValue,\n                            @Param(\"updateTime\") Date updateTime);\n\n    public int registryDelete(@Param(\"registryGroup\") String registryGroup,\n                              @Param(\"registryKey\") String registryKey,\n                              @Param(\"registryValue\") String registryValue);\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobUserDao.java",
    "content": "package com.xxl.job.admin.dao;\n\nimport com.xxl.job.admin.core.model.XxlJobUser;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * @author xuxueli 2019-05-04 16:44:59\n */\n@Mapper\npublic interface XxlJobUserDao {\n\n    public List<XxlJobUser> pageList(@Param(\"offset\") int offset,\n                                     @Param(\"pagesize\") int pagesize,\n                                     @Param(\"username\") String username,\n                                     @Param(\"role\") int role);\n\n    public int pageListCount(@Param(\"offset\") int offset,\n                             @Param(\"pagesize\") int pagesize,\n                             @Param(\"username\") String username,\n                             @Param(\"role\") int role);\n\n    public XxlJobUser loadByUserName(@Param(\"username\") String username);\n\n    public int save(XxlJobUser xxlJobUser);\n\n    public int update(XxlJobUser xxlJobUser);\n\n    public int delete(@Param(\"id\") int id);\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/service/LoginService.java",
    "content": "package com.xxl.job.admin.service;\n\nimport com.xxl.job.admin.core.model.XxlJobUser;\nimport com.xxl.job.admin.core.util.CookieUtil;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport com.xxl.job.admin.core.util.JacksonUtil;\nimport com.xxl.job.admin.dao.XxlJobUserDao;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.util.DigestUtils;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.math.BigInteger;\n\n/**\n * @author xuxueli 2019-05-04 22:13:264\n */\n@Configuration\npublic class LoginService {\n\n    public static final String LOGIN_IDENTITY_KEY = \"XXL_JOB_LOGIN_IDENTITY\";\n\n    @Resource\n    private XxlJobUserDao xxlJobUserDao;\n\n\n    private String makeToken(XxlJobUser xxlJobUser) {\n        String tokenJson = JacksonUtil.writeValueAsString(xxlJobUser);\n        String tokenHex = new BigInteger(tokenJson.getBytes()).toString(16);\n        return tokenHex;\n    }\n\n    private XxlJobUser parseToken(String tokenHex) {\n        XxlJobUser xxlJobUser = null;\n        if (tokenHex != null) {\n            String tokenJson = new String(new BigInteger(tokenHex, 16).toByteArray());      // username_password(md5)\n            xxlJobUser = JacksonUtil.readValue(tokenJson, XxlJobUser.class);\n        }\n        return xxlJobUser;\n    }\n\n\n    public ReturnT<String> login(HttpServletRequest request, HttpServletResponse response, String username, String password, boolean ifRemember) {\n\n        // param\n        if (username == null || username.trim().length() == 0 || password == null || password.trim().length() == 0) {\n            return new ReturnT<String>(500, I18nUtil.getString(\"login_param_empty\"));\n        }\n\n        // valid passowrd\n        XxlJobUser xxlJobUser = xxlJobUserDao.loadByUserName(username);\n        if (xxlJobUser == null) {\n            return new ReturnT<String>(500, I18nUtil.getString(\"login_param_unvalid\"));\n        }\n        String passwordMd5 = DigestUtils.md5DigestAsHex(password.getBytes());\n        if (!passwordMd5.equals(xxlJobUser.getPassword())) {\n            return new ReturnT<String>(500, I18nUtil.getString(\"login_param_unvalid\"));\n        }\n\n        String loginToken = makeToken(xxlJobUser);\n\n        // do login\n        CookieUtil.set(response, LOGIN_IDENTITY_KEY, loginToken, ifRemember);\n        return ReturnT.SUCCESS;\n    }\n\n    /**\n     * logout\n     *\n     * @param request\n     * @param response\n     */\n    public ReturnT<String> logout(HttpServletRequest request, HttpServletResponse response) {\n        CookieUtil.remove(request, response, LOGIN_IDENTITY_KEY);\n        return ReturnT.SUCCESS;\n    }\n\n    /**\n     * logout\n     *\n     * @param request\n     * @return\n     */\n    public XxlJobUser ifLogin(HttpServletRequest request, HttpServletResponse response) {\n        String cookieToken = CookieUtil.getValue(request, LOGIN_IDENTITY_KEY);\n        if (cookieToken != null) {\n            XxlJobUser cookieUser = null;\n            try {\n                cookieUser = parseToken(cookieToken);\n            } catch (Exception e) {\n                logout(request, response);\n            }\n            if (cookieUser != null) {\n                XxlJobUser dbUser = xxlJobUserDao.loadByUserName(cookieUser.getUsername());\n                if (dbUser != null) {\n                    if (cookieUser.getPassword().equals(dbUser.getPassword())) {\n                        return dbUser;\n                    }\n                }\n            }\n        }\n        return null;\n    }\n\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/service/XxlJobService.java",
    "content": "package com.xxl.job.admin.service;\n\n\nimport com.xxl.job.admin.core.model.XxlJobInfo;\nimport com.xxl.job.core.biz.model.ReturnT;\n\nimport java.util.Date;\nimport java.util.Map;\n\n/**\n * core job action for xxl-job\n *\n * @author xuxueli 2016-5-28 15:30:33\n */\npublic interface XxlJobService {\n\n    /**\n     * page list\n     *\n     * @param start\n     * @param length\n     * @param jobGroup\n     * @param jobDesc\n     * @param executorHandler\n     * @param author\n     * @return\n     */\n    public Map<String, Object> pageList(int start, int length, int jobGroup, int triggerStatus, String jobDesc, String executorHandler, String author);\n\n    /**\n     * add job\n     *\n     * @param jobInfo\n     * @return\n     */\n    public ReturnT<String> add(XxlJobInfo jobInfo);\n\n    /**\n     * update job\n     *\n     * @param jobInfo\n     * @return\n     */\n    public ReturnT<String> update(XxlJobInfo jobInfo);\n\n    /**\n     * remove job\n     * *\n     *\n     * @param id\n     * @return\n     */\n    public ReturnT<String> remove(int id);\n\n    /**\n     * start job\n     *\n     * @param id\n     * @return\n     */\n    public ReturnT<String> start(int id);\n\n    /**\n     * stop job\n     *\n     * @param id\n     * @return\n     */\n    public ReturnT<String> stop(int id);\n\n    /**\n     * dashboard info\n     *\n     * @return\n     */\n    public Map<String, Object> dashboardInfo();\n\n    /**\n     * chart info\n     *\n     * @param startDate\n     * @param endDate\n     * @return\n     */\n    public ReturnT<Map<String, Object>> chartInfo(Date startDate, Date endDate);\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/AdminBizImpl.java",
    "content": "package com.xxl.job.admin.service.impl;\n\nimport com.xxl.job.admin.core.thread.JobCompleteHelper;\nimport com.xxl.job.admin.core.thread.JobRegistryHelper;\nimport com.xxl.job.core.biz.AdminBiz;\nimport com.xxl.job.core.biz.model.HandleCallbackParam;\nimport com.xxl.job.core.biz.model.RegistryParam;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n/**\n * @author xuxueli 2017-07-27 21:54:20\n */\n@Service\npublic class AdminBizImpl implements AdminBiz {\n\n\n    @Override\n    public ReturnT<String> callback(List<HandleCallbackParam> callbackParamList) {\n        return JobCompleteHelper.getInstance().callback(callbackParamList);\n    }\n\n    @Override\n    public ReturnT<String> registry(RegistryParam registryParam) {\n        return JobRegistryHelper.getInstance().registry(registryParam);\n    }\n\n    @Override\n    public ReturnT<String> registryRemove(RegistryParam registryParam) {\n        return JobRegistryHelper.getInstance().registryRemove(registryParam);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java",
    "content": "package com.xxl.job.admin.service.impl;\n\nimport com.xxl.job.admin.core.cron.CronExpression;\nimport com.xxl.job.admin.core.model.XxlJobGroup;\nimport com.xxl.job.admin.core.model.XxlJobInfo;\nimport com.xxl.job.admin.core.model.XxlJobLogReport;\nimport com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;\nimport com.xxl.job.admin.core.scheduler.MisfireStrategyEnum;\nimport com.xxl.job.admin.core.scheduler.ScheduleTypeEnum;\nimport com.xxl.job.admin.core.thread.JobScheduleHelper;\nimport com.xxl.job.admin.core.util.I18nUtil;\nimport com.xxl.job.admin.dao.*;\nimport com.xxl.job.admin.service.XxlJobService;\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.enums.ExecutorBlockStrategyEnum;\nimport com.xxl.job.core.glue.GlueTypeEnum;\nimport com.xxl.job.core.util.DateUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.text.MessageFormat;\nimport java.util.*;\n\n/**\n * core job action for xxl-job\n *\n * @author xuxueli 2016-5-28 15:30:33\n */\n@Service\npublic class XxlJobServiceImpl implements XxlJobService {\n    private static Logger logger = LoggerFactory.getLogger(XxlJobServiceImpl.class);\n\n    @Resource\n    private XxlJobGroupDao xxlJobGroupDao;\n    @Resource\n    private XxlJobInfoDao xxlJobInfoDao;\n    @Resource\n    public XxlJobLogDao xxlJobLogDao;\n    @Resource\n    private XxlJobLogGlueDao xxlJobLogGlueDao;\n    @Resource\n    private XxlJobLogReportDao xxlJobLogReportDao;\n\n    @Override\n    public Map<String, Object> pageList(int start, int length, int jobGroup, int triggerStatus, String jobDesc, String executorHandler, String author) {\n\n        // page list\n        List<XxlJobInfo> list = xxlJobInfoDao.pageList(start, length, jobGroup, triggerStatus, jobDesc, executorHandler, author);\n        int list_count = xxlJobInfoDao.pageListCount(start, length, jobGroup, triggerStatus, jobDesc, executorHandler, author);\n\n        // package result\n        Map<String, Object> maps = new HashMap<String, Object>();\n        maps.put(\"recordsTotal\", list_count);        // 总记录数\n        maps.put(\"recordsFiltered\", list_count);    // 过滤后的总记录数\n        maps.put(\"data\", list);                    // 分页列表\n        return maps;\n    }\n\n    @Override\n    public ReturnT<String> add(XxlJobInfo jobInfo) {\n\n        // valid base\n        XxlJobGroup group = xxlJobGroupDao.load(jobInfo.getJobGroup());\n        if (group == null) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"system_please_choose\") + I18nUtil.getString(\"jobinfo_field_jobgroup\")));\n        }\n        if (jobInfo.getJobDesc() == null || jobInfo.getJobDesc().trim().length() == 0) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"system_please_input\") + I18nUtil.getString(\"jobinfo_field_jobdesc\")));\n        }\n        if (jobInfo.getAuthor() == null || jobInfo.getAuthor().trim().length() == 0) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"system_please_input\") + I18nUtil.getString(\"jobinfo_field_author\")));\n        }\n\n        // valid trigger\n        ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), null);\n        if (scheduleTypeEnum == null) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"schedule_type\") + I18nUtil.getString(\"system_unvalid\")));\n        }\n        if (scheduleTypeEnum == ScheduleTypeEnum.CRON) {\n            if (jobInfo.getScheduleConf() == null || !CronExpression.isValidExpression(jobInfo.getScheduleConf())) {\n                return new ReturnT<String>(ReturnT.FAIL_CODE, \"Cron\" + I18nUtil.getString(\"system_unvalid\"));\n            }\n        } else if (scheduleTypeEnum == ScheduleTypeEnum.FIX_RATE/* || scheduleTypeEnum == ScheduleTypeEnum.FIX_DELAY*/) {\n            if (jobInfo.getScheduleConf() == null) {\n                return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"schedule_type\")));\n            }\n            try {\n                int fixSecond = Integer.valueOf(jobInfo.getScheduleConf());\n                if (fixSecond < 1) {\n                    return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"schedule_type\") + I18nUtil.getString(\"system_unvalid\")));\n                }\n            } catch (Exception e) {\n                return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"schedule_type\") + I18nUtil.getString(\"system_unvalid\")));\n            }\n        }\n\n        // valid job\n        if (GlueTypeEnum.match(jobInfo.getGlueType()) == null) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"jobinfo_field_gluetype\") + I18nUtil.getString(\"system_unvalid\")));\n        }\n        if (GlueTypeEnum.BEAN == GlueTypeEnum.match(jobInfo.getGlueType()) && (jobInfo.getExecutorHandler() == null || jobInfo.getExecutorHandler().trim().length() == 0)) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"system_please_input\") + \"JobHandler\"));\n        }\n        // 》fix \"\\r\" in shell\n        if (GlueTypeEnum.GLUE_SHELL == GlueTypeEnum.match(jobInfo.getGlueType()) && jobInfo.getGlueSource() != null) {\n            jobInfo.setGlueSource(jobInfo.getGlueSource().replaceAll(\"\\r\", \"\"));\n        }\n\n        // valid advanced\n        if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"jobinfo_field_executorRouteStrategy\") + I18nUtil.getString(\"system_unvalid\")));\n        }\n        if (MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), null) == null) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"misfire_strategy\") + I18nUtil.getString(\"system_unvalid\")));\n        }\n        if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"jobinfo_field_executorBlockStrategy\") + I18nUtil.getString(\"system_unvalid\")));\n        }\n\n        // 》ChildJobId valid\n        if (jobInfo.getChildJobId() != null && jobInfo.getChildJobId().trim().length() > 0) {\n            String[] childJobIds = jobInfo.getChildJobId().split(\",\");\n            for (String childJobIdItem : childJobIds) {\n                if (childJobIdItem != null && childJobIdItem.trim().length() > 0 && isNumeric(childJobIdItem)) {\n                    XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.parseInt(childJobIdItem));\n                    if (childJobInfo == null) {\n                        return new ReturnT<String>(ReturnT.FAIL_CODE,\n                            MessageFormat.format((I18nUtil.getString(\"jobinfo_field_childJobId\") + \"({0})\" + I18nUtil.getString(\"system_not_found\")), childJobIdItem));\n                    }\n                } else {\n                    return new ReturnT<String>(ReturnT.FAIL_CODE,\n                        MessageFormat.format((I18nUtil.getString(\"jobinfo_field_childJobId\") + \"({0})\" + I18nUtil.getString(\"system_unvalid\")), childJobIdItem));\n                }\n            }\n\n            // join , avoid \"xxx,,\"\n            String temp = \"\";\n            for (String item : childJobIds) {\n                temp += item + \",\";\n            }\n            temp = temp.substring(0, temp.length() - 1);\n\n            jobInfo.setChildJobId(temp);\n        }\n\n        // add in db\n        jobInfo.setAddTime(new Date());\n        jobInfo.setUpdateTime(new Date());\n        jobInfo.setGlueUpdatetime(new Date());\n        xxlJobInfoDao.save(jobInfo);\n        if (jobInfo.getId() < 1) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"jobinfo_field_add\") + I18nUtil.getString(\"system_fail\")));\n        }\n\n        return new ReturnT<String>(String.valueOf(jobInfo.getId()));\n    }\n\n    private boolean isNumeric(String str) {\n        try {\n            int result = Integer.valueOf(str);\n            return true;\n        } catch (NumberFormatException e) {\n            return false;\n        }\n    }\n\n    @Override\n    public ReturnT<String> update(XxlJobInfo jobInfo) {\n\n        // valid base\n        if (jobInfo.getJobDesc() == null || jobInfo.getJobDesc().trim().length() == 0) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"system_please_input\") + I18nUtil.getString(\"jobinfo_field_jobdesc\")));\n        }\n        if (jobInfo.getAuthor() == null || jobInfo.getAuthor().trim().length() == 0) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"system_please_input\") + I18nUtil.getString(\"jobinfo_field_author\")));\n        }\n\n        // valid trigger\n        ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), null);\n        if (scheduleTypeEnum == null) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"schedule_type\") + I18nUtil.getString(\"system_unvalid\")));\n        }\n        if (scheduleTypeEnum == ScheduleTypeEnum.CRON) {\n            if (jobInfo.getScheduleConf() == null || !CronExpression.isValidExpression(jobInfo.getScheduleConf())) {\n                return new ReturnT<String>(ReturnT.FAIL_CODE, \"Cron\" + I18nUtil.getString(\"system_unvalid\"));\n            }\n        } else if (scheduleTypeEnum == ScheduleTypeEnum.FIX_RATE /*|| scheduleTypeEnum == ScheduleTypeEnum.FIX_DELAY*/) {\n            if (jobInfo.getScheduleConf() == null) {\n                return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"schedule_type\") + I18nUtil.getString(\"system_unvalid\")));\n            }\n            try {\n                int fixSecond = Integer.valueOf(jobInfo.getScheduleConf());\n                if (fixSecond < 1) {\n                    return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"schedule_type\") + I18nUtil.getString(\"system_unvalid\")));\n                }\n            } catch (Exception e) {\n                return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"schedule_type\") + I18nUtil.getString(\"system_unvalid\")));\n            }\n        }\n\n        // valid advanced\n        if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"jobinfo_field_executorRouteStrategy\") + I18nUtil.getString(\"system_unvalid\")));\n        }\n        if (MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), null) == null) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"misfire_strategy\") + I18nUtil.getString(\"system_unvalid\")));\n        }\n        if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"jobinfo_field_executorBlockStrategy\") + I18nUtil.getString(\"system_unvalid\")));\n        }\n\n        // 》ChildJobId valid\n        if (jobInfo.getChildJobId() != null && jobInfo.getChildJobId().trim().length() > 0) {\n            String[] childJobIds = jobInfo.getChildJobId().split(\",\");\n            for (String childJobIdItem : childJobIds) {\n                if (childJobIdItem != null && childJobIdItem.trim().length() > 0 && isNumeric(childJobIdItem)) {\n                    XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.parseInt(childJobIdItem));\n                    if (childJobInfo == null) {\n                        return new ReturnT<String>(ReturnT.FAIL_CODE,\n                            MessageFormat.format((I18nUtil.getString(\"jobinfo_field_childJobId\") + \"({0})\" + I18nUtil.getString(\"system_not_found\")), childJobIdItem));\n                    }\n                } else {\n                    return new ReturnT<String>(ReturnT.FAIL_CODE,\n                        MessageFormat.format((I18nUtil.getString(\"jobinfo_field_childJobId\") + \"({0})\" + I18nUtil.getString(\"system_unvalid\")), childJobIdItem));\n                }\n            }\n\n            // join , avoid \"xxx,,\"\n            String temp = \"\";\n            for (String item : childJobIds) {\n                temp += item + \",\";\n            }\n            temp = temp.substring(0, temp.length() - 1);\n\n            jobInfo.setChildJobId(temp);\n        }\n\n        // group valid\n        XxlJobGroup jobGroup = xxlJobGroupDao.load(jobInfo.getJobGroup());\n        if (jobGroup == null) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"jobinfo_field_jobgroup\") + I18nUtil.getString(\"system_unvalid\")));\n        }\n\n        // stage job info\n        XxlJobInfo exists_jobInfo = xxlJobInfoDao.loadById(jobInfo.getId());\n        if (exists_jobInfo == null) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"jobinfo_field_id\") + I18nUtil.getString(\"system_not_found\")));\n        }\n\n        // next trigger time (5s后生效，避开预读周期)\n        long nextTriggerTime = exists_jobInfo.getTriggerNextTime();\n        boolean scheduleDataNotChanged = jobInfo.getScheduleType().equals(exists_jobInfo.getScheduleType()) && jobInfo.getScheduleConf().equals(exists_jobInfo.getScheduleConf());\n        if (exists_jobInfo.getTriggerStatus() == 1 && !scheduleDataNotChanged) {\n            try {\n                Date nextValidTime = JobScheduleHelper.generateNextValidTime(jobInfo, new Date(System.currentTimeMillis() + JobScheduleHelper.PRE_READ_MS));\n                if (nextValidTime == null) {\n                    return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"schedule_type\") + I18nUtil.getString(\"system_unvalid\")));\n                }\n                nextTriggerTime = nextValidTime.getTime();\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n                return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"schedule_type\") + I18nUtil.getString(\"system_unvalid\")));\n            }\n        }\n\n        exists_jobInfo.setJobGroup(jobInfo.getJobGroup());\n        exists_jobInfo.setJobDesc(jobInfo.getJobDesc());\n        exists_jobInfo.setAuthor(jobInfo.getAuthor());\n        exists_jobInfo.setAlarmEmail(jobInfo.getAlarmEmail());\n        exists_jobInfo.setScheduleType(jobInfo.getScheduleType());\n        exists_jobInfo.setScheduleConf(jobInfo.getScheduleConf());\n        exists_jobInfo.setMisfireStrategy(jobInfo.getMisfireStrategy());\n        exists_jobInfo.setExecutorRouteStrategy(jobInfo.getExecutorRouteStrategy());\n        exists_jobInfo.setExecutorHandler(jobInfo.getExecutorHandler());\n        exists_jobInfo.setExecutorParam(jobInfo.getExecutorParam());\n        exists_jobInfo.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy());\n        exists_jobInfo.setExecutorTimeout(jobInfo.getExecutorTimeout());\n        exists_jobInfo.setExecutorFailRetryCount(jobInfo.getExecutorFailRetryCount());\n        exists_jobInfo.setChildJobId(jobInfo.getChildJobId());\n        exists_jobInfo.setTriggerNextTime(nextTriggerTime);\n\n        exists_jobInfo.setUpdateTime(new Date());\n        xxlJobInfoDao.update(exists_jobInfo);\n\n\n        return ReturnT.SUCCESS;\n    }\n\n    @Override\n    public ReturnT<String> remove(int id) {\n        XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(id);\n        if (xxlJobInfo == null) {\n            return ReturnT.SUCCESS;\n        }\n\n        xxlJobInfoDao.delete(id);\n        xxlJobLogDao.delete(id);\n        xxlJobLogGlueDao.deleteByJobId(id);\n        return ReturnT.SUCCESS;\n    }\n\n    @Override\n    public ReturnT<String> start(int id) {\n        XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(id);\n\n        // valid\n        ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(xxlJobInfo.getScheduleType(), ScheduleTypeEnum.NONE);\n        if (ScheduleTypeEnum.NONE == scheduleTypeEnum) {\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"schedule_type_none_limit_start\")));\n        }\n\n        // next trigger time (5s后生效，避开预读周期)\n        long nextTriggerTime = 0;\n        try {\n            Date nextValidTime = JobScheduleHelper.generateNextValidTime(xxlJobInfo, new Date(System.currentTimeMillis() + JobScheduleHelper.PRE_READ_MS));\n            if (nextValidTime == null) {\n                return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"schedule_type\") + I18nUtil.getString(\"system_unvalid\")));\n            }\n            nextTriggerTime = nextValidTime.getTime();\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString(\"schedule_type\") + I18nUtil.getString(\"system_unvalid\")));\n        }\n\n        xxlJobInfo.setTriggerStatus(1);\n        xxlJobInfo.setTriggerLastTime(0);\n        xxlJobInfo.setTriggerNextTime(nextTriggerTime);\n\n        xxlJobInfo.setUpdateTime(new Date());\n        xxlJobInfoDao.update(xxlJobInfo);\n        return ReturnT.SUCCESS;\n    }\n\n    @Override\n    public ReturnT<String> stop(int id) {\n        XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(id);\n\n        xxlJobInfo.setTriggerStatus(0);\n        xxlJobInfo.setTriggerLastTime(0);\n        xxlJobInfo.setTriggerNextTime(0);\n\n        xxlJobInfo.setUpdateTime(new Date());\n        xxlJobInfoDao.update(xxlJobInfo);\n        return ReturnT.SUCCESS;\n    }\n\n    @Override\n    public Map<String, Object> dashboardInfo() {\n\n        int jobInfoCount = xxlJobInfoDao.findAllCount();\n        int jobLogCount = 0;\n        int jobLogSuccessCount = 0;\n        XxlJobLogReport xxlJobLogReport = xxlJobLogReportDao.queryLogReportTotal();\n        if (xxlJobLogReport != null) {\n            jobLogCount = xxlJobLogReport.getRunningCount() + xxlJobLogReport.getSucCount() + xxlJobLogReport.getFailCount();\n            jobLogSuccessCount = xxlJobLogReport.getSucCount();\n        }\n\n        // executor count\n        Set<String> executorAddressSet = new HashSet<String>();\n        List<XxlJobGroup> groupList = xxlJobGroupDao.findAll();\n\n        if (groupList != null && !groupList.isEmpty()) {\n            for (XxlJobGroup group : groupList) {\n                if (group.getRegistryList() != null && !group.getRegistryList().isEmpty()) {\n                    executorAddressSet.addAll(group.getRegistryList());\n                }\n            }\n        }\n\n        int executorCount = executorAddressSet.size();\n\n        Map<String, Object> dashboardMap = new HashMap<String, Object>();\n        dashboardMap.put(\"jobInfoCount\", jobInfoCount);\n        dashboardMap.put(\"jobLogCount\", jobLogCount);\n        dashboardMap.put(\"jobLogSuccessCount\", jobLogSuccessCount);\n        dashboardMap.put(\"executorCount\", executorCount);\n        return dashboardMap;\n    }\n\n    @Override\n    public ReturnT<Map<String, Object>> chartInfo(Date startDate, Date endDate) {\n\n        // process\n        List<String> triggerDayList = new ArrayList<String>();\n        List<Integer> triggerDayCountRunningList = new ArrayList<Integer>();\n        List<Integer> triggerDayCountSucList = new ArrayList<Integer>();\n        List<Integer> triggerDayCountFailList = new ArrayList<Integer>();\n        int triggerCountRunningTotal = 0;\n        int triggerCountSucTotal = 0;\n        int triggerCountFailTotal = 0;\n\n        List<XxlJobLogReport> logReportList = xxlJobLogReportDao.queryLogReport(startDate, endDate);\n\n        if (logReportList != null && logReportList.size() > 0) {\n            for (XxlJobLogReport item : logReportList) {\n                String day = DateUtil.formatDate(item.getTriggerDay());\n                int triggerDayCountRunning = item.getRunningCount();\n                int triggerDayCountSuc = item.getSucCount();\n                int triggerDayCountFail = item.getFailCount();\n\n                triggerDayList.add(day);\n                triggerDayCountRunningList.add(triggerDayCountRunning);\n                triggerDayCountSucList.add(triggerDayCountSuc);\n                triggerDayCountFailList.add(triggerDayCountFail);\n\n                triggerCountRunningTotal += triggerDayCountRunning;\n                triggerCountSucTotal += triggerDayCountSuc;\n                triggerCountFailTotal += triggerDayCountFail;\n            }\n        } else {\n            for (int i = -6; i <= 0; i++) {\n                triggerDayList.add(DateUtil.formatDate(DateUtil.addDays(new Date(), i)));\n                triggerDayCountRunningList.add(0);\n                triggerDayCountSucList.add(0);\n                triggerDayCountFailList.add(0);\n            }\n        }\n\n        Map<String, Object> result = new HashMap<String, Object>();\n        result.put(\"triggerDayList\", triggerDayList);\n        result.put(\"triggerDayCountRunningList\", triggerDayCountRunningList);\n        result.put(\"triggerDayCountSucList\", triggerDayCountSucList);\n        result.put(\"triggerDayCountFailList\", triggerDayCountFailList);\n\n        result.put(\"triggerCountRunningTotal\", triggerCountRunningTotal);\n        result.put(\"triggerCountSucTotal\", triggerCountSucTotal);\n        result.put(\"triggerCountFailTotal\", triggerCountFailTotal);\n\n        return new ReturnT<Map<String, Object>>(result);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application-dev.yml",
    "content": "--- # 监控配置\nspring.boot.admin.client:\n  # 增加客户端开关\n  enabled: true\n  # 设置 Spring Boot Admin Server 地址\n  url: http://localhost:9090/admin\n  instance:\n    service-host-type: IP\n  username: ruoyi\n  password: 123456\n\n--- # 数据库配置\nspring:\n  datasource:\n    type: com.zaxxer.hikari.HikariDataSource\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    url: jdbc:mysql://81.68.131.40:3306/paizhicheng?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai\n    username: Paizhicheng\n    password: pZdDw5YKLKbWFfEB\n    hikari:\n      auto-commit: true\n      connection-test-query: SELECT 1\n      connection-timeout: 10000\n      idle-timeout: 30000\n      max-lifetime: 900000\n      maximum-pool-size: 30\n      minimum-idle: 10\n      pool-name: HikariCP\n      validation-timeout: 1000\n\n--- # 邮件配置\nspring:\n  mail:\n    from: xxx@qq.com\n    host: smtp.qq.com\n    username: xxx@qq.com\n    password: xxx\n    port: 25\n    properties:\n      mail:\n        smtp:\n          auth: true\n          socketFactory:\n            class: javax.net.ssl.SSLSocketFactory\n          starttls:\n            enable: true\n            required: true\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application-prod.yml",
    "content": "--- # 监控配置\nspring.boot.admin.client:\n  # 增加客户端开关\n  enabled: true\n  # 设置 Spring Boot Admin Server 地址\n  url: http://localhost:9090/admin\n  instance:\n    service-host-type: IP\n  username: ruoyi\n  password: 123456\n\n--- # 数据库配置\nspring:\n  datasource:\n    type: com.zaxxer.hikari.HikariDataSource\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    url: jdbc:mysql://127.0.0.1:3306/ry-vue?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai\n    username: root\n    password: root\n    hikari:\n      auto-commit: true\n      connection-test-query: SELECT 1\n      connection-timeout: 10000\n      idle-timeout: 30000\n      max-lifetime: 900000\n      maximum-pool-size: 30\n      minimum-idle: 10\n      pool-name: HikariCP\n      validation-timeout: 1000\n\n--- # 邮件配置\nspring:\n  mail:\n    from: xxx@qq.com\n    host: smtp.qq.com\n    username: xxx@qq.com\n    password: xxx\n    port: 25\n    properties:\n      mail:\n        smtp:\n          auth: true\n          socketFactory:\n            class: javax.net.ssl.SSLSocketFactory\n          starttls:\n            enable: true\n            required: true\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml",
    "content": "--- # server 配置\nserver:\n  port: 9100\n  servlet:\n    context-path: /xxl-job-admin\nspring:\n  application:\n    name: ruoyi-xxl-job-admin\n  profiles:\n    active: @profiles.active@\n  mvc:\n    servlet:\n      load-on-startup: 0\n    static-path-pattern: /static/**\n  web:\n    resources:\n      static-locations: classpath:/static/\n\nlogging:\n  config: classpath:logback-plus.xml\n\n--- # mybatis 配置\nmybatis:\n  mapper-locations: classpath:/mybatis-mapper/*Mapper.xml\n\n--- # 页面配置\nspring:\n  freemarker:\n    charset: UTF-8\n    request-context-attribute: request\n    settings:\n      number_format: 0.##########\n    suffix: .ftl\n    templateLoaderPath: classpath:/templates/\n\n--- # Actuator 监控端点的配置项\nmanagement:\n  health:\n    mail:\n      enabled: false\n  endpoints:\n    web:\n      exposure:\n        include: '*'\n  endpoint:\n    health:\n      show-details: ALWAYS\n    logfile:\n      external-file: ./logs/ruoyi-xxl-job-admin.log\n\n--- # xxljob系统配置\nxxl:\n  job:\n    # 鉴权token\n    accessToken: xxl-job\n    # 国际化\n    i18n: zh_CN\n    # 日志清理\n    logretentiondays: 30\n    triggerpool:\n      fast:\n        max: 200\n      slow:\n        max: 100\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/banner.txt",
    "content": "Application Version: ${ruoyi-vue-plus.version}\nSpring Boot Version: ${spring-boot.version}\n__   __      _            _       _                     _           _\n\\ \\ / /     | |          | |     | |           /\\      | |         (_)\n \\ V / __  _| |______    | | ___ | |__ ______ /  \\   __| |_ __ ___  _ _ __\n  > <  \\ \\/ / |______|   | |/ _ \\| '_ \\______/ /\\ \\ / _` | '_ ` _ \\| | '_ \\\n / . \\  >  <| |     | |__| | (_) | |_) |    / ____ \\ (_| | | | | | | | | | |\n/_/ \\_\\/_/\\_\\_|      \\____/ \\___/|_.__/    /_/    \\_\\__,_|_| |_| |_|_|_| |_|\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_en.properties",
    "content": "admin_name=Scheduling Center\nadmin_name_full=Distributed Task Scheduling Platform XXL-JOB\nadmin_version=2.4.0\nadmin_i18n=en\n\n## system\nsystem_tips=System message\nsystem_ok=Confirm\nsystem_close=Close\nsystem_save=Save\nsystem_cancel=Cancel\nsystem_search=Search\nsystem_status=Status\nsystem_opt=Operate\nsystem_please_input=please input\nsystem_please_choose=please choose\nsystem_success=success\nsystem_fail=fail\nsystem_add_suc=add success\nsystem_add_fail=add fail\nsystem_update_suc=update success\nsystem_update_fail=update fail\nsystem_all=All\nsystem_api_error=net error\nsystem_show=Show\nsystem_empty=Empty\nsystem_opt_suc=operate success\nsystem_opt_fail=operate fail\nsystem_opt_edit=Edit\nsystem_opt_del=Delete\nsystem_opt_copy=Copy\nsystem_unvalid=illegal\nsystem_not_found=not exist\nsystem_nav=Navigation\nsystem_digits=digits\nsystem_lengh_limit=Length limit\nsystem_permission_limit=Permission limit\nsystem_welcome=Welcome\n\n## daterangepicker\ndaterangepicker_ranges_recent_hour=recent one hour\ndaterangepicker_ranges_today=today\ndaterangepicker_ranges_yesterday=yesterday\ndaterangepicker_ranges_this_month=this month\ndaterangepicker_ranges_last_month=last month\ndaterangepicker_ranges_recent_week=recent one week\ndaterangepicker_ranges_recent_month=recent one month\ndaterangepicker_custom_name=custom\ndaterangepicker_custom_starttime=start time\ndaterangepicker_custom_endtime=end time\ndaterangepicker_custom_daysofweek=Sun,Mon,Tue,Wed,Thu,Fri,Sat\ndaterangepicker_custom_monthnames=Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec\n\n## dataTable\ndataTable_sProcessing=processing...\ndataTable_sLengthMenu= _MENU_ records per page\ndataTable_sZeroRecords=No matching results\ndataTable_sInfo=page _PAGE_  ( Total _PAGES_ pages，_TOTAL_ records )\ndataTable_sInfoEmpty=No Record\ndataTable_sInfoFiltered=(Filtered by _MAX_ results)\ndataTable_sSearch=Search\ndataTable_sEmptyTable=Table data is empty\ndataTable_sLoadingRecords=Loading...\ndataTable_sFirst=FIRST PAGE\ndataTable_sPrevious=Previous Page\ndataTable_sNext=Next Page\ndataTable_sLast=LAST PAGE\ndataTable_sSortAscending=: Rank this column in ascending order\ndataTable_sSortDescending=: Rank this column in descending order\n\n## login\nlogin_btn=Login\nlogin_remember_me=Remember Me\nlogin_username_placeholder=Please enter username\nlogin_password_placeholder=Please enter password\nlogin_username_empty=Please enter username\nlogin_username_lt_4=Username length should not be less than 4\nlogin_password_empty=Please enter password\nlogin_password_lt_4=Password length should not be less than 4\nlogin_success=Login success\nlogin_fail=Login fail\nlogin_param_empty=Username or password is empty\nlogin_param_unvalid=Username or password error\n\n## logout\nlogout_btn=Logout\nlogout_confirm=Confirm logout?\nlogout_success=Logout success\nlogout_fail=Logout fail\n\n## change pwd\nchange_pwd=Change password\nchange_pwd_suc_to_logout=Change password successful, about to log out login\nchange_pwd_field_newpwd=new password\n\n## dashboard\njob_dashboard_name=Run report\njob_dashboard_job_num=Job number\njob_dashboard_job_num_tip=The number of tasks running in the scheduling center\njob_dashboard_trigger_num=trigger number\njob_dashboard_trigger_num_tip=The number of trigger record scheduled by the scheduling center\njob_dashboard_jobgroup_num=Executor number\njob_dashboard_jobgroup_num_tip=The number of online executor machines perceived by the scheduling center\njob_dashboard_report=Scheduling report\njob_dashboard_report_loaddata_fail=Scheduling report load data error\njob_dashboard_date_report=Date distribution\njob_dashboard_rate_report=Percentage distribution\n\n## job info\njobinfo_name=Job Manage\njobinfo_job=Job\njobinfo_field_add=Add Job\njobinfo_field_update=Edit Job\njobinfo_field_id=Job ID\njobinfo_field_jobgroup=Executor\njobinfo_field_jobdesc=Job description\njobinfo_field_timeout=Job timeout period\njobinfo_field_gluetype=GLUE Type\njobinfo_field_executorparam=Param\njobinfo_field_author=Author\njobinfo_field_alarmemail=Alarm email\njobinfo_field_alarmemail_placeholder=Please enter alarm mail, if there are more than one comma separated\njobinfo_field_executorRouteStrategy=Route Strategy\njobinfo_field_childJobId=Child Job ID\njobinfo_field_childJobId_placeholder=Please enter the Child job ID, if there are more than one comma separated\njobinfo_field_executorBlockStrategy=Block Strategy\njobinfo_field_executorFailRetryCount=Fail Retry Count\njobinfo_field_executorFailRetryCount_placeholder=Fail Retry Count. effect if greater than zero\njobinfo_script_location=Script location\njobinfo_shard_index=Shard index\njobinfo_shard_total=Shard total\njobinfo_opt_stop=Stop\njobinfo_opt_start=Start\njobinfo_opt_log=Query Log\njobinfo_opt_run=Run Once\njobinfo_opt_run_tips=Please input the address for this trigger. Null will be obtained from the executor\njobinfo_opt_registryinfo=Registry Info\njobinfo_opt_next_time=Next trigger time\njobinfo_glue_remark=Resource Remark\njobinfo_glue_remark_limit=Resource Remark length is limited to 4~100\njobinfo_glue_rollback=Version Backtrack\njobinfo_glue_jobid_unvalid=Job ID is illegal\njobinfo_glue_gluetype_unvalid=The job is not GLUE Type\njobinfo_field_executorTimeout_placeholder=Job Timeout period，in seconds. effect if greater than zero\nschedule_type=Schedule Type\nschedule_type_none=None\nschedule_type_cron=Cron\nschedule_type_fix_rate=Fix rate\nschedule_type_fix_delay=Fix delay\nschedule_type_none_limit_start=The current schedule type disables startup\nmisfire_strategy=Misfire strategy\nmisfire_strategy_do_nothing=Do nothing\nmisfire_strategy_fire_once_now=Fire once now\njobinfo_conf_base=Base configuration\njobinfo_conf_schedule=Schedule configuration\njobinfo_conf_job=Job configuration\njobinfo_conf_advanced=Advanced configuration\n\n## job log\njoblog_name=Trigger Log\njoblog_status=Status\njoblog_status_all=All\njoblog_status_suc=Success\njoblog_status_fail=Fail\njoblog_status_running=Running\njoblog_field_triggerTime=Trigger Time\njoblog_field_triggerCode=Trigger Result\njoblog_field_triggerMsg=Trigger Msg\njoblog_field_handleTime=Handle Time\njoblog_field_handleCode=Handle Result\njoblog_field_handleMsg=Trigger Msg\njoblog_field_executorAddress=Executor Address\njoblog_clean=Clean\njoblog_clean_log=Clean Log\njoblog_clean_type=Clean Type\njoblog_clean_type_1=Clean up log data a month ago\njoblog_clean_type_2=Clean up log data three month ago\njoblog_clean_type_3=Clean up log data six month ago\njoblog_clean_type_4=Clean up log data a year ago\njoblog_clean_type_5=Clean up log data a thousand record ago\njoblog_clean_type_6=Clean up log data ten thousand record ago\njoblog_clean_type_7=Clean up log data thirty thousand record ago\njoblog_clean_type_8=Clean up log data hundred thousand record ago\njoblog_clean_type_9=Clean up all log data\njoblog_clean_type_unvalid=Clean type is illegal\njoblog_handleCode_200=Success\njoblog_handleCode_500=Fail\njoblog_handleCode_502=Timeout\njoblog_kill_log=Kill Job\njoblog_kill_log_limit=Trigger Fail, can not kill job\njoblog_kill_log_byman=Manual operation, kill job\njoblog_lost_fail=Job result lost, marked as failure\njoblog_rolling_log=Rolling log\njoblog_rolling_log_refresh=Refresh\njoblog_rolling_log_triggerfail=The job trigger fail, can not view the rolling log\njoblog_rolling_log_failoften=The request for the Rolling log is terminated, the number of failed requests exceeds the limit, Reload the log on the refresh page\njoblog_logid_unvalid=Log ID is illegal\n\n## job group\njobgroup_name=Executor Manage\njobgroup_list=Executor List\njobgroup_add=Add Executor\njobgroup_edit=Edit Executor\njobgroup_del=Delete Executor\njobgroup_field_title=Title\njobgroup_field_addressType=Registry Type\njobgroup_field_addressType_0=Automatic registration\njobgroup_field_addressType_1=Manual registration\njobgroup_field_addressType_limit=Manually registration type, the machine address must not be empty\njobgroup_field_registryList=machine address\njobgroup_field_registryList_unvalid=registry machine address is illegal\njobgroup_field_registryList_placeholder=Please enter the machine address, if there are more than one comma separated\njobgroup_field_appname_limit=Limit the beginning of a lowercase letter, consists of lowercase letters、number and hyphen.\njobgroup_field_appname_length=AppName length is limited to 4~64\njobgroup_field_title_length=Title length is limited to 4~12\njobgroup_field_order_digits=Please enter a positive integer\njobgroup_field_orderrange=Order is limited to 1~1000\njobgroup_del_limit_0=Refuse to delete, the executor is being used\njobgroup_del_limit_1=Refuses to delete, the system retains at least one executor\njobgroup_empty=There is no valid executor. Please contact the administrator\n\n## job conf\njobconf_block_SERIAL_EXECUTION=Serial execution\njobconf_block_DISCARD_LATER=Discard Later\njobconf_block_COVER_EARLY=Cover Early\njobconf_route_first=First\njobconf_route_last=Last\njobconf_route_round=Round\njobconf_route_random=Random\njobconf_route_consistenthash=Consistent Hash\njobconf_route_lfu=Least Frequently Used\njobconf_route_lru=Least Recently Used\njobconf_route_failover=Failover\njobconf_route_busyover=Busyover\njobconf_route_shard=Sharding Broadcast\njobconf_idleBeat=Idle check\njobconf_beat=Heartbeats\njobconf_monitor=Task Scheduling Center monitor alarm\njobconf_monitor_detail=monitor alarm details\njobconf_monitor_alarm_title=Alarm Type\njobconf_monitor_alarm_type=Trigger Fail\njobconf_monitor_alarm_content=Alarm Content\njobconf_trigger_admin_adress=Trigger machine address\njobconf_trigger_exe_regtype=Execotor-Registry Type\njobconf_trigger_exe_regaddress=Execotor-Registry Address\njobconf_trigger_address_empty=Trigger Fail：registry address is empty\njobconf_trigger_run=Trigger Job\njobconf_trigger_child_run=Trigger child job\njobconf_callback_child_msg1={0}/{1} [Job ID={2}], Trigger {3}, Trigger msg: {4} <br>\njobconf_callback_child_msg2={0}/{1} [Job ID={2}], Trigger Fail, Trigger msg: Job ID is illegal <br>\njobconf_trigger_type=Job trigger type\njobconf_trigger_type_cron=Cron trigger\njobconf_trigger_type_manual=Manual trigger\njobconf_trigger_type_parent=Parent job trigger\njobconf_trigger_type_api=Api trigger\njobconf_trigger_type_retry=Fail retry trigger\njobconf_trigger_type_misfire=Misfire compensation trigger\n\n## user\nuser_manage=User Manage\nuser_username=Username\nuser_password=Password\nuser_role=Role\nuser_role_admin=Admin User\nuser_role_normal=Normal User\nuser_permission=Permission\nuser_add=Add User\nuser_update=Edit User\nuser_username_repeat=Username Repeat\nuser_username_valid=Restrictions start with a lowercase letter and consist of lowercase letters and Numbers\nuser_password_update_placeholder=Please input password, empty means not update\nuser_update_loginuser_limit=Operation of current login account is not allowed\n\n## help\njob_help=Tutorial\njob_help_document=Official Document\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties",
    "content": "admin_name=任务调度中心\nadmin_name_full=分布式任务调度平台XXL-JOB\nadmin_version=2.4.0\nadmin_i18n=\n\n## system\nsystem_tips=系统提示\nsystem_ok=确定\nsystem_close=关闭\nsystem_save=保存\nsystem_cancel=取消\nsystem_search=搜索\nsystem_status=状态\nsystem_opt=操作\nsystem_please_input=请输入\nsystem_please_choose=请选择\nsystem_success=成功\nsystem_fail=失败\nsystem_add_suc=新增成功\nsystem_add_fail=新增失败\nsystem_update_suc=更新成功\nsystem_update_fail=更新失败\nsystem_all=全部\nsystem_api_error=接口异常\nsystem_show=查看\nsystem_empty=无\nsystem_opt_suc=操作成功\nsystem_opt_fail=操作失败\nsystem_opt_edit=编辑\nsystem_opt_del=删除\nsystem_opt_copy=复制\nsystem_unvalid=非法\nsystem_not_found=不存在\nsystem_nav=导航\nsystem_digits=整数\nsystem_lengh_limit=长度限制\nsystem_permission_limit=权限拦截\nsystem_welcome=欢迎\n\n## daterangepicker\ndaterangepicker_ranges_recent_hour=最近一小时\ndaterangepicker_ranges_today=今日\ndaterangepicker_ranges_yesterday=昨日\ndaterangepicker_ranges_this_month=本月\ndaterangepicker_ranges_last_month=上个月\ndaterangepicker_ranges_recent_week=最近一周\ndaterangepicker_ranges_recent_month=最近一月\ndaterangepicker_custom_name=自定义\ndaterangepicker_custom_starttime=起始时间\ndaterangepicker_custom_endtime=结束时间\ndaterangepicker_custom_daysofweek=日,一,二,三,四,五,六\ndaterangepicker_custom_monthnames=一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月\n\n## dataTable\ndataTable_sProcessing=处理中...\ndataTable_sLengthMenu=每页 _MENU_ 条记录\ndataTable_sZeroRecords=没有匹配结果\ndataTable_sInfo=第 _PAGE_ 页 ( 总共 _PAGES_ 页，_TOTAL_ 条记录 )\ndataTable_sInfoEmpty=无记录\ndataTable_sInfoFiltered=(由 _MAX_ 项结果过滤)\ndataTable_sSearch=搜索\ndataTable_sEmptyTable=表中数据为空\ndataTable_sLoadingRecords=载入中...\ndataTable_sFirst=首页\ndataTable_sPrevious=上页\ndataTable_sNext=下页\ndataTable_sLast=末页\ndataTable_sSortAscending=: 以升序排列此列\ndataTable_sSortDescending=: 以降序排列此列\n\n## login\nlogin_btn=登录\nlogin_remember_me=记住密码\nlogin_username_placeholder=请输入登录账号\nlogin_password_placeholder=请输入登录密码\nlogin_username_empty=请输入登录账号\nlogin_username_lt_4=登录账号不应低于4位\nlogin_password_empty=请输入登录密码\nlogin_password_lt_4=登录密码不应低于4位\nlogin_success=登录成功\nlogin_fail=登录失败\nlogin_param_empty=账号或密码为空\nlogin_param_unvalid=账号或密码错误\n\n## logout\nlogout_btn=注销\nlogout_confirm=确认注销登录?\nlogout_success=注销成功\nlogout_fail=注销失败\n\n## change pwd\nchange_pwd=修改密码\nchange_pwd_suc_to_logout=修改密码成功，即将注销登陆\nchange_pwd_field_newpwd=新密码\n\n## dashboard\njob_dashboard_name=运行报表\njob_dashboard_job_num=任务数量\njob_dashboard_job_num_tip=调度中心运行的任务数量\njob_dashboard_trigger_num=调度次数\njob_dashboard_trigger_num_tip=调度中心触发的调度次数\njob_dashboard_jobgroup_num=执行器数量\njob_dashboard_jobgroup_num_tip=调度中心在线的执行器机器数量\njob_dashboard_report=调度报表\njob_dashboard_report_loaddata_fail=调度报表数据加载异常\njob_dashboard_date_report=日期分布图\njob_dashboard_rate_report=成功比例图\n\n## job info\njobinfo_name=任务管理\njobinfo_job=任务\njobinfo_field_add=新增\njobinfo_field_update=更新任务\njobinfo_field_id=任务ID\njobinfo_field_jobgroup=执行器\njobinfo_field_jobdesc=任务描述\njobinfo_field_gluetype=运行模式\njobinfo_field_executorparam=任务参数\njobinfo_field_author=负责人\njobinfo_field_timeout=任务超时时间\njobinfo_field_alarmemail=报警邮件\njobinfo_field_alarmemail_placeholder=请输入报警邮件，多个邮件地址则逗号分隔\njobinfo_field_executorRouteStrategy=路由策略\njobinfo_field_childJobId=子任务ID\njobinfo_field_childJobId_placeholder=请输入子任务的任务ID,如存在多个则逗号分隔\njobinfo_field_executorBlockStrategy=阻塞处理策略\njobinfo_field_executorFailRetryCount=失败重试次数\njobinfo_field_executorFailRetryCount_placeholder=失败重试次数，大于零时生效\njobinfo_script_location=脚本位置\njobinfo_shard_index=分片序号\njobinfo_shard_total=分片总数\njobinfo_opt_stop=停止\njobinfo_opt_start=启动\njobinfo_opt_log=查询日志\njobinfo_opt_run=执行一次\njobinfo_opt_run_tips=请输入本次执行的机器地址，为空则从执行器获取\njobinfo_opt_registryinfo=注册节点\njobinfo_opt_next_time=下次执行时间\njobinfo_glue_remark=源码备注\njobinfo_glue_remark_limit=源码备注长度限制为4~100\njobinfo_glue_rollback=版本回溯\njobinfo_glue_jobid_unvalid=任务ID非法\njobinfo_glue_gluetype_unvalid=该任务非GLUE模式\njobinfo_field_executorTimeout_placeholder=任务超时时间，单位秒，大于零时生效\nschedule_type=调度类型\nschedule_type_none=无\nschedule_type_cron=CRON\nschedule_type_fix_rate=固定速度\nschedule_type_fix_delay=固定延迟\nschedule_type_none_limit_start=当前调度类型禁止启动\nmisfire_strategy=调度过期策略\nmisfire_strategy_do_nothing=忽略\nmisfire_strategy_fire_once_now=立即执行一次\njobinfo_conf_base=基础配置\njobinfo_conf_schedule=调度配置\njobinfo_conf_job=任务配置\njobinfo_conf_advanced=高级配置\n\n## job log\njoblog_name=调度日志\njoblog_status=状态\njoblog_status_all=全部\njoblog_status_suc=成功\njoblog_status_fail=失败\njoblog_status_running=进行中\njoblog_field_triggerTime=调度时间\njoblog_field_triggerCode=调度结果\njoblog_field_triggerMsg=调度备注\njoblog_field_handleTime=执行时间\njoblog_field_handleCode=执行结果\njoblog_field_handleMsg=执行备注\njoblog_field_executorAddress=执行器地址\njoblog_clean=清理\njoblog_clean_log=日志清理\njoblog_clean_type=清理方式\njoblog_clean_type_1=清理一个月之前日志数据\njoblog_clean_type_2=清理三个月之前日志数据\njoblog_clean_type_3=清理六个月之前日志数据\njoblog_clean_type_4=清理一年之前日志数据\njoblog_clean_type_5=清理一千条以前日志数据\njoblog_clean_type_6=清理一万条以前日志数据\njoblog_clean_type_7=清理三万条以前日志数据\njoblog_clean_type_8=清理十万条以前日志数据\njoblog_clean_type_9=清理所有日志数据\njoblog_clean_type_unvalid=清理类型参数异常\njoblog_handleCode_200=成功\njoblog_handleCode_500=失败\njoblog_handleCode_502=失败(超时)\njoblog_kill_log=终止任务\njoblog_kill_log_limit=调度失败，无法终止日志\njoblog_kill_log_byman=人为操作，主动终止\njoblog_lost_fail=任务结果丢失，标记失败\njoblog_rolling_log=执行日志\njoblog_rolling_log_refresh=刷新\njoblog_rolling_log_triggerfail=任务发起调度失败，无法查看执行日志\njoblog_rolling_log_failoften=终止请求Rolling日志,请求失败次数超上限,可刷新页面重新加载日志\njoblog_logid_unvalid=日志ID非法\n\n## job group\njobgroup_name=执行器管理\njobgroup_list=执行器列表\njobgroup_add=新增执行器\njobgroup_edit=编辑执行器\njobgroup_del=删除执行器\njobgroup_field_title=名称\njobgroup_field_addressType=注册方式\njobgroup_field_addressType_0=自动注册\njobgroup_field_addressType_1=手动录入\njobgroup_field_addressType_limit=手动录入注册方式，机器地址不可为空\njobgroup_field_registryList=机器地址\njobgroup_field_registryList_unvalid=机器地址格式非法\njobgroup_field_registryList_placeholder=请输入执行器地址列表，多地址逗号分隔\njobgroup_field_appname_limit=限制以小写字母开头，由小写字母、数字和中划线组成\njobgroup_field_appname_length=AppName长度限制为4~64\njobgroup_field_title_length=名称长度限制为4~12\njobgroup_field_order_digits=请输入整数\njobgroup_field_orderrange=取值范围为1~1000\njobgroup_del_limit_0=拒绝删除，该执行器使用中\njobgroup_del_limit_1=拒绝删除, 系统至少保留一个执行器\njobgroup_empty=不存在有效执行器,请联系管理员\n\n## job conf\njobconf_block_SERIAL_EXECUTION=单机串行\njobconf_block_DISCARD_LATER=丢弃后续调度\njobconf_block_COVER_EARLY=覆盖之前调度\njobconf_route_first=第一个\njobconf_route_last=最后一个\njobconf_route_round=轮询\njobconf_route_random=随机\njobconf_route_consistenthash=一致性HASH\njobconf_route_lfu=最不经常使用\njobconf_route_lru=最近最久未使用\njobconf_route_failover=故障转移\njobconf_route_busyover=忙碌转移\njobconf_route_shard=分片广播\njobconf_idleBeat=空闲检测\njobconf_beat=心跳检测\njobconf_monitor=任务调度中心监控报警\njobconf_monitor_detail=监控告警明细\njobconf_monitor_alarm_title=告警类型\njobconf_monitor_alarm_type=调度失败\njobconf_monitor_alarm_content=告警内容\njobconf_trigger_admin_adress=调度机器\njobconf_trigger_exe_regtype=执行器-注册方式\njobconf_trigger_exe_regaddress=执行器-地址列表\njobconf_trigger_address_empty=调度失败：执行器地址为空\njobconf_trigger_run=触发调度\njobconf_trigger_child_run=触发子任务\njobconf_callback_child_msg1={0}/{1} [任务ID={2}], 触发{3}, 触发备注: {4} <br>\njobconf_callback_child_msg2={0}/{1} [任务ID={2}], 触发失败, 触发备注: 任务ID格式错误 <br>\njobconf_trigger_type=任务触发类型\njobconf_trigger_type_cron=Cron触发\njobconf_trigger_type_manual=手动触发\njobconf_trigger_type_parent=父任务触发\njobconf_trigger_type_api=API触发\njobconf_trigger_type_retry=失败重试触发\njobconf_trigger_type_misfire=调度过期补偿\n\n## user\nuser_manage=用户管理\nuser_username=账号\nuser_password=密码\nuser_role=角色\nuser_role_admin=管理员\nuser_role_normal=普通用户\nuser_permission=权限\nuser_add=新增用户\nuser_update=更新用户\nuser_username_repeat=账号重复\nuser_username_valid=限制以小写字母开头，由小写字母、数字组成\nuser_password_update_placeholder=请输入新密码，为空则不更新密码\nuser_update_loginuser_limit=禁止操作当前登录账号\n\n## help\njob_help=使用教程\njob_help_document=官方文档\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties",
    "content": "admin_name=任務調度中心\nadmin_name_full=分布式任務調度平臺XXL-JOB\nadmin_version=2.4.0\nadmin_i18n=\n\n## system\nsystem_tips=系統提示\nsystem_ok=確定\nsystem_close=關閉\nsystem_save=儲存\nsystem_cancel=取消\nsystem_search=搜尋\nsystem_status=狀態\nsystem_opt=操作\nsystem_please_input=請輸入\nsystem_please_choose=请選擇\nsystem_success=成功\nsystem_fail=失敗\nsystem_add_suc=新增成功\nsystem_add_fail=新增失敗\nsystem_update_suc=更新成功\nsystem_update_fail=更新失敗\nsystem_all=全部\nsystem_api_error=API錯誤\nsystem_show=查看\nsystem_empty=無\nsystem_opt_suc=操作成功\nsystem_opt_fail=操作失敗\nsystem_opt_edit=編輯\nsystem_opt_del=刪除\nsystem_opt_copy=復制\nsystem_unvalid=非法\nsystem_not_found=不存在\nsystem_nav=導航\nsystem_digits=整數\nsystem_lengh_limit=長度限制\nsystem_permission_limit=權限控管\nsystem_welcome=歡迎\n\n## daterangepicker\ndaterangepicker_ranges_recent_hour=最近一小時\ndaterangepicker_ranges_today=今日\ndaterangepicker_ranges_yesterday=昨日\ndaterangepicker_ranges_this_month=本月\ndaterangepicker_ranges_last_month=上個月\ndaterangepicker_ranges_recent_week=最近一周\ndaterangepicker_ranges_recent_month=最近一月\ndaterangepicker_custom_name=自定義\ndaterangepicker_custom_starttime=起始時間\ndaterangepicker_custom_endtime=結束時間\ndaterangepicker_custom_daysofweek=日,一,二,三,四,五,六\ndaterangepicker_custom_monthnames=一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月\n\n## dataTable\ndataTable_sProcessing=處理中...\ndataTable_sLengthMenu=每頁 _MENU_ 條記錄\ndataTable_sZeroRecords=沒有相符合記錄\ndataTable_sInfo=第 _PAGE_ 頁 ( 總共 _PAGES_ 頁，_TOTAL_ 條記錄 )\ndataTable_sInfoEmpty=無記錄\ndataTable_sInfoFiltered=(由 _MAX_ 項結果過濾)\ndataTable_sSearch=搜尋\ndataTable_sEmptyTable=表中資料為空\ndataTable_sLoadingRecords=載入中...\ndataTable_sFirst=首頁\ndataTable_sPrevious=上頁\ndataTable_sNext=下頁\ndataTable_sLast=末頁\ndataTable_sSortAscending=: 以升幂排序此列\ndataTable_sSortDescending=: 以降幂排序此列\n\n## login\nlogin_btn=登入\nlogin_remember_me=記住密碼\nlogin_username_placeholder=請輸入登入帳號\nlogin_password_placeholder=請輸入登入密碼\nlogin_username_empty=請輸入登入帳號\nlogin_username_lt_4=登入帳號不應低於4位數\nlogin_password_empty=請輸入登入密碼\nlogin_password_lt_4=登入密碼不應低於4位數\nlogin_success=登入成功\nlogin_fail=登入失敗\nlogin_param_empty=帳號或密碼為空值\nlogin_param_unvalid=帳號或密碼錯誤\n\n## logout\nlogout_btn=登出\nlogout_confirm=確認登出?\nlogout_success=登出成功\nlogout_fail=登出失敗\n\n## change pwd\nchange_pwd=修改密碼\nchange_pwd_suc_to_logout=修改密碼成功，即將登出\nchange_pwd_field_newpwd=新密碼\n\n## dashboard\njob_dashboard_name=運行報表\njob_dashboard_job_num=任務數量\njob_dashboard_job_num_tip=調度中心運行的任務數量\njob_dashboard_trigger_num=調度次數\njob_dashboard_trigger_num_tip=調度中心觸發的調度次數\njob_dashboard_jobgroup_num=執行器數量\njob_dashboard_jobgroup_num_tip=調度中心在線的執行器機器數量\njob_dashboard_report=調度報表\njob_dashboard_report_loaddata_fail=調度報表資料加載異常\njob_dashboard_date_report=日期分布圖\njob_dashboard_rate_report=成功比例圖\n\n## job info\njobinfo_name=任務管理\njobinfo_job=任務\njobinfo_field_add=新增\njobinfo_field_update=更新任務\njobinfo_field_id=任務ID\njobinfo_field_jobgroup=執行器\njobinfo_field_jobdesc=任務描述\njobinfo_field_gluetype=運行模式\njobinfo_field_executorparam=任務參數\njobinfo_field_author=負責人\njobinfo_field_timeout=任務超時秒數\njobinfo_field_alarmemail=告警郵件\njobinfo_field_alarmemail_placeholder=輸入多個告警郵件地址，請以逗號分隔\njobinfo_field_executorRouteStrategy=路由策略\njobinfo_field_childJobId=子任務ID\njobinfo_field_childJobId_placeholder=輸入子任務ID，如有多個請以逗號分隔\njobinfo_field_executorBlockStrategy=阻塞處理策略\njobinfo_field_executorFailRetryCount=失敗重試次數\njobinfo_field_executorFailRetryCount_placeholder=失敗重試次數，大於零時生效\njobinfo_script_location=腳本位置\njobinfo_shard_index=分片序號\njobinfo_shard_total=分片總數\njobinfo_opt_stop=停止\njobinfo_opt_start=啟動\njobinfo_opt_log=查詢日誌\njobinfo_opt_run=執行一次\njobinfo_opt_run_tips=請輸入本次執行的機器地址，為空則從執行器獲取\njobinfo_opt_registryinfo=注冊節點\njobinfo_opt_next_time=下次執行時間\njobinfo_glue_remark=源碼備註\njobinfo_glue_remark_limit=源碼備註長度限制為4~100\njobinfo_glue_rollback=版本回復\njobinfo_glue_jobid_unvalid=任務ID非法\njobinfo_glue_gluetype_unvalid=該任務非GLUE模式\njobinfo_field_executorTimeout_placeholder=任務超時時間，單位秒，大於零時生效\nschedule_type=調度類型\nschedule_type_none=無\nschedule_type_cron=CRON\nschedule_type_fix_rate=固定速度\nschedule_type_fix_delay=固定延遲\nschedule_type_none_limit_start=當前調度類型禁止啟動\nmisfire_strategy=調度過期策略\nmisfire_strategy_do_nothing=忽略\nmisfire_strategy_fire_once_now=立即執行壹次\njobinfo_conf_base=基礎配置\njobinfo_conf_schedule=調度配置\njobinfo_conf_job=任務配置\njobinfo_conf_advanced=高級配置\n\n## job log\njoblog_name=調度日誌\njoblog_status=狀態\njoblog_status_all=全部\njoblog_status_suc=成功\njoblog_status_fail=失敗\njoblog_status_running=進行中\njoblog_field_triggerTime=調度時間\njoblog_field_triggerCode=調度結果\njoblog_field_triggerMsg=調度備註\njoblog_field_handleTime=執行時間\njoblog_field_handleCode=執行结果\njoblog_field_handleMsg=執行備註\njoblog_field_executorAddress=執行器地址\njoblog_clean=清理\njoblog_clean_log=日誌清理\njoblog_clean_type=清理方式\njoblog_clean_type_1=清理一個月之前日誌資料\njoblog_clean_type_2=清理三個月之前日誌資料\njoblog_clean_type_3=清理六個月之前日誌資料\njoblog_clean_type_4=清理一年之前日誌資料\njoblog_clean_type_5=清理一千條以前日誌資料\njoblog_clean_type_6=清理一萬條以前日誌資料\njoblog_clean_type_7=清理三萬條以前日誌資料\njoblog_clean_type_8=清理十萬條以前日誌資料\njoblog_clean_type_9=清理所有日誌資料\njoblog_clean_type_unvalid=清理類型參数異常\njoblog_handleCode_200=成功\njoblog_handleCode_500=失敗\njoblog_handleCode_502=失敗(超時)\njoblog_kill_log=终止任務\njoblog_kill_log_limit=調度失敗，無法终止日誌\njoblog_kill_log_byman=人為操作，主動終止\njoblog_lost_fail=任務結果丟失，標記失敗\njoblog_rolling_log=執行日誌\njoblog_rolling_log_refresh=更新\njoblog_rolling_log_triggerfail=任務發起調度失敗，無法查看執行日誌\njoblog_rolling_log_failoften=終止請求Rolling日誌，請求失敗次數超上限，可刷新頁面重新加載日誌\njoblog_logid_unvalid=日誌ID非法\n\n## job group\njobgroup_name=執行器管理\njobgroup_list=執行器列表\njobgroup_add=新增執行器\njobgroup_edit=編輯執行器\njobgroup_del=刪除執行器\njobgroup_field_title=名稱\njobgroup_field_addressType=注冊方式\njobgroup_field_addressType_0=自動注冊\njobgroup_field_addressType_1=手動登錄\njobgroup_field_addressType_limit=手動登錄注冊方式，機器地址不可為空\njobgroup_field_registryList=機器地址\njobgroup_field_registryList_unvalid=機器地址格式非法\njobgroup_field_registryList_placeholder=請輸入執行器地址列表，多個地址請以逗號分隔\njobgroup_field_appname_limit=限制以小寫字母開頭，由小寫字母、數字和中划線組成\njobgroup_field_appname_length=AppName長度限制為4~64\njobgroup_field_title_length=名稱長度限制為4~12\njobgroup_field_order_digits=請輸入整數\njobgroup_field_orderrange=取值範圍為1~1000\njobgroup_del_limit_0=拒絕刪除，該執行器使用中\njobgroup_del_limit_1=拒絕删除，系统至少保留一個執行器\njobgroup_empty=不存在有效執行器，請聯絡系統管理員\n\n## job conf\njobconf_block_SERIAL_EXECUTION=單機串行\njobconf_block_DISCARD_LATER=丢棄后續調度\njobconf_block_COVER_EARLY=覆蓋之前調度\njobconf_route_first=第一個\njobconf_route_last=最後一個\njobconf_route_round=輪詢\njobconf_route_random=隨機\njobconf_route_consistenthash=一致性HASH\njobconf_route_lfu=最不經常使用\njobconf_route_lru=最近最久未使用\njobconf_route_failover=故障轉移\njobconf_route_busyover=忙碌轉移\njobconf_route_shard=分片廣播\njobconf_idleBeat=空閒檢測\njobconf_beat=心跳檢測\njobconf_monitor=任務調度中心監控告警\njobconf_monitor_detail=監控告警明细\njobconf_monitor_alarm_title=告警類型\njobconf_monitor_alarm_type=調度失敗\njobconf_monitor_alarm_content=告警内容\njobconf_trigger_admin_adress=調度機器\njobconf_trigger_exe_regtype=執行器-注冊方式\njobconf_trigger_exe_regaddress=執行器-地址列表\njobconf_trigger_address_empty=調度失敗：執行器地址為空\njobconf_trigger_run=觸發調度\njobconf_trigger_child_run=觸發子任務\njobconf_callback_child_msg1={0}/{1} [任務ID={2}], 觸發{3}, 觸發備註: {4} <br>\njobconf_callback_child_msg2={0}/{1} [任務ID={2}], 觸發失败, 觸發備註: 任務ID格式錯誤 <br>\njobconf_trigger_type=任務觸發類型\njobconf_trigger_type_cron=Cron觸發\njobconf_trigger_type_manual=手動觸發\njobconf_trigger_type_parent=父任務觸發\njobconf_trigger_type_api=API觸發\njobconf_trigger_type_retry=失敗重試觸發\njobconf_trigger_type_misfire=調度過期補償\n\n## user\nuser_manage=用户管理\nuser_username=帳號\nuser_password=密碼\nuser_role=角色\nuser_role_admin=管理員\nuser_role_normal=普通用戶\nuser_permission=權限\nuser_add=新增用戶\nuser_update=更新用戶\nuser_username_repeat=帳號重複\nuser_username_valid=限制以小寫字母開頭，由小寫字母、數字組成\nuser_password_update_placeholder=請輸入新密碼，為空則不更新密碼\nuser_update_loginuser_limit=禁止操作當前登入帳號\n\n## help\njob_help=使用教程\njob_help_document=官方文件\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/logback-plus.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration debug=\"false\" scan=\"true\" scanPeriod=\"1 seconds\">\n\n    <contextName>logback</contextName>\n    <property name=\"log.path\" value=\"./logs/ruoyi-xxl-job-admin\"/>\n    <property name=\"console.log.pattern\"\n              value=\"%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n\"/>\n    <property name=\"log.pattern\" value=\"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"/>\n\n    <appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>${console.log.pattern}</pattern>\n            <charset>utf-8</charset>\n        </encoder>\n    </appender>\n\n    <appender name=\"file\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${log.path}.log</file>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n            <fileNamePattern>${log.path}.%d{yyyy-MM-dd}.log</fileNamePattern>\n            <!-- 日志最大的历史 60天 -->\n            <maxHistory>60</maxHistory>\n        </rollingPolicy>\n        <encoder>\n            <pattern>${log.pattern}</pattern>\n        </encoder>\n    </appender>\n\n    <root level=\"info\">\n        <appender-ref ref=\"console\"/>\n        <appender-ref ref=\"file\"/>\n    </root>\n\n</configuration>"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobGroupMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n    \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.xxl.job.admin.dao.XxlJobGroupDao\">\n\n    <resultMap id=\"XxlJobGroup\" type=\"com.xxl.job.admin.core.model.XxlJobGroup\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"app_name\" property=\"appname\" />\n        <result column=\"title\" property=\"title\" />\n        <result column=\"address_type\" property=\"addressType\" />\n        <result column=\"address_list\" property=\"addressList\" />\n        <result column=\"update_time\" property=\"updateTime\" />\n    </resultMap>\n\n    <sql id=\"Base_Column_List\">\n        t.id,\n        t.app_name,\n        t.title,\n        t.address_type,\n        t.address_list,\n        t.update_time\n    </sql>\n\n    <select id=\"findAll\" resultMap=\"XxlJobGroup\">\n        SELECT <include refid=\"Base_Column_List\" />\n        FROM xxl_job_group AS t\n        ORDER BY t.app_name, t.title, t.id ASC\n    </select>\n\n    <select id=\"findByAddressType\" parameterType=\"java.lang.Integer\" resultMap=\"XxlJobGroup\">\n        SELECT <include refid=\"Base_Column_List\" />\n        FROM xxl_job_group AS t\n        WHERE t.address_type = #{addressType}\n        ORDER BY t.app_name, t.title, t.id ASC\n    </select>\n\n    <insert id=\"save\" parameterType=\"com.xxl.job.admin.core.model.XxlJobGroup\" useGeneratedKeys=\"true\" keyProperty=\"id\" >\n        INSERT INTO xxl_job_group ( `app_name`, `title`, `address_type`, `address_list`, `update_time`)\n        values ( #{appname}, #{title}, #{addressType}, #{addressList}, #{updateTime} );\n    </insert>\n\n    <update id=\"update\" parameterType=\"com.xxl.job.admin.core.model.XxlJobGroup\" >\n        UPDATE xxl_job_group\n        SET `app_name` = #{appname},\n            `title` = #{title},\n            `address_type` = #{addressType},\n            `address_list` = #{addressList},\n            `update_time` = #{updateTime}\n        WHERE id = #{id}\n    </update>\n\n    <delete id=\"remove\" parameterType=\"java.lang.Integer\" >\n        DELETE FROM xxl_job_group\n        WHERE id = #{id}\n    </delete>\n\n    <select id=\"load\" parameterType=\"java.lang.Integer\" resultMap=\"XxlJobGroup\">\n        SELECT <include refid=\"Base_Column_List\" />\n        FROM xxl_job_group AS t\n        WHERE t.id = #{id}\n    </select>\n\n    <select id=\"pageList\" parameterType=\"java.util.HashMap\" resultMap=\"XxlJobGroup\">\n        SELECT <include refid=\"Base_Column_List\" />\n        FROM xxl_job_group AS t\n        <trim prefix=\"WHERE\" prefixOverrides=\"AND | OR\" >\n            <if test=\"appname != null and appname != ''\">\n                AND t.app_name like CONCAT(CONCAT('%', #{appname}), '%')\n            </if>\n            <if test=\"title != null and title != ''\">\n                AND t.title like CONCAT(CONCAT('%', #{title}), '%')\n            </if>\n        </trim>\n        ORDER BY t.app_name, t.title, t.id ASC\n        LIMIT #{offset}, #{pagesize}\n    </select>\n\n    <select id=\"pageListCount\" parameterType=\"java.util.HashMap\" resultType=\"int\">\n        SELECT count(1)\n        FROM xxl_job_group AS t\n        <trim prefix=\"WHERE\" prefixOverrides=\"AND | OR\" >\n            <if test=\"appname != null and appname != ''\">\n                AND t.app_name like CONCAT(CONCAT('%', #{appname}), '%')\n            </if>\n            <if test=\"title != null and title != ''\">\n                AND t.title like CONCAT(CONCAT('%', #{title}), '%')\n            </if>\n        </trim>\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n    \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.xxl.job.admin.dao.XxlJobInfoDao\">\n\n    <resultMap id=\"XxlJobInfo\" type=\"com.xxl.job.admin.core.model.XxlJobInfo\" >\n        <result column=\"id\" property=\"id\" />\n\n        <result column=\"job_group\" property=\"jobGroup\" />\n        <result column=\"job_desc\" property=\"jobDesc\" />\n\n        <result column=\"add_time\" property=\"addTime\" />\n        <result column=\"update_time\" property=\"updateTime\" />\n\n        <result column=\"author\" property=\"author\" />\n        <result column=\"alarm_email\" property=\"alarmEmail\" />\n\n        <result column=\"schedule_type\" property=\"scheduleType\" />\n        <result column=\"schedule_conf\" property=\"scheduleConf\" />\n        <result column=\"misfire_strategy\" property=\"misfireStrategy\" />\n\n        <result column=\"executor_route_strategy\" property=\"executorRouteStrategy\" />\n        <result column=\"executor_handler\" property=\"executorHandler\" />\n        <result column=\"executor_param\" property=\"executorParam\" />\n        <result column=\"executor_block_strategy\" property=\"executorBlockStrategy\" />\n        <result column=\"executor_timeout\" property=\"executorTimeout\" />\n        <result column=\"executor_fail_retry_count\" property=\"executorFailRetryCount\" />\n\n        <result column=\"glue_type\" property=\"glueType\" />\n        <result column=\"glue_source\" property=\"glueSource\" />\n        <result column=\"glue_remark\" property=\"glueRemark\" />\n        <result column=\"glue_updatetime\" property=\"glueUpdatetime\" />\n\n        <result column=\"child_jobid\" property=\"childJobId\" />\n\n        <result column=\"trigger_status\" property=\"triggerStatus\" />\n        <result column=\"trigger_last_time\" property=\"triggerLastTime\" />\n        <result column=\"trigger_next_time\" property=\"triggerNextTime\" />\n    </resultMap>\n\n    <sql id=\"Base_Column_List\">\n        t.id,\n        t.job_group,\n        t.job_desc,\n        t.add_time,\n        t.update_time,\n        t.author,\n        t.alarm_email,\n        t.schedule_type,\n        t.schedule_conf,\n        t.misfire_strategy,\n        t.executor_route_strategy,\n        t.executor_handler,\n        t.executor_param,\n        t.executor_block_strategy,\n        t.executor_timeout,\n        t.executor_fail_retry_count,\n        t.glue_type,\n        t.glue_source,\n        t.glue_remark,\n        t.glue_updatetime,\n        t.child_jobid,\n        t.trigger_status,\n        t.trigger_last_time,\n        t.trigger_next_time\n    </sql>\n\n    <select id=\"pageList\" parameterType=\"java.util.HashMap\" resultMap=\"XxlJobInfo\">\n        SELECT <include refid=\"Base_Column_List\" />\n        FROM xxl_job_info AS t\n        <trim prefix=\"WHERE\" prefixOverrides=\"AND | OR\" >\n            <if test=\"jobGroup gt 0\">\n                AND t.job_group = #{jobGroup}\n            </if>\n            <if test=\"triggerStatus gte 0\">\n                AND t.trigger_status = #{triggerStatus}\n            </if>\n            <if test=\"jobDesc != null and jobDesc != ''\">\n                AND t.job_desc like CONCAT(CONCAT('%', #{jobDesc}), '%')\n            </if>\n            <if test=\"executorHandler != null and executorHandler != ''\">\n                AND t.executor_handler like CONCAT(CONCAT('%', #{executorHandler}), '%')\n            </if>\n            <if test=\"author != null and author != ''\">\n                AND t.author like CONCAT(CONCAT('%', #{author}), '%')\n            </if>\n        </trim>\n        ORDER BY id DESC\n        LIMIT #{offset}, #{pagesize}\n    </select>\n\n    <select id=\"pageListCount\" parameterType=\"java.util.HashMap\" resultType=\"int\">\n        SELECT count(1)\n        FROM xxl_job_info AS t\n        <trim prefix=\"WHERE\" prefixOverrides=\"AND | OR\" >\n            <if test=\"jobGroup gt 0\">\n                AND t.job_group = #{jobGroup}\n            </if>\n            <if test=\"triggerStatus gte 0\">\n                AND t.trigger_status = #{triggerStatus}\n            </if>\n            <if test=\"jobDesc != null and jobDesc != ''\">\n                AND t.job_desc like CONCAT(CONCAT('%', #{jobDesc}), '%')\n            </if>\n            <if test=\"executorHandler != null and executorHandler != ''\">\n                AND t.executor_handler like CONCAT(CONCAT('%', #{executorHandler}), '%')\n            </if>\n            <if test=\"author != null and author != ''\">\n                AND t.author like CONCAT(CONCAT('%', #{author}), '%')\n            </if>\n        </trim>\n    </select>\n\n    <insert id=\"save\" parameterType=\"com.xxl.job.admin.core.model.XxlJobInfo\" useGeneratedKeys=\"true\" keyProperty=\"id\" >\n        INSERT INTO xxl_job_info (\n            job_group,\n            job_desc,\n            add_time,\n            update_time,\n            author,\n            alarm_email,\n            schedule_type,\n            schedule_conf,\n            misfire_strategy,\n            executor_route_strategy,\n            executor_handler,\n            executor_param,\n            executor_block_strategy,\n            executor_timeout,\n            executor_fail_retry_count,\n            glue_type,\n            glue_source,\n            glue_remark,\n            glue_updatetime,\n            child_jobid,\n            trigger_status,\n            trigger_last_time,\n            trigger_next_time\n        ) VALUES (\n            #{jobGroup},\n            #{jobDesc},\n            #{addTime},\n            #{updateTime},\n            #{author},\n            #{alarmEmail},\n            #{scheduleType},\n            #{scheduleConf},\n            #{misfireStrategy},\n            #{executorRouteStrategy},\n            #{executorHandler},\n            #{executorParam},\n            #{executorBlockStrategy},\n            #{executorTimeout},\n            #{executorFailRetryCount},\n            #{glueType},\n            #{glueSource},\n            #{glueRemark},\n            #{glueUpdatetime},\n            #{childJobId},\n            #{triggerStatus},\n            #{triggerLastTime},\n            #{triggerNextTime}\n        );\n        <!--<selectKey resultType=\"java.lang.Integer\" order=\"AFTER\" keyProperty=\"id\">\n            SELECT LAST_INSERT_ID()\n            /*SELECT @@IDENTITY AS id*/\n        </selectKey>-->\n    </insert>\n\n    <select id=\"loadById\" parameterType=\"java.util.HashMap\" resultMap=\"XxlJobInfo\">\n        SELECT <include refid=\"Base_Column_List\" />\n        FROM xxl_job_info AS t\n        WHERE t.id = #{id}\n    </select>\n\n    <update id=\"update\" parameterType=\"com.xxl.job.admin.core.model.XxlJobInfo\" >\n        UPDATE xxl_job_info\n        SET\n            job_group = #{jobGroup},\n            job_desc = #{jobDesc},\n            update_time = #{updateTime},\n            author = #{author},\n            alarm_email = #{alarmEmail},\n            schedule_type = #{scheduleType},\n            schedule_conf = #{scheduleConf},\n            misfire_strategy = #{misfireStrategy},\n            executor_route_strategy = #{executorRouteStrategy},\n            executor_handler = #{executorHandler},\n            executor_param = #{executorParam},\n            executor_block_strategy = #{executorBlockStrategy},\n            executor_timeout = ${executorTimeout},\n            executor_fail_retry_count = ${executorFailRetryCount},\n            glue_type = #{glueType},\n            glue_source = #{glueSource},\n            glue_remark = #{glueRemark},\n            glue_updatetime = #{glueUpdatetime},\n            child_jobid = #{childJobId},\n            trigger_status = #{triggerStatus},\n            trigger_last_time = #{triggerLastTime},\n            trigger_next_time = #{triggerNextTime}\n        WHERE id = #{id}\n    </update>\n\n    <delete id=\"delete\" parameterType=\"java.util.HashMap\">\n        DELETE\n        FROM xxl_job_info\n        WHERE id = #{id}\n    </delete>\n\n    <select id=\"getJobsByGroup\" parameterType=\"java.util.HashMap\" resultMap=\"XxlJobInfo\">\n        SELECT <include refid=\"Base_Column_List\" />\n        FROM xxl_job_info AS t\n        WHERE t.job_group = #{jobGroup}\n    </select>\n\n    <select id=\"findAllCount\" resultType=\"int\">\n        SELECT count(1)\n        FROM xxl_job_info\n    </select>\n\n\n    <select id=\"scheduleJobQuery\" parameterType=\"java.util.HashMap\" resultMap=\"XxlJobInfo\">\n        SELECT <include refid=\"Base_Column_List\" />\n        FROM xxl_job_info AS t\n        WHERE t.trigger_status = 1\n            and t.trigger_next_time <![CDATA[ <= ]]> #{maxNextTime}\n        ORDER BY id ASC\n        LIMIT #{pagesize}\n    </select>\n\n    <update id=\"scheduleUpdate\" parameterType=\"com.xxl.job.admin.core.model.XxlJobInfo\"  >\n        UPDATE xxl_job_info\n        SET\n            trigger_last_time = #{triggerLastTime},\n            trigger_next_time = #{triggerNextTime},\n            trigger_status = #{triggerStatus}\n        WHERE id = #{id}\n    </update>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogGlueMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n    \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.xxl.job.admin.dao.XxlJobLogGlueDao\">\n\n    <resultMap id=\"XxlJobLogGlue\" type=\"com.xxl.job.admin.core.model.XxlJobLogGlue\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"job_id\" property=\"jobId\" />\n        <result column=\"glue_type\" property=\"glueType\" />\n        <result column=\"glue_source\" property=\"glueSource\" />\n        <result column=\"glue_remark\" property=\"glueRemark\" />\n        <result column=\"add_time\" property=\"addTime\" />\n        <result column=\"update_time\" property=\"updateTime\" />\n    </resultMap>\n\n    <sql id=\"Base_Column_List\">\n        t.id,\n        t.job_id,\n        t.glue_type,\n        t.glue_source,\n        t.glue_remark,\n        t.add_time,\n        t.update_time\n    </sql>\n\n    <insert id=\"save\" parameterType=\"com.xxl.job.admin.core.model.XxlJobLogGlue\" useGeneratedKeys=\"true\" keyProperty=\"id\" >\n        INSERT INTO xxl_job_logglue (\n            `job_id`,\n            `glue_type`,\n            `glue_source`,\n            `glue_remark`,\n            `add_time`,\n            `update_time`\n        ) VALUES (\n            #{jobId},\n            #{glueType},\n            #{glueSource},\n            #{glueRemark},\n            #{addTime},\n            #{updateTime}\n        );\n        <!--<selectKey resultType=\"java.lang.Integer\" order=\"AFTER\" keyProperty=\"id\">\n            SELECT LAST_INSERT_ID()\n        </selectKey>-->\n    </insert>\n\n    <select id=\"findByJobId\" parameterType=\"java.lang.Integer\" resultMap=\"XxlJobLogGlue\">\n        SELECT <include refid=\"Base_Column_List\" />\n        FROM xxl_job_logglue AS t\n        WHERE t.job_id = #{jobId}\n        ORDER BY id DESC\n    </select>\n\n    <delete id=\"removeOld\" >\n        DELETE FROM xxl_job_logglue\n        WHERE id NOT in(\n            SELECT id FROM(\n                SELECT id FROM xxl_job_logglue\n                WHERE `job_id` = #{jobId}\n                ORDER BY update_time desc\n                LIMIT 0, #{limit}\n            ) t1\n        ) AND `job_id` = #{jobId}\n    </delete>\n\n    <delete id=\"deleteByJobId\" parameterType=\"java.lang.Integer\" >\n        DELETE FROM xxl_job_logglue\n        WHERE `job_id` = #{jobId}\n    </delete>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n    \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.xxl.job.admin.dao.XxlJobLogDao\">\n\n    <resultMap id=\"XxlJobLog\" type=\"com.xxl.job.admin.core.model.XxlJobLog\" >\n        <result column=\"id\" property=\"id\" />\n\n        <result column=\"job_group\" property=\"jobGroup\" />\n        <result column=\"job_id\" property=\"jobId\" />\n\n        <result column=\"executor_address\" property=\"executorAddress\" />\n        <result column=\"executor_handler\" property=\"executorHandler\" />\n        <result column=\"executor_param\" property=\"executorParam\" />\n        <result column=\"executor_sharding_param\" property=\"executorShardingParam\" />\n        <result column=\"executor_fail_retry_count\" property=\"executorFailRetryCount\" />\n\n        <result column=\"trigger_time\" property=\"triggerTime\" />\n        <result column=\"trigger_code\" property=\"triggerCode\" />\n        <result column=\"trigger_msg\" property=\"triggerMsg\" />\n\n        <result column=\"handle_time\" property=\"handleTime\" />\n        <result column=\"handle_code\" property=\"handleCode\" />\n        <result column=\"handle_msg\" property=\"handleMsg\" />\n\n        <result column=\"alarm_status\" property=\"alarmStatus\" />\n    </resultMap>\n\n    <sql id=\"Base_Column_List\">\n        t.id,\n        t.job_group,\n        t.job_id,\n        t.executor_address,\n        t.executor_handler,\n        t.executor_param,\n        t.executor_sharding_param,\n        t.executor_fail_retry_count,\n        t.trigger_time,\n        t.trigger_code,\n        t.trigger_msg,\n        t.handle_time,\n        t.handle_code,\n        t.handle_msg,\n        t.alarm_status\n    </sql>\n\n    <select id=\"pageList\" resultMap=\"XxlJobLog\">\n        SELECT <include refid=\"Base_Column_List\" />\n        FROM xxl_job_log AS t\n        <trim prefix=\"WHERE\" prefixOverrides=\"AND | OR\" >\n            <if test=\"jobId==0 and jobGroup gt 0\">\n                AND t.job_group = #{jobGroup}\n            </if>\n            <if test=\"jobId gt 0\">\n                AND t.job_id = #{jobId}\n            </if>\n            <if test=\"triggerTimeStart != null\">\n                AND t.trigger_time <![CDATA[ >= ]]> #{triggerTimeStart}\n            </if>\n            <if test=\"triggerTimeEnd != null\">\n                AND t.trigger_time <![CDATA[ <= ]]> #{triggerTimeEnd}\n            </if>\n            <if test=\"logStatus == 1\" >\n                AND t.handle_code = 200\n            </if>\n            <if test=\"logStatus == 2\" >\n                AND (\n                    t.trigger_code NOT IN (0, 200) OR\n                    t.handle_code NOT IN (0, 200)\n                )\n            </if>\n            <if test=\"logStatus == 3\" >\n                AND t.trigger_code = 200\n                AND t.handle_code = 0\n            </if>\n        </trim>\n        ORDER BY t.trigger_time DESC\n        LIMIT #{offset}, #{pagesize}\n    </select>\n\n    <select id=\"pageListCount\" resultType=\"int\">\n        SELECT count(1)\n        FROM xxl_job_log AS t\n        <trim prefix=\"WHERE\" prefixOverrides=\"AND | OR\" >\n            <if test=\"jobId==0 and jobGroup gt 0\">\n                AND t.job_group = #{jobGroup}\n            </if>\n            <if test=\"jobId gt 0\">\n                AND t.job_id = #{jobId}\n            </if>\n            <if test=\"triggerTimeStart != null\">\n                AND t.trigger_time <![CDATA[ >= ]]> #{triggerTimeStart}\n            </if>\n            <if test=\"triggerTimeEnd != null\">\n                AND t.trigger_time <![CDATA[ <= ]]> #{triggerTimeEnd}\n            </if>\n            <if test=\"logStatus == 1\" >\n                AND t.handle_code = 200\n            </if>\n            <if test=\"logStatus == 2\" >\n                AND (\n                    t.trigger_code NOT IN (0, 200) OR\n                    t.handle_code NOT IN (0, 200)\n                )\n            </if>\n            <if test=\"logStatus == 3\" >\n                AND t.trigger_code = 200\n                AND t.handle_code = 0\n            </if>\n        </trim>\n    </select>\n\n    <select id=\"load\" parameterType=\"java.lang.Long\" resultMap=\"XxlJobLog\">\n        SELECT <include refid=\"Base_Column_List\" />\n        FROM xxl_job_log AS t\n        WHERE t.id = #{id}\n    </select>\n\n\n    <insert id=\"save\" parameterType=\"com.xxl.job.admin.core.model.XxlJobLog\" useGeneratedKeys=\"true\" keyProperty=\"id\" >\n        INSERT INTO xxl_job_log (\n            `job_group`,\n            `job_id`,\n            `trigger_time`,\n            `trigger_code`,\n            `handle_code`\n        ) VALUES (\n            #{jobGroup},\n            #{jobId},\n            #{triggerTime},\n            #{triggerCode},\n            #{handleCode}\n        );\n        <!--<selectKey resultType=\"java.lang.Integer\" order=\"AFTER\" keyProperty=\"id\">\n            SELECT LAST_INSERT_ID()\n        </selectKey>-->\n    </insert>\n\n    <update id=\"updateTriggerInfo\" >\n        UPDATE xxl_job_log\n        SET\n            `trigger_time`= #{triggerTime},\n            `trigger_code`= #{triggerCode},\n            `trigger_msg`= #{triggerMsg},\n            `executor_address`= #{executorAddress},\n            `executor_handler`=#{executorHandler},\n            `executor_param`= #{executorParam},\n            `executor_sharding_param`= #{executorShardingParam},\n            `executor_fail_retry_count`= #{executorFailRetryCount}\n        WHERE `id`= #{id}\n    </update>\n\n    <update id=\"updateHandleInfo\">\n        UPDATE xxl_job_log\n        SET\n            `handle_time`= #{handleTime},\n            `handle_code`= #{handleCode},\n            `handle_msg`= #{handleMsg}\n        WHERE `id`= #{id}\n    </update>\n\n    <delete id=\"delete\" >\n        delete from xxl_job_log\n        WHERE job_id = #{jobId}\n    </delete>\n\n    <!--<select id=\"triggerCountByDay\" resultType=\"java.util.Map\" >\n        SELECT\n            DATE_FORMAT(trigger_time,'%Y-%m-%d') triggerDay,\n            COUNT(handle_code) triggerDayCount,\n            SUM(CASE WHEN (trigger_code in (0, 200) and handle_code = 0) then 1 else 0 end) as triggerDayCountRunning,\n            SUM(CASE WHEN handle_code = 200 then 1 else 0 end) as triggerDayCountSuc\n        FROM xxl_job_log\n        WHERE trigger_time BETWEEN #{from} and #{to}\n        GROUP BY triggerDay\n        ORDER BY triggerDay\n    </select>-->\n\n    <select id=\"findLogReport\" resultType=\"java.util.Map\" >\n        SELECT\n            COUNT(handle_code) triggerDayCount,\n            SUM(CASE WHEN (trigger_code in (0, 200) and handle_code = 0) then 1 else 0 end) as triggerDayCountRunning,\n            SUM(CASE WHEN handle_code = 200 then 1 else 0 end) as triggerDayCountSuc\n        FROM xxl_job_log\n        WHERE trigger_time BETWEEN #{from} and #{to}\n    </select>\n\n    <select id=\"findClearLogIds\" resultType=\"long\" >\n        SELECT id FROM xxl_job_log\n        <trim prefix=\"WHERE\" prefixOverrides=\"AND | OR\" >\n            <if test=\"jobGroup gt 0\">\n                AND job_group = #{jobGroup}\n            </if>\n            <if test=\"jobId gt 0\">\n                AND job_id = #{jobId}\n            </if>\n            <if test=\"clearBeforeTime != null\">\n                AND trigger_time <![CDATA[ <= ]]> #{clearBeforeTime}\n            </if>\n            <if test=\"clearBeforeNum gt 0\">\n                AND id NOT in(\n                SELECT id FROM(\n                SELECT id FROM xxl_job_log AS t\n                <trim prefix=\"WHERE\" prefixOverrides=\"AND | OR\" >\n                    <if test=\"jobGroup gt 0\">\n                        AND t.job_group = #{jobGroup}\n                    </if>\n                    <if test=\"jobId gt 0\">\n                        AND t.job_id = #{jobId}\n                    </if>\n                </trim>\n                ORDER BY t.trigger_time desc\n                LIMIT 0, #{clearBeforeNum}\n                ) t1\n                )\n            </if>\n        </trim>\n        order by id asc\n        LIMIT #{pagesize}\n    </select>\n\n    <delete id=\"clearLog\" >\n        delete from xxl_job_log\n        WHERE id in\n        <foreach collection=\"logIds\" item=\"item\" open=\"(\" close=\")\" separator=\",\" >\n            #{item}\n        </foreach>\n    </delete>\n\n    <select id=\"findFailJobLogIds\" resultType=\"long\" >\n        SELECT id FROM `xxl_job_log`\n        WHERE !(\n            (trigger_code in (0, 200) and handle_code = 0)\n            OR\n            (handle_code = 200)\n        )\n        AND `alarm_status` = 0\n        ORDER BY id ASC\n        LIMIT #{pagesize}\n    </select>\n\n    <update id=\"updateAlarmStatus\" >\n        UPDATE xxl_job_log\n        SET\n            `alarm_status` = #{newAlarmStatus}\n        WHERE `id`= #{logId} AND `alarm_status` = #{oldAlarmStatus}\n    </update>\n\n    <select id=\"findLostJobIds\" resultType=\"long\" >\n        SELECT\n            t.id\n        FROM\n            xxl_job_log t\n            LEFT JOIN xxl_job_registry t2 ON t.executor_address = t2.registry_value\n        WHERE\n            t.trigger_code = 200\n                AND t.handle_code = 0\n                AND t.trigger_time <![CDATA[ <= ]]> #{losedTime}\n                AND t2.id IS NULL;\n    </select>\n    <!--\n    SELECT t.id\n    FROM xxl_job_log AS t\n    WHERE t.trigger_code = 200\n        and t.handle_code = 0\n        and t.trigger_time <![CDATA[ <= ]]> #{losedTime}\n        and t.executor_address not in (\n            SELECT t2.registry_value\n            FROM xxl_job_registry AS t2\n        )\n    -->\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogReportMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n    \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.xxl.job.admin.dao.XxlJobLogReportDao\">\n\n    <resultMap id=\"XxlJobLogReport\" type=\"com.xxl.job.admin.core.model.XxlJobLogReport\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"trigger_day\" property=\"triggerDay\" />\n        <result column=\"running_count\" property=\"runningCount\" />\n        <result column=\"suc_count\" property=\"sucCount\" />\n        <result column=\"fail_count\" property=\"failCount\" />\n    </resultMap>\n\n    <sql id=\"Base_Column_List\">\n        t.id,\n        t.trigger_day,\n        t.running_count,\n        t.suc_count,\n        t.fail_count\n    </sql>\n\n    <insert id=\"save\" parameterType=\"com.xxl.job.admin.core.model.XxlJobLogReport\" useGeneratedKeys=\"true\" keyProperty=\"id\" >\n        INSERT INTO xxl_job_log_report (\n            `trigger_day`,\n            `running_count`,\n            `suc_count`,\n            `fail_count`\n        ) VALUES (\n            #{triggerDay},\n            #{runningCount},\n            #{sucCount},\n            #{failCount}\n        );\n        <!--<selectKey resultType=\"java.lang.Integer\" order=\"AFTER\" keyProperty=\"id\">\n            SELECT LAST_INSERT_ID()\n        </selectKey>-->\n    </insert>\n\n    <update id=\"update\" >\n        UPDATE xxl_job_log_report\n        SET `running_count` = #{runningCount},\n            `suc_count` = #{sucCount},\n            `fail_count` = #{failCount}\n        WHERE `trigger_day` = #{triggerDay}\n    </update>\n\n    <select id=\"queryLogReport\" resultMap=\"XxlJobLogReport\">\n        SELECT <include refid=\"Base_Column_List\" />\n        FROM xxl_job_log_report AS t\n        WHERE t.trigger_day between #{triggerDayFrom} and #{triggerDayTo}\n        ORDER BY t.trigger_day ASC\n    </select>\n\n    <select id=\"queryLogReportTotal\" resultMap=\"XxlJobLogReport\">\n        SELECT\n            SUM(running_count) running_count,\n            SUM(suc_count) suc_count,\n            SUM(fail_count) fail_count\n        FROM xxl_job_log_report AS t\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobRegistryMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n    \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.xxl.job.admin.dao.XxlJobRegistryDao\">\n\n    <resultMap id=\"XxlJobRegistry\" type=\"com.xxl.job.admin.core.model.XxlJobRegistry\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"registry_group\" property=\"registryGroup\" />\n        <result column=\"registry_key\" property=\"registryKey\" />\n        <result column=\"registry_value\" property=\"registryValue\" />\n        <result column=\"update_time\" property=\"updateTime\" />\n    </resultMap>\n\n    <sql id=\"Base_Column_List\">\n        t.id,\n        t.registry_group,\n        t.registry_key,\n        t.registry_value,\n        t.update_time\n    </sql>\n\n    <select id=\"findDead\" parameterType=\"java.util.HashMap\" resultType=\"java.lang.Integer\" >\n        SELECT t.id\n        FROM xxl_job_registry AS t\n        WHERE t.update_time <![CDATA[ < ]]> DATE_ADD(#{nowTime},INTERVAL -#{timeout} SECOND)\n    </select>\n\n    <delete id=\"removeDead\" parameterType=\"java.lang.Integer\" >\n        DELETE FROM xxl_job_registry\n        WHERE id in\n        <foreach collection=\"ids\" item=\"item\" open=\"(\" close=\")\" separator=\",\" >\n            #{item}\n        </foreach>\n    </delete>\n\n    <select id=\"findAll\" parameterType=\"java.util.HashMap\" resultMap=\"XxlJobRegistry\">\n        SELECT <include refid=\"Base_Column_List\" />\n        FROM xxl_job_registry AS t\n        WHERE t.update_time <![CDATA[ > ]]> DATE_ADD(#{nowTime},INTERVAL -#{timeout} SECOND)\n    </select>\n\n    <update id=\"registryUpdate\" >\n        UPDATE xxl_job_registry\n        SET `update_time` = #{updateTime}\n        WHERE `registry_group` = #{registryGroup}\n          AND `registry_key` = #{registryKey}\n          AND `registry_value` = #{registryValue}\n    </update>\n\n    <insert id=\"registrySave\" >\n        INSERT INTO xxl_job_registry( `registry_group` , `registry_key` , `registry_value`, `update_time`)\n        VALUES( #{registryGroup}  , #{registryKey} , #{registryValue}, #{updateTime})\n    </insert>\n\n    <delete id=\"registryDelete\" >\n        DELETE FROM xxl_job_registry\n        WHERE registry_group = #{registryGroup}\n            AND registry_key = #{registryKey}\n            AND registry_value = #{registryValue}\n    </delete>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobUserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n    \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.xxl.job.admin.dao.XxlJobUserDao\">\n\n    <resultMap id=\"XxlJobUser\" type=\"com.xxl.job.admin.core.model.XxlJobUser\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"username\" property=\"username\" />\n        <result column=\"password\" property=\"password\" />\n        <result column=\"role\" property=\"role\" />\n        <result column=\"permission\" property=\"permission\" />\n    </resultMap>\n\n    <sql id=\"Base_Column_List\">\n        t.id,\n        t.username,\n        t.password,\n        t.role,\n        t.permission\n    </sql>\n\n    <select id=\"pageList\" parameterType=\"java.util.HashMap\" resultMap=\"XxlJobUser\">\n        SELECT <include refid=\"Base_Column_List\" />\n        FROM xxl_job_user AS t\n        <trim prefix=\"WHERE\" prefixOverrides=\"AND | OR\" >\n            <if test=\"username != null and username != ''\">\n                AND t.username like CONCAT(CONCAT('%', #{username}), '%')\n            </if>\n            <if test=\"role gt -1\">\n                AND t.role = #{role}\n            </if>\n        </trim>\n        ORDER BY username ASC\n        LIMIT #{offset}, #{pagesize}\n    </select>\n\n    <select id=\"pageListCount\" parameterType=\"java.util.HashMap\" resultType=\"int\">\n        SELECT count(1)\n        FROM xxl_job_user AS t\n        <trim prefix=\"WHERE\" prefixOverrides=\"AND | OR\" >\n            <if test=\"username != null and username != ''\">\n                AND t.username like CONCAT(CONCAT('%', #{username}), '%')\n            </if>\n            <if test=\"role gt -1\">\n                AND t.role = #{role}\n            </if>\n        </trim>\n    </select>\n\n    <select id=\"loadByUserName\" parameterType=\"java.util.HashMap\" resultMap=\"XxlJobUser\">\n        SELECT <include refid=\"Base_Column_List\" />\n        FROM xxl_job_user AS t\n        WHERE t.username = #{username}\n    </select>\n\n    <insert id=\"save\" parameterType=\"com.xxl.job.admin.core.model.XxlJobUser\" useGeneratedKeys=\"true\" keyProperty=\"id\" >\n        INSERT INTO xxl_job_user (\n            username,\n            password,\n            role,\n            permission\n        ) VALUES (\n            #{username},\n            #{password},\n            #{role},\n            #{permission}\n        );\n    </insert>\n\n    <update id=\"update\" parameterType=\"com.xxl.job.admin.core.model.XxlJobUser\" >\n        UPDATE xxl_job_user\n        SET\n            <if test=\"password != null and password != ''\">\n                password = #{password},\n            </if>\n            role = #{role},\n            permission = #{permission}\n        WHERE id = #{id}\n    </update>\n\n    <delete id=\"delete\" parameterType=\"java.util.HashMap\">\n        DELETE\n        FROM xxl_job_user\n        WHERE id = #{id}\n    </delete>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/adminlte/bower_components/PACE/themes/blue/pace-theme-flash.css",
    "content": "/* This is a compiled file, you should be editing the file in the templates directory */\n.pace {\n  -webkit-pointer-events: none;\n  pointer-events: none;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  user-select: none;\n}\n\n.pace-inactive {\n  display: none;\n}\n\n.pace .pace-progress {\n  background: #2299dd;\n  position: fixed;\n  z-index: 2000;\n  top: 0;\n  right: 100%;\n  width: 100%;\n  height: 2px;\n}\n\n.pace .pace-progress-inner {\n  display: block;\n  position: absolute;\n  right: 0px;\n  width: 100px;\n  height: 100%;\n  box-shadow: 0 0 10px #2299dd, 0 0 5px #2299dd;\n  opacity: 1.0;\n  -webkit-transform: rotate(3deg) translate(0px, -4px);\n  -moz-transform: rotate(3deg) translate(0px, -4px);\n  -ms-transform: rotate(3deg) translate(0px, -4px);\n  -o-transform: rotate(3deg) translate(0px, -4px);\n  transform: rotate(3deg) translate(0px, -4px);\n}\n\n.pace .pace-activity {\n  display: block;\n  position: fixed;\n  z-index: 2000;\n  top: 15px;\n  right: 15px;\n  width: 14px;\n  height: 14px;\n  border: solid 2px transparent;\n  border-top-color: #2299dd;\n  border-left-color: #2299dd;\n  border-radius: 10px;\n  -webkit-animation: pace-spinner 400ms linear infinite;\n  -moz-animation: pace-spinner 400ms linear infinite;\n  -ms-animation: pace-spinner 400ms linear infinite;\n  -o-animation: pace-spinner 400ms linear infinite;\n  animation: pace-spinner 400ms linear infinite;\n}\n\n@-webkit-keyframes pace-spinner {\n  0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); }\n  100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); }\n}\n@-moz-keyframes pace-spinner {\n  0% { -moz-transform: rotate(0deg); transform: rotate(0deg); }\n  100% { -moz-transform: rotate(360deg); transform: rotate(360deg); }\n}\n@-o-keyframes pace-spinner {\n  0% { -o-transform: rotate(0deg); transform: rotate(0deg); }\n  100% { -o-transform: rotate(360deg); transform: rotate(360deg); }\n}\n@-ms-keyframes pace-spinner {\n  0% { -ms-transform: rotate(0deg); transform: rotate(0deg); }\n  100% { -ms-transform: rotate(360deg); transform: rotate(360deg); }\n}\n@keyframes pace-spinner {\n  0% { transform: rotate(0deg); transform: rotate(0deg); }\n  100% { transform: rotate(360deg); transform: rotate(360deg); }\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/adminlte/bower_components/bootstrap-daterangepicker/daterangepicker.css",
    "content": ".daterangepicker {\n  position: absolute;\n  color: inherit;\n  background-color: #fff;\n  border-radius: 4px;\n  width: 278px;\n  padding: 4px;\n  margin-top: 1px;\n  top: 100px;\n  left: 20px;\n  /* Calendars */ }\n  .daterangepicker:before, .daterangepicker:after {\n    position: absolute;\n    display: inline-block;\n    border-bottom-color: rgba(0, 0, 0, 0.2);\n    content: ''; }\n  .daterangepicker:before {\n    top: -7px;\n    border-right: 7px solid transparent;\n    border-left: 7px solid transparent;\n    border-bottom: 7px solid #ccc; }\n  .daterangepicker:after {\n    top: -6px;\n    border-right: 6px solid transparent;\n    border-bottom: 6px solid #fff;\n    border-left: 6px solid transparent; }\n  .daterangepicker.opensleft:before {\n    right: 9px; }\n  .daterangepicker.opensleft:after {\n    right: 10px; }\n  .daterangepicker.openscenter:before {\n    left: 0;\n    right: 0;\n    width: 0;\n    margin-left: auto;\n    margin-right: auto; }\n  .daterangepicker.openscenter:after {\n    left: 0;\n    right: 0;\n    width: 0;\n    margin-left: auto;\n    margin-right: auto; }\n  .daterangepicker.opensright:before {\n    left: 9px; }\n  .daterangepicker.opensright:after {\n    left: 10px; }\n  .daterangepicker.dropup {\n    margin-top: -5px; }\n    .daterangepicker.dropup:before {\n      top: initial;\n      bottom: -7px;\n      border-bottom: initial;\n      border-top: 7px solid #ccc; }\n    .daterangepicker.dropup:after {\n      top: initial;\n      bottom: -6px;\n      border-bottom: initial;\n      border-top: 6px solid #fff; }\n  .daterangepicker.dropdown-menu {\n    max-width: none;\n    z-index: 3001; }\n  .daterangepicker.single .ranges, .daterangepicker.single .calendar {\n    float: none; }\n  .daterangepicker.show-calendar .calendar {\n    display: block; }\n  .daterangepicker .calendar {\n    display: none;\n    max-width: 270px;\n    margin: 4px; }\n    .daterangepicker .calendar.single .calendar-table {\n      border: none; }\n    .daterangepicker .calendar th, .daterangepicker .calendar td {\n      white-space: nowrap;\n      text-align: center;\n      min-width: 32px; }\n  .daterangepicker .calendar-table {\n    border: 1px solid #fff;\n    padding: 4px;\n    border-radius: 4px;\n    background-color: #fff; }\n  .daterangepicker table {\n    width: 100%;\n    margin: 0; }\n  .daterangepicker td, .daterangepicker th {\n    text-align: center;\n    width: 20px;\n    height: 20px;\n    border-radius: 4px;\n    border: 1px solid transparent;\n    white-space: nowrap;\n    cursor: pointer; }\n    .daterangepicker td.available:hover, .daterangepicker th.available:hover {\n      background-color: #eee;\n      border-color: transparent;\n      color: inherit; }\n    .daterangepicker td.week, .daterangepicker th.week {\n      font-size: 80%;\n      color: #ccc; }\n  .daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {\n    background-color: #fff;\n    border-color: transparent;\n    color: #999; }\n  .daterangepicker td.in-range {\n    background-color: #ebf4f8;\n    border-color: transparent;\n    color: #000;\n    border-radius: 0; }\n  .daterangepicker td.start-date {\n    border-radius: 4px 0 0 4px; }\n  .daterangepicker td.end-date {\n    border-radius: 0 4px 4px 0; }\n  .daterangepicker td.start-date.end-date {\n    border-radius: 4px; }\n  .daterangepicker td.active, .daterangepicker td.active:hover {\n    background-color: #357ebd;\n    border-color: transparent;\n    color: #fff; }\n  .daterangepicker th.month {\n    width: auto; }\n  .daterangepicker td.disabled, .daterangepicker option.disabled {\n    color: #999;\n    cursor: not-allowed;\n    text-decoration: line-through; }\n  .daterangepicker select.monthselect, .daterangepicker select.yearselect {\n    font-size: 12px;\n    padding: 1px;\n    height: auto;\n    margin: 0;\n    cursor: default; }\n  .daterangepicker select.monthselect {\n    margin-right: 2%;\n    width: 56%; }\n  .daterangepicker select.yearselect {\n    width: 40%; }\n  .daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {\n    width: 50px;\n    margin-bottom: 0; }\n  .daterangepicker .input-mini {\n    border: 1px solid #ccc;\n    border-radius: 4px;\n    color: #555;\n    height: 30px;\n    line-height: 30px;\n    display: block;\n    vertical-align: middle;\n    margin: 0 0 5px 0;\n    padding: 0 6px 0 28px;\n    width: 100%; }\n    .daterangepicker .input-mini.active {\n      border: 1px solid #08c;\n      border-radius: 4px; }\n  .daterangepicker .daterangepicker_input {\n    position: relative; }\n    .daterangepicker .daterangepicker_input i {\n      position: absolute;\n      left: 8px;\n      top: 8px; }\n  .daterangepicker.rtl .input-mini {\n    padding-right: 28px;\n    padding-left: 6px; }\n  .daterangepicker.rtl .daterangepicker_input i {\n    left: auto;\n    right: 8px; }\n  .daterangepicker .calendar-time {\n    text-align: center;\n    margin: 5px auto;\n    line-height: 30px;\n    position: relative;\n    padding-left: 28px; }\n    .daterangepicker .calendar-time select.disabled {\n      color: #ccc;\n      cursor: not-allowed; }\n\n.ranges {\n  font-size: 11px;\n  float: none;\n  margin: 4px;\n  text-align: left; }\n  .ranges ul {\n    list-style: none;\n    margin: 0 auto;\n    padding: 0;\n    width: 100%; }\n  .ranges li {\n    font-size: 13px;\n    background-color: #f5f5f5;\n    border: 1px solid #f5f5f5;\n    border-radius: 4px;\n    color: #08c;\n    padding: 3px 12px;\n    margin-bottom: 8px;\n    cursor: pointer; }\n    .ranges li:hover {\n      background-color: #08c;\n      border: 1px solid #08c;\n      color: #fff; }\n    .ranges li.active {\n      background-color: #08c;\n      border: 1px solid #08c;\n      color: #fff; }\n\n/*  Larger Screen Styling */\n@media (min-width: 564px) {\n  .daterangepicker {\n    width: auto; }\n    .daterangepicker .ranges ul {\n      width: 160px; }\n    .daterangepicker.single .ranges ul {\n      width: 100%; }\n    .daterangepicker.single .calendar.left {\n      clear: none; }\n    .daterangepicker.single.ltr .ranges, .daterangepicker.single.ltr .calendar {\n      float: left; }\n    .daterangepicker.single.rtl .ranges, .daterangepicker.single.rtl .calendar {\n      float: right; }\n    .daterangepicker.ltr {\n      direction: ltr;\n      text-align: left; }\n      .daterangepicker.ltr .calendar.left {\n        clear: left;\n        margin-right: 0; }\n        .daterangepicker.ltr .calendar.left .calendar-table {\n          border-right: none;\n          border-top-right-radius: 0;\n          border-bottom-right-radius: 0; }\n      .daterangepicker.ltr .calendar.right {\n        margin-left: 0; }\n        .daterangepicker.ltr .calendar.right .calendar-table {\n          border-left: none;\n          border-top-left-radius: 0;\n          border-bottom-left-radius: 0; }\n      .daterangepicker.ltr .left .daterangepicker_input {\n        padding-right: 12px; }\n      .daterangepicker.ltr .calendar.left .calendar-table {\n        padding-right: 12px; }\n      .daterangepicker.ltr .ranges, .daterangepicker.ltr .calendar {\n        float: left; }\n    .daterangepicker.rtl {\n      direction: rtl;\n      text-align: right; }\n      .daterangepicker.rtl .calendar.left {\n        clear: right;\n        margin-left: 0; }\n        .daterangepicker.rtl .calendar.left .calendar-table {\n          border-left: none;\n          border-top-left-radius: 0;\n          border-bottom-left-radius: 0; }\n      .daterangepicker.rtl .calendar.right {\n        margin-right: 0; }\n        .daterangepicker.rtl .calendar.right .calendar-table {\n          border-right: none;\n          border-top-right-radius: 0;\n          border-bottom-right-radius: 0; }\n      .daterangepicker.rtl .left .daterangepicker_input {\n        padding-left: 12px; }\n      .daterangepicker.rtl .calendar.left .calendar-table {\n        padding-left: 12px; }\n      .daterangepicker.rtl .ranges, .daterangepicker.rtl .calendar {\n        text-align: right;\n        float: right; } }\n@media (min-width: 730px) {\n  .daterangepicker .ranges {\n    width: auto; }\n  .daterangepicker.ltr .ranges {\n    float: left; }\n  .daterangepicker.rtl .ranges {\n    float: right; }\n  .daterangepicker .calendar.left {\n    clear: none !important; } }\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/adminlte/bower_components/bootstrap-daterangepicker/daterangepicker.js",
    "content": "/**\n* @version: 2.1.27\n* @author: Dan Grossman http://www.dangrossman.info/\n* @copyright: Copyright (c) 2012-2017 Dan Grossman. All rights reserved.\n* @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php\n* @website: http://www.daterangepicker.com/\n*/\n// Follow the UMD template https://github.com/umdjs/umd/blob/master/templates/returnExportsGlobal.js\n(function (root, factory) {\n    if (typeof define === 'function' && define.amd) {\n        // AMD. Make globaly available as well\n        define(['moment', 'jquery'], function (moment, jquery) {\n            if (!jquery.fn) jquery.fn = {}; // webpack server rendering\n            return factory(moment, jquery);\n        });\n    } else if (typeof module === 'object' && module.exports) {\n        // Node / Browserify\n        //isomorphic issue\n        var jQuery = (typeof window != 'undefined') ? window.jQuery : undefined;\n        if (!jQuery) {\n            jQuery = require('jquery');\n            if (!jQuery.fn) jQuery.fn = {};\n        }\n        var moment = (typeof window != 'undefined' && typeof window.moment != 'undefined') ? window.moment : require('moment');\n        module.exports = factory(moment, jQuery);\n    } else {\n        // Browser globals\n        root.daterangepicker = factory(root.moment, root.jQuery);\n    }\n}(this, function(moment, $) {\n    var DateRangePicker = function(element, options, cb) {\n\n        //default settings for options\n        this.parentEl = 'body';\n        this.element = $(element);\n        this.startDate = moment().startOf('day');\n        this.endDate = moment().endOf('day');\n        this.minDate = false;\n        this.maxDate = false;\n        this.dateLimit = false;\n        this.autoApply = false;\n        this.singleDatePicker = false;\n        this.showDropdowns = false;\n        this.showWeekNumbers = false;\n        this.showISOWeekNumbers = false;\n        this.showCustomRangeLabel = true;\n        this.timePicker = false;\n        this.timePicker24Hour = false;\n        this.timePickerIncrement = 1;\n        this.timePickerSeconds = false;\n        this.linkedCalendars = true;\n        this.autoUpdateInput = true;\n        this.alwaysShowCalendars = false;\n        this.ranges = {};\n\n        this.opens = 'right';\n        if (this.element.hasClass('pull-right'))\n            this.opens = 'left';\n\n        this.drops = 'down';\n        if (this.element.hasClass('dropup'))\n            this.drops = 'up';\n\n        this.buttonClasses = 'btn btn-sm';\n        this.applyClass = 'btn-success';\n        this.cancelClass = 'btn-default';\n\n        this.locale = {\n            direction: 'ltr',\n            format: moment.localeData().longDateFormat('L'),\n            separator: ' - ',\n            applyLabel: 'Apply',\n            cancelLabel: 'Cancel',\n            weekLabel: 'W',\n            customRangeLabel: 'Custom Range',\n            daysOfWeek: moment.weekdaysMin(),\n            monthNames: moment.monthsShort(),\n            firstDay: moment.localeData().firstDayOfWeek()\n        };\n\n        this.callback = function() { };\n\n        //some state information\n        this.isShowing = false;\n        this.leftCalendar = {};\n        this.rightCalendar = {};\n\n        //custom options from user\n        if (typeof options !== 'object' || options === null)\n            options = {};\n\n        //allow setting options with data attributes\n        //data-api options will be overwritten with custom javascript options\n        options = $.extend(this.element.data(), options);\n\n        //html template for the picker UI\n        if (typeof options.template !== 'string' && !(options.template instanceof $))\n            options.template = '<div class=\"daterangepicker dropdown-menu\">' +\n                '<div class=\"calendar left\">' +\n                    '<div class=\"daterangepicker_input\">' +\n                      '<input class=\"input-mini form-control\" type=\"text\" name=\"daterangepicker_start\" value=\"\" />' +\n                      '<i class=\"fa fa-calendar glyphicon glyphicon-calendar\"></i>' +\n                      '<div class=\"calendar-time\">' +\n                        '<div></div>' +\n                        '<i class=\"fa fa-clock-o glyphicon glyphicon-time\"></i>' +\n                      '</div>' +\n                    '</div>' +\n                    '<div class=\"calendar-table\"></div>' +\n                '</div>' +\n                '<div class=\"calendar right\">' +\n                    '<div class=\"daterangepicker_input\">' +\n                      '<input class=\"input-mini form-control\" type=\"text\" name=\"daterangepicker_end\" value=\"\" />' +\n                      '<i class=\"fa fa-calendar glyphicon glyphicon-calendar\"></i>' +\n                      '<div class=\"calendar-time\">' +\n                        '<div></div>' +\n                        '<i class=\"fa fa-clock-o glyphicon glyphicon-time\"></i>' +\n                      '</div>' +\n                    '</div>' +\n                    '<div class=\"calendar-table\"></div>' +\n                '</div>' +\n                '<div class=\"ranges\">' +\n                    '<div class=\"range_inputs\">' +\n                        '<button class=\"applyBtn\" disabled=\"disabled\" type=\"button\"></button> ' +\n                        '<button class=\"cancelBtn\" type=\"button\"></button>' +\n                    '</div>' +\n                '</div>' +\n            '</div>';\n\n        this.parentEl = (options.parentEl && $(options.parentEl).length) ? $(options.parentEl) : $(this.parentEl);\n        this.container = $(options.template).appendTo(this.parentEl);\n\n        //\n        // handle all the possible options overriding defaults\n        //\n\n        if (typeof options.locale === 'object') {\n\n            if (typeof options.locale.direction === 'string')\n                this.locale.direction = options.locale.direction;\n\n            if (typeof options.locale.format === 'string')\n                this.locale.format = options.locale.format;\n\n            if (typeof options.locale.separator === 'string')\n                this.locale.separator = options.locale.separator;\n\n            if (typeof options.locale.daysOfWeek === 'object')\n                this.locale.daysOfWeek = options.locale.daysOfWeek.slice();\n\n            if (typeof options.locale.monthNames === 'object')\n              this.locale.monthNames = options.locale.monthNames.slice();\n\n            if (typeof options.locale.firstDay === 'number')\n              this.locale.firstDay = options.locale.firstDay;\n\n            if (typeof options.locale.applyLabel === 'string')\n              this.locale.applyLabel = options.locale.applyLabel;\n\n            if (typeof options.locale.cancelLabel === 'string')\n              this.locale.cancelLabel = options.locale.cancelLabel;\n\n            if (typeof options.locale.weekLabel === 'string')\n              this.locale.weekLabel = options.locale.weekLabel;\n\n            if (typeof options.locale.customRangeLabel === 'string'){\n                //Support unicode chars in the custom range name.\n                var elem = document.createElement('textarea');\n                elem.innerHTML = options.locale.customRangeLabel;\n                var rangeHtml = elem.value;\n                this.locale.customRangeLabel = rangeHtml;\n            }\n        }\n        this.container.addClass(this.locale.direction);\n\n        if (typeof options.startDate === 'string')\n            this.startDate = moment(options.startDate, this.locale.format);\n\n        if (typeof options.endDate === 'string')\n            this.endDate = moment(options.endDate, this.locale.format);\n\n        if (typeof options.minDate === 'string')\n            this.minDate = moment(options.minDate, this.locale.format);\n\n        if (typeof options.maxDate === 'string')\n            this.maxDate = moment(options.maxDate, this.locale.format);\n\n        if (typeof options.startDate === 'object')\n            this.startDate = moment(options.startDate);\n\n        if (typeof options.endDate === 'object')\n            this.endDate = moment(options.endDate);\n\n        if (typeof options.minDate === 'object')\n            this.minDate = moment(options.minDate);\n\n        if (typeof options.maxDate === 'object')\n            this.maxDate = moment(options.maxDate);\n\n        // sanity check for bad options\n        if (this.minDate && this.startDate.isBefore(this.minDate))\n            this.startDate = this.minDate.clone();\n\n        // sanity check for bad options\n        if (this.maxDate && this.endDate.isAfter(this.maxDate))\n            this.endDate = this.maxDate.clone();\n\n        if (typeof options.applyClass === 'string')\n            this.applyClass = options.applyClass;\n\n        if (typeof options.cancelClass === 'string')\n            this.cancelClass = options.cancelClass;\n\n        if (typeof options.dateLimit === 'object')\n            this.dateLimit = options.dateLimit;\n\n        if (typeof options.opens === 'string')\n            this.opens = options.opens;\n\n        if (typeof options.drops === 'string')\n            this.drops = options.drops;\n\n        if (typeof options.showWeekNumbers === 'boolean')\n            this.showWeekNumbers = options.showWeekNumbers;\n\n        if (typeof options.showISOWeekNumbers === 'boolean')\n            this.showISOWeekNumbers = options.showISOWeekNumbers;\n\n        if (typeof options.buttonClasses === 'string')\n            this.buttonClasses = options.buttonClasses;\n\n        if (typeof options.buttonClasses === 'object')\n            this.buttonClasses = options.buttonClasses.join(' ');\n\n        if (typeof options.showDropdowns === 'boolean')\n            this.showDropdowns = options.showDropdowns;\n\n        if (typeof options.showCustomRangeLabel === 'boolean')\n            this.showCustomRangeLabel = options.showCustomRangeLabel;\n\n        if (typeof options.singleDatePicker === 'boolean') {\n            this.singleDatePicker = options.singleDatePicker;\n            if (this.singleDatePicker)\n                this.endDate = this.startDate.clone();\n        }\n\n        if (typeof options.timePicker === 'boolean')\n            this.timePicker = options.timePicker;\n\n        if (typeof options.timePickerSeconds === 'boolean')\n            this.timePickerSeconds = options.timePickerSeconds;\n\n        if (typeof options.timePickerIncrement === 'number')\n            this.timePickerIncrement = options.timePickerIncrement;\n\n        if (typeof options.timePicker24Hour === 'boolean')\n            this.timePicker24Hour = options.timePicker24Hour;\n\n        if (typeof options.autoApply === 'boolean')\n            this.autoApply = options.autoApply;\n\n        if (typeof options.autoUpdateInput === 'boolean')\n            this.autoUpdateInput = options.autoUpdateInput;\n\n        if (typeof options.linkedCalendars === 'boolean')\n            this.linkedCalendars = options.linkedCalendars;\n\n        if (typeof options.isInvalidDate === 'function')\n            this.isInvalidDate = options.isInvalidDate;\n\n        if (typeof options.isCustomDate === 'function')\n            this.isCustomDate = options.isCustomDate;\n\n        if (typeof options.alwaysShowCalendars === 'boolean')\n            this.alwaysShowCalendars = options.alwaysShowCalendars;\n\n        // update day names order to firstDay\n        if (this.locale.firstDay != 0) {\n            var iterator = this.locale.firstDay;\n            while (iterator > 0) {\n                this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift());\n                iterator--;\n            }\n        }\n\n        var start, end, range;\n\n        //if no start/end dates set, check if an input element contains initial values\n        if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') {\n            if ($(this.element).is('input[type=text]')) {\n                var val = $(this.element).val(),\n                    split = val.split(this.locale.separator);\n\n                start = end = null;\n\n                if (split.length == 2) {\n                    start = moment(split[0], this.locale.format);\n                    end = moment(split[1], this.locale.format);\n                } else if (this.singleDatePicker && val !== \"\") {\n                    start = moment(val, this.locale.format);\n                    end = moment(val, this.locale.format);\n                }\n                if (start !== null && end !== null) {\n                    this.setStartDate(start);\n                    this.setEndDate(end);\n                }\n            }\n        }\n\n        if (typeof options.ranges === 'object') {\n            for (range in options.ranges) {\n\n                if (typeof options.ranges[range][0] === 'string')\n                    start = moment(options.ranges[range][0], this.locale.format);\n                else\n                    start = moment(options.ranges[range][0]);\n\n                if (typeof options.ranges[range][1] === 'string')\n                    end = moment(options.ranges[range][1], this.locale.format);\n                else\n                    end = moment(options.ranges[range][1]);\n\n                // If the start or end date exceed those allowed by the minDate or dateLimit\n                // options, shorten the range to the allowable period.\n                if (this.minDate && start.isBefore(this.minDate))\n                    start = this.minDate.clone();\n\n                var maxDate = this.maxDate;\n                if (this.dateLimit && maxDate && start.clone().add(this.dateLimit).isAfter(maxDate))\n                    maxDate = start.clone().add(this.dateLimit);\n                if (maxDate && end.isAfter(maxDate))\n                    end = maxDate.clone();\n\n                // If the end of the range is before the minimum or the start of the range is\n                // after the maximum, don't display this range option at all.\n                if ((this.minDate && end.isBefore(this.minDate, this.timepicker ? 'minute' : 'day')) \n                  || (maxDate && start.isAfter(maxDate, this.timepicker ? 'minute' : 'day')))\n                    continue;\n\n                //Support unicode chars in the range names.\n                var elem = document.createElement('textarea');\n                elem.innerHTML = range;\n                var rangeHtml = elem.value;\n\n                this.ranges[rangeHtml] = [start, end];\n            }\n\n            var list = '<ul>';\n            for (range in this.ranges) {\n                list += '<li data-range-key=\"' + range + '\">' + range + '</li>';\n            }\n            if (this.showCustomRangeLabel) {\n                list += '<li data-range-key=\"' + this.locale.customRangeLabel + '\">' + this.locale.customRangeLabel + '</li>';\n            }\n            list += '</ul>';\n            this.container.find('.ranges').prepend(list);\n        }\n\n        if (typeof cb === 'function') {\n            this.callback = cb;\n        }\n\n        if (!this.timePicker) {\n            this.startDate = this.startDate.startOf('day');\n            this.endDate = this.endDate.endOf('day');\n            this.container.find('.calendar-time').hide();\n        }\n\n        //can't be used together for now\n        if (this.timePicker && this.autoApply)\n            this.autoApply = false;\n\n        if (this.autoApply && typeof options.ranges !== 'object') {\n            this.container.find('.ranges').hide();\n        } else if (this.autoApply) {\n            this.container.find('.applyBtn, .cancelBtn').addClass('hide');\n        }\n\n        if (this.singleDatePicker) {\n            this.container.addClass('single');\n            this.container.find('.calendar.left').addClass('single');\n            this.container.find('.calendar.left').show();\n            this.container.find('.calendar.right').hide();\n            this.container.find('.daterangepicker_input input, .daterangepicker_input > i').hide();\n            if (this.timePicker) {\n                this.container.find('.ranges ul').hide();\n            } else {\n                this.container.find('.ranges').hide();\n            }\n        }\n\n        if ((typeof options.ranges === 'undefined' && !this.singleDatePicker) || this.alwaysShowCalendars) {\n            this.container.addClass('show-calendar');\n        }\n\n        this.container.addClass('opens' + this.opens);\n\n        //swap the position of the predefined ranges if opens right\n        if (typeof options.ranges !== 'undefined' && this.opens == 'right') {\n            this.container.find('.ranges').prependTo( this.container.find('.calendar.left').parent() );\n        }\n\n        //apply CSS classes and labels to buttons\n        this.container.find('.applyBtn, .cancelBtn').addClass(this.buttonClasses);\n        if (this.applyClass.length)\n            this.container.find('.applyBtn').addClass(this.applyClass);\n        if (this.cancelClass.length)\n            this.container.find('.cancelBtn').addClass(this.cancelClass);\n        this.container.find('.applyBtn').html(this.locale.applyLabel);\n        this.container.find('.cancelBtn').html(this.locale.cancelLabel);\n\n        //\n        // event listeners\n        //\n\n        this.container.find('.calendar')\n            .on('click.daterangepicker', '.prev', $.proxy(this.clickPrev, this))\n            .on('click.daterangepicker', '.next', $.proxy(this.clickNext, this))\n            .on('mousedown.daterangepicker', 'td.available', $.proxy(this.clickDate, this))\n            .on('mouseenter.daterangepicker', 'td.available', $.proxy(this.hoverDate, this))\n            .on('mouseleave.daterangepicker', 'td.available', $.proxy(this.updateFormInputs, this))\n            .on('change.daterangepicker', 'select.yearselect', $.proxy(this.monthOrYearChanged, this))\n            .on('change.daterangepicker', 'select.monthselect', $.proxy(this.monthOrYearChanged, this))\n            .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged, this))\n            .on('click.daterangepicker', '.daterangepicker_input input', $.proxy(this.showCalendars, this))\n            .on('focus.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsFocused, this))\n            .on('blur.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsBlurred, this))\n            .on('change.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged, this))\n            .on('keydown.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsKeydown, this));\n\n        this.container.find('.ranges')\n            .on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this))\n            .on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this))\n            .on('click.daterangepicker', 'li', $.proxy(this.clickRange, this))\n            .on('mouseenter.daterangepicker', 'li', $.proxy(this.hoverRange, this))\n            .on('mouseleave.daterangepicker', 'li', $.proxy(this.updateFormInputs, this));\n\n        if (this.element.is('input') || this.element.is('button')) {\n            this.element.on({\n                'click.daterangepicker': $.proxy(this.show, this),\n                'focus.daterangepicker': $.proxy(this.show, this),\n                'keyup.daterangepicker': $.proxy(this.elementChanged, this),\n                'keydown.daterangepicker': $.proxy(this.keydown, this) //IE 11 compatibility\n            });\n        } else {\n            this.element.on('click.daterangepicker', $.proxy(this.toggle, this));\n            this.element.on('keydown.daterangepicker', $.proxy(this.toggle, this));\n        }\n\n        //\n        // if attached to a text input, set the initial value\n        //\n\n        if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) {\n            this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));\n            this.element.trigger('change');\n        } else if (this.element.is('input') && this.autoUpdateInput) {\n            this.element.val(this.startDate.format(this.locale.format));\n            this.element.trigger('change');\n        }\n\n    };\n\n    DateRangePicker.prototype = {\n\n        constructor: DateRangePicker,\n\n        setStartDate: function(startDate) {\n            if (typeof startDate === 'string')\n                this.startDate = moment(startDate, this.locale.format);\n\n            if (typeof startDate === 'object')\n                this.startDate = moment(startDate);\n\n            if (!this.timePicker)\n                this.startDate = this.startDate.startOf('day');\n\n            if (this.timePicker && this.timePickerIncrement)\n                this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);\n\n            if (this.minDate && this.startDate.isBefore(this.minDate)) {\n                this.startDate = this.minDate.clone();\n                if (this.timePicker && this.timePickerIncrement)\n                    this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);\n            }\n\n            if (this.maxDate && this.startDate.isAfter(this.maxDate)) {\n                this.startDate = this.maxDate.clone();\n                if (this.timePicker && this.timePickerIncrement)\n                    this.startDate.minute(Math.floor(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);\n            }\n\n            if (!this.isShowing)\n                this.updateElement();\n\n            this.updateMonthsInView();\n        },\n\n        setEndDate: function(endDate) {\n            if (typeof endDate === 'string')\n                this.endDate = moment(endDate, this.locale.format);\n\n            if (typeof endDate === 'object')\n                this.endDate = moment(endDate);\n\n            if (!this.timePicker)\n                this.endDate = this.endDate.add(1,'d').startOf('day').subtract(1,'second');\n\n            if (this.timePicker && this.timePickerIncrement)\n                this.endDate.minute(Math.round(this.endDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);\n\n            if (this.endDate.isBefore(this.startDate))\n                this.endDate = this.startDate.clone();\n\n            if (this.maxDate && this.endDate.isAfter(this.maxDate))\n                this.endDate = this.maxDate.clone();\n\n            if (this.dateLimit && this.startDate.clone().add(this.dateLimit).isBefore(this.endDate))\n                this.endDate = this.startDate.clone().add(this.dateLimit);\n\n            this.previousRightTime = this.endDate.clone();\n\n            if (!this.isShowing)\n                this.updateElement();\n\n            this.updateMonthsInView();\n        },\n\n        isInvalidDate: function() {\n            return false;\n        },\n\n        isCustomDate: function() {\n            return false;\n        },\n\n        updateView: function() {\n            if (this.timePicker) {\n                this.renderTimePicker('left');\n                this.renderTimePicker('right');\n                if (!this.endDate) {\n                    this.container.find('.right .calendar-time select').attr('disabled', 'disabled').addClass('disabled');\n                } else {\n                    this.container.find('.right .calendar-time select').removeAttr('disabled').removeClass('disabled');\n                }\n            }\n            if (this.endDate) {\n                this.container.find('input[name=\"daterangepicker_end\"]').removeClass('active');\n                this.container.find('input[name=\"daterangepicker_start\"]').addClass('active');\n            } else {\n                this.container.find('input[name=\"daterangepicker_end\"]').addClass('active');\n                this.container.find('input[name=\"daterangepicker_start\"]').removeClass('active');\n            }\n            this.updateMonthsInView();\n            this.updateCalendars();\n            this.updateFormInputs();\n        },\n\n        updateMonthsInView: function() {\n            if (this.endDate) {\n\n                //if both dates are visible already, do nothing\n                if (!this.singleDatePicker && this.leftCalendar.month && this.rightCalendar.month &&\n                    (this.startDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.startDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM'))\n                    &&\n                    (this.endDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.endDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM'))\n                    ) {\n                    return;\n                }\n\n                this.leftCalendar.month = this.startDate.clone().date(2);\n                if (!this.linkedCalendars && (this.endDate.month() != this.startDate.month() || this.endDate.year() != this.startDate.year())) {\n                    this.rightCalendar.month = this.endDate.clone().date(2);\n                } else {\n                    this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month');\n                }\n\n            } else {\n                if (this.leftCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM') && this.rightCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM')) {\n                    this.leftCalendar.month = this.startDate.clone().date(2);\n                    this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month');\n                }\n            }\n            if (this.maxDate && this.linkedCalendars && !this.singleDatePicker && this.rightCalendar.month > this.maxDate) {\n              this.rightCalendar.month = this.maxDate.clone().date(2);\n              this.leftCalendar.month = this.maxDate.clone().date(2).subtract(1, 'month');\n            }\n        },\n\n        updateCalendars: function() {\n\n            if (this.timePicker) {\n                var hour, minute, second;\n                if (this.endDate) {\n                    hour = parseInt(this.container.find('.left .hourselect').val(), 10);\n                    minute = parseInt(this.container.find('.left .minuteselect').val(), 10);\n                    second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0;\n                    if (!this.timePicker24Hour) {\n                        var ampm = this.container.find('.left .ampmselect').val();\n                        if (ampm === 'PM' && hour < 12)\n                            hour += 12;\n                        if (ampm === 'AM' && hour === 12)\n                            hour = 0;\n                    }\n                } else {\n                    hour = parseInt(this.container.find('.right .hourselect').val(), 10);\n                    minute = parseInt(this.container.find('.right .minuteselect').val(), 10);\n                    second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0;\n                    if (!this.timePicker24Hour) {\n                        var ampm = this.container.find('.right .ampmselect').val();\n                        if (ampm === 'PM' && hour < 12)\n                            hour += 12;\n                        if (ampm === 'AM' && hour === 12)\n                            hour = 0;\n                    }\n                }\n                this.leftCalendar.month.hour(hour).minute(minute).second(second);\n                this.rightCalendar.month.hour(hour).minute(minute).second(second);\n            }\n\n            this.renderCalendar('left');\n            this.renderCalendar('right');\n\n            //highlight any predefined range matching the current start and end dates\n            this.container.find('.ranges li').removeClass('active');\n            if (this.endDate == null) return;\n\n            this.calculateChosenLabel();\n        },\n\n        renderCalendar: function(side) {\n\n            //\n            // Build the matrix of dates that will populate the calendar\n            //\n\n            var calendar = side == 'left' ? this.leftCalendar : this.rightCalendar;\n            var month = calendar.month.month();\n            var year = calendar.month.year();\n            var hour = calendar.month.hour();\n            var minute = calendar.month.minute();\n            var second = calendar.month.second();\n            var daysInMonth = moment([year, month]).daysInMonth();\n            var firstDay = moment([year, month, 1]);\n            var lastDay = moment([year, month, daysInMonth]);\n            var lastMonth = moment(firstDay).subtract(1, 'month').month();\n            var lastYear = moment(firstDay).subtract(1, 'month').year();\n            var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth();\n            var dayOfWeek = firstDay.day();\n\n            //initialize a 6 rows x 7 columns array for the calendar\n            var calendar = [];\n            calendar.firstDay = firstDay;\n            calendar.lastDay = lastDay;\n\n            for (var i = 0; i < 6; i++) {\n                calendar[i] = [];\n            }\n\n            //populate the calendar with date objects\n            var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1;\n            if (startDay > daysInLastMonth)\n                startDay -= 7;\n\n            if (dayOfWeek == this.locale.firstDay)\n                startDay = daysInLastMonth - 6;\n\n            var curDate = moment([lastYear, lastMonth, startDay, 12, minute, second]);\n\n            var col, row;\n            for (var i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add(24, 'hour')) {\n                if (i > 0 && col % 7 === 0) {\n                    col = 0;\n                    row++;\n                }\n                calendar[row][col] = curDate.clone().hour(hour).minute(minute).second(second);\n                curDate.hour(12);\n\n                if (this.minDate && calendar[row][col].format('YYYY-MM-DD') == this.minDate.format('YYYY-MM-DD') && calendar[row][col].isBefore(this.minDate) && side == 'left') {\n                    calendar[row][col] = this.minDate.clone();\n                }\n\n                if (this.maxDate && calendar[row][col].format('YYYY-MM-DD') == this.maxDate.format('YYYY-MM-DD') && calendar[row][col].isAfter(this.maxDate) && side == 'right') {\n                    calendar[row][col] = this.maxDate.clone();\n                }\n\n            }\n\n            //make the calendar object available to hoverDate/clickDate\n            if (side == 'left') {\n                this.leftCalendar.calendar = calendar;\n            } else {\n                this.rightCalendar.calendar = calendar;\n            }\n\n            //\n            // Display the calendar\n            //\n\n            var minDate = side == 'left' ? this.minDate : this.startDate;\n            var maxDate = this.maxDate;\n            var selected = side == 'left' ? this.startDate : this.endDate;\n            var arrow = this.locale.direction == 'ltr' ? {left: 'chevron-left', right: 'chevron-right'} : {left: 'chevron-right', right: 'chevron-left'};\n\n            var html = '<table class=\"table-condensed\">';\n            html += '<thead>';\n            html += '<tr>';\n\n            // add empty cell for week number\n            if (this.showWeekNumbers || this.showISOWeekNumbers)\n                html += '<th></th>';\n\n            if ((!minDate || minDate.isBefore(calendar.firstDay)) && (!this.linkedCalendars || side == 'left')) {\n                html += '<th class=\"prev available\"><i class=\"fa fa-' + arrow.left + ' glyphicon glyphicon-' + arrow.left + '\"></i></th>';\n            } else {\n                html += '<th></th>';\n            }\n\n            var dateHtml = this.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(\" YYYY\");\n\n            if (this.showDropdowns) {\n                var currentMonth = calendar[1][1].month();\n                var currentYear = calendar[1][1].year();\n                var maxYear = (maxDate && maxDate.year()) || (currentYear + 5);\n                var minYear = (minDate && minDate.year()) || (currentYear - 50);\n                var inMinYear = currentYear == minYear;\n                var inMaxYear = currentYear == maxYear;\n\n                var monthHtml = '<select class=\"monthselect\">';\n                for (var m = 0; m < 12; m++) {\n                    if ((!inMinYear || m >= minDate.month()) && (!inMaxYear || m <= maxDate.month())) {\n                        monthHtml += \"<option value='\" + m + \"'\" +\n                            (m === currentMonth ? \" selected='selected'\" : \"\") +\n                            \">\" + this.locale.monthNames[m] + \"</option>\";\n                    } else {\n                        monthHtml += \"<option value='\" + m + \"'\" +\n                            (m === currentMonth ? \" selected='selected'\" : \"\") +\n                            \" disabled='disabled'>\" + this.locale.monthNames[m] + \"</option>\";\n                    }\n                }\n                monthHtml += \"</select>\";\n\n                var yearHtml = '<select class=\"yearselect\">';\n                for (var y = minYear; y <= maxYear; y++) {\n                    yearHtml += '<option value=\"' + y + '\"' +\n                        (y === currentYear ? ' selected=\"selected\"' : '') +\n                        '>' + y + '</option>';\n                }\n                yearHtml += '</select>';\n\n                dateHtml = monthHtml + yearHtml;\n            }\n\n            html += '<th colspan=\"5\" class=\"month\">' + dateHtml + '</th>';\n            if ((!maxDate || maxDate.isAfter(calendar.lastDay)) && (!this.linkedCalendars || side == 'right' || this.singleDatePicker)) {\n                html += '<th class=\"next available\"><i class=\"fa fa-' + arrow.right + ' glyphicon glyphicon-' + arrow.right + '\"></i></th>';\n            } else {\n                html += '<th></th>';\n            }\n\n            html += '</tr>';\n            html += '<tr>';\n\n            // add week number label\n            if (this.showWeekNumbers || this.showISOWeekNumbers)\n                html += '<th class=\"week\">' + this.locale.weekLabel + '</th>';\n\n            $.each(this.locale.daysOfWeek, function(index, dayOfWeek) {\n                html += '<th>' + dayOfWeek + '</th>';\n            });\n\n            html += '</tr>';\n            html += '</thead>';\n            html += '<tbody>';\n\n            //adjust maxDate to reflect the dateLimit setting in order to\n            //grey out end dates beyond the dateLimit\n            if (this.endDate == null && this.dateLimit) {\n                var maxLimit = this.startDate.clone().add(this.dateLimit).endOf('day');\n                if (!maxDate || maxLimit.isBefore(maxDate)) {\n                    maxDate = maxLimit;\n                }\n            }\n\n            for (var row = 0; row < 6; row++) {\n                html += '<tr>';\n\n                // add week number\n                if (this.showWeekNumbers)\n                    html += '<td class=\"week\">' + calendar[row][0].week() + '</td>';\n                else if (this.showISOWeekNumbers)\n                    html += '<td class=\"week\">' + calendar[row][0].isoWeek() + '</td>';\n\n                for (var col = 0; col < 7; col++) {\n\n                    var classes = [];\n\n                    //highlight today's date\n                    if (calendar[row][col].isSame(new Date(), \"day\"))\n                        classes.push('today');\n\n                    //highlight weekends\n                    if (calendar[row][col].isoWeekday() > 5)\n                        classes.push('weekend');\n\n                    //grey out the dates in other months displayed at beginning and end of this calendar\n                    if (calendar[row][col].month() != calendar[1][1].month())\n                        classes.push('off');\n\n                    //don't allow selection of dates before the minimum date\n                    if (this.minDate && calendar[row][col].isBefore(this.minDate, 'day'))\n                        classes.push('off', 'disabled');\n\n                    //don't allow selection of dates after the maximum date\n                    if (maxDate && calendar[row][col].isAfter(maxDate, 'day'))\n                        classes.push('off', 'disabled');\n\n                    //don't allow selection of date if a custom function decides it's invalid\n                    if (this.isInvalidDate(calendar[row][col]))\n                        classes.push('off', 'disabled');\n\n                    //highlight the currently selected start date\n                    if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD'))\n                        classes.push('active', 'start-date');\n\n                    //highlight the currently selected end date\n                    if (this.endDate != null && calendar[row][col].format('YYYY-MM-DD') == this.endDate.format('YYYY-MM-DD'))\n                        classes.push('active', 'end-date');\n\n                    //highlight dates in-between the selected dates\n                    if (this.endDate != null && calendar[row][col] > this.startDate && calendar[row][col] < this.endDate)\n                        classes.push('in-range');\n\n                    //apply custom classes for this date\n                    var isCustom = this.isCustomDate(calendar[row][col]);\n                    if (isCustom !== false) {\n                        if (typeof isCustom === 'string')\n                            classes.push(isCustom);\n                        else\n                            Array.prototype.push.apply(classes, isCustom);\n                    }\n\n                    var cname = '', disabled = false;\n                    for (var i = 0; i < classes.length; i++) {\n                        cname += classes[i] + ' ';\n                        if (classes[i] == 'disabled')\n                            disabled = true;\n                    }\n                    if (!disabled)\n                        cname += 'available';\n\n                    html += '<td class=\"' + cname.replace(/^\\s+|\\s+$/g, '') + '\" data-title=\"' + 'r' + row + 'c' + col + '\">' + calendar[row][col].date() + '</td>';\n\n                }\n                html += '</tr>';\n            }\n\n            html += '</tbody>';\n            html += '</table>';\n\n            this.container.find('.calendar.' + side + ' .calendar-table').html(html);\n\n        },\n\n        renderTimePicker: function(side) {\n\n            // Don't bother updating the time picker if it's currently disabled\n            // because an end date hasn't been clicked yet\n            if (side == 'right' && !this.endDate) return;\n\n            var html, selected, minDate, maxDate = this.maxDate;\n\n            if (this.dateLimit && (!this.maxDate || this.startDate.clone().add(this.dateLimit).isAfter(this.maxDate)))\n                maxDate = this.startDate.clone().add(this.dateLimit);\n\n            if (side == 'left') {\n                selected = this.startDate.clone();\n                minDate = this.minDate;\n            } else if (side == 'right') {\n                selected = this.endDate.clone();\n                minDate = this.startDate;\n\n                //Preserve the time already selected\n                var timeSelector = this.container.find('.calendar.right .calendar-time div');\n                if (timeSelector.html() != '') {\n\n                    selected.hour(timeSelector.find('.hourselect option:selected').val() || selected.hour());\n                    selected.minute(timeSelector.find('.minuteselect option:selected').val() || selected.minute());\n                    selected.second(timeSelector.find('.secondselect option:selected').val() || selected.second());\n\n                    if (!this.timePicker24Hour) {\n                        var ampm = timeSelector.find('.ampmselect option:selected').val();\n                        if (ampm === 'PM' && selected.hour() < 12)\n                            selected.hour(selected.hour() + 12);\n                        if (ampm === 'AM' && selected.hour() === 12)\n                            selected.hour(0);\n                    }\n\n                }\n\n                if (selected.isBefore(this.startDate))\n                    selected = this.startDate.clone();\n\n                if (maxDate && selected.isAfter(maxDate))\n                    selected = maxDate.clone();\n\n            }\n\n            //\n            // hours\n            //\n\n            html = '<select class=\"hourselect\">';\n\n            var start = this.timePicker24Hour ? 0 : 1;\n            var end = this.timePicker24Hour ? 23 : 12;\n\n            for (var i = start; i <= end; i++) {\n                var i_in_24 = i;\n                if (!this.timePicker24Hour)\n                    i_in_24 = selected.hour() >= 12 ? (i == 12 ? 12 : i + 12) : (i == 12 ? 0 : i);\n\n                var time = selected.clone().hour(i_in_24);\n                var disabled = false;\n                if (minDate && time.minute(59).isBefore(minDate))\n                    disabled = true;\n                if (maxDate && time.minute(0).isAfter(maxDate))\n                    disabled = true;\n\n                if (i_in_24 == selected.hour() && !disabled) {\n                    html += '<option value=\"' + i + '\" selected=\"selected\">' + i + '</option>';\n                } else if (disabled) {\n                    html += '<option value=\"' + i + '\" disabled=\"disabled\" class=\"disabled\">' + i + '</option>';\n                } else {\n                    html += '<option value=\"' + i + '\">' + i + '</option>';\n                }\n            }\n\n            html += '</select> ';\n\n            //\n            // minutes\n            //\n\n            html += ': <select class=\"minuteselect\">';\n\n            for (var i = 0; i < 60; i += this.timePickerIncrement) {\n                var padded = i < 10 ? '0' + i : i;\n                var time = selected.clone().minute(i);\n\n                var disabled = false;\n                if (minDate && time.second(59).isBefore(minDate))\n                    disabled = true;\n                if (maxDate && time.second(0).isAfter(maxDate))\n                    disabled = true;\n\n                if (selected.minute() == i && !disabled) {\n                    html += '<option value=\"' + i + '\" selected=\"selected\">' + padded + '</option>';\n                } else if (disabled) {\n                    html += '<option value=\"' + i + '\" disabled=\"disabled\" class=\"disabled\">' + padded + '</option>';\n                } else {\n                    html += '<option value=\"' + i + '\">' + padded + '</option>';\n                }\n            }\n\n            html += '</select> ';\n\n            //\n            // seconds\n            //\n\n            if (this.timePickerSeconds) {\n                html += ': <select class=\"secondselect\">';\n\n                for (var i = 0; i < 60; i++) {\n                    var padded = i < 10 ? '0' + i : i;\n                    var time = selected.clone().second(i);\n\n                    var disabled = false;\n                    if (minDate && time.isBefore(minDate))\n                        disabled = true;\n                    if (maxDate && time.isAfter(maxDate))\n                        disabled = true;\n\n                    if (selected.second() == i && !disabled) {\n                        html += '<option value=\"' + i + '\" selected=\"selected\">' + padded + '</option>';\n                    } else if (disabled) {\n                        html += '<option value=\"' + i + '\" disabled=\"disabled\" class=\"disabled\">' + padded + '</option>';\n                    } else {\n                        html += '<option value=\"' + i + '\">' + padded + '</option>';\n                    }\n                }\n\n                html += '</select> ';\n            }\n\n            //\n            // AM/PM\n            //\n\n            if (!this.timePicker24Hour) {\n                html += '<select class=\"ampmselect\">';\n\n                var am_html = '';\n                var pm_html = '';\n\n                if (minDate && selected.clone().hour(12).minute(0).second(0).isBefore(minDate))\n                    am_html = ' disabled=\"disabled\" class=\"disabled\"';\n\n                if (maxDate && selected.clone().hour(0).minute(0).second(0).isAfter(maxDate))\n                    pm_html = ' disabled=\"disabled\" class=\"disabled\"';\n\n                if (selected.hour() >= 12) {\n                    html += '<option value=\"AM\"' + am_html + '>AM</option><option value=\"PM\" selected=\"selected\"' + pm_html + '>PM</option>';\n                } else {\n                    html += '<option value=\"AM\" selected=\"selected\"' + am_html + '>AM</option><option value=\"PM\"' + pm_html + '>PM</option>';\n                }\n\n                html += '</select>';\n            }\n\n            this.container.find('.calendar.' + side + ' .calendar-time div').html(html);\n\n        },\n\n        updateFormInputs: function() {\n\n            //ignore mouse movements while an above-calendar text input has focus\n            if (this.container.find('input[name=daterangepicker_start]').is(\":focus\") || this.container.find('input[name=daterangepicker_end]').is(\":focus\"))\n                return;\n\n            this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.locale.format));\n            if (this.endDate)\n                this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.locale.format));\n\n            if (this.singleDatePicker || (this.endDate && (this.startDate.isBefore(this.endDate) || this.startDate.isSame(this.endDate)))) {\n                this.container.find('button.applyBtn').removeAttr('disabled');\n            } else {\n                this.container.find('button.applyBtn').attr('disabled', 'disabled');\n            }\n\n        },\n\n        move: function() {\n            var parentOffset = { top: 0, left: 0 },\n                containerTop;\n            var parentRightEdge = $(window).width();\n            if (!this.parentEl.is('body')) {\n                parentOffset = {\n                    top: this.parentEl.offset().top - this.parentEl.scrollTop(),\n                    left: this.parentEl.offset().left - this.parentEl.scrollLeft()\n                };\n                parentRightEdge = this.parentEl[0].clientWidth + this.parentEl.offset().left;\n            }\n\n            if (this.drops == 'up')\n                containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top;\n            else\n                containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top;\n            this.container[this.drops == 'up' ? 'addClass' : 'removeClass']('dropup');\n\n            if (this.opens == 'left') {\n                this.container.css({\n                    top: containerTop,\n                    right: parentRightEdge - this.element.offset().left - this.element.outerWidth(),\n                    left: 'auto'\n                });\n                if (this.container.offset().left < 0) {\n                    this.container.css({\n                        right: 'auto',\n                        left: 9\n                    });\n                }\n            } else if (this.opens == 'center') {\n                this.container.css({\n                    top: containerTop,\n                    left: this.element.offset().left - parentOffset.left + this.element.outerWidth() / 2\n                            - this.container.outerWidth() / 2,\n                    right: 'auto'\n                });\n                if (this.container.offset().left < 0) {\n                    this.container.css({\n                        right: 'auto',\n                        left: 9\n                    });\n                }\n            } else {\n                this.container.css({\n                    top: containerTop,\n                    left: this.element.offset().left - parentOffset.left,\n                    right: 'auto'\n                });\n                if (this.container.offset().left + this.container.outerWidth() > $(window).width()) {\n                    this.container.css({\n                        left: 'auto',\n                        right: 0\n                    });\n                }\n            }\n        },\n\n        show: function(e) {\n            if (this.isShowing) return;\n\n            // Create a click proxy that is private to this instance of datepicker, for unbinding\n            this._outsideClickProxy = $.proxy(function(e) { this.outsideClick(e); }, this);\n\n            // Bind global datepicker mousedown for hiding and\n            $(document)\n              .on('mousedown.daterangepicker', this._outsideClickProxy)\n              // also support mobile devices\n              .on('touchend.daterangepicker', this._outsideClickProxy)\n              // also explicitly play nice with Bootstrap dropdowns, which stopPropagation when clicking them\n              .on('click.daterangepicker', '[data-toggle=dropdown]', this._outsideClickProxy)\n              // and also close when focus changes to outside the picker (eg. tabbing between controls)\n              .on('focusin.daterangepicker', this._outsideClickProxy);\n\n            // Reposition the picker if the window is resized while it's open\n            $(window).on('resize.daterangepicker', $.proxy(function(e) { this.move(e); }, this));\n\n            this.oldStartDate = this.startDate.clone();\n            this.oldEndDate = this.endDate.clone();\n            this.previousRightTime = this.endDate.clone();\n\n            this.updateView();\n            this.container.show();\n            this.move();\n            this.element.trigger('show.daterangepicker', this);\n            this.isShowing = true;\n        },\n\n        hide: function(e) {\n            if (!this.isShowing) return;\n\n            //incomplete date selection, revert to last values\n            if (!this.endDate) {\n                this.startDate = this.oldStartDate.clone();\n                this.endDate = this.oldEndDate.clone();\n            }\n\n            //if a new date range was selected, invoke the user callback function\n            if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))\n                this.callback(this.startDate, this.endDate, this.chosenLabel);\n\n            //if picker is attached to a text input, update it\n            this.updateElement();\n\n            $(document).off('.daterangepicker');\n            $(window).off('.daterangepicker');\n            this.container.hide();\n            this.element.trigger('hide.daterangepicker', this);\n            this.isShowing = false;\n        },\n\n        toggle: function(e) {\n            if (this.isShowing) {\n                this.hide();\n            } else {\n                this.show();\n            }\n        },\n\n        outsideClick: function(e) {\n            var target = $(e.target);\n            // if the page is clicked anywhere except within the daterangerpicker/button\n            // itself then call this.hide()\n            if (\n                // ie modal dialog fix\n                e.type == \"focusin\" ||\n                target.closest(this.element).length ||\n                target.closest(this.container).length ||\n                target.closest('.calendar-table').length\n                ) return;\n            this.hide();\n            this.element.trigger('outsideClick.daterangepicker', this);\n        },\n\n        showCalendars: function() {\n            this.container.addClass('show-calendar');\n            this.move();\n            this.element.trigger('showCalendar.daterangepicker', this);\n        },\n\n        hideCalendars: function() {\n            this.container.removeClass('show-calendar');\n            this.element.trigger('hideCalendar.daterangepicker', this);\n        },\n\n        hoverRange: function(e) {\n\n            //ignore mouse movements while an above-calendar text input has focus\n            if (this.container.find('input[name=daterangepicker_start]').is(\":focus\") || this.container.find('input[name=daterangepicker_end]').is(\":focus\"))\n                return;\n\n            var label = e.target.getAttribute('data-range-key');\n\n            if (label == this.locale.customRangeLabel) {\n                this.updateView();\n            } else {\n                var dates = this.ranges[label];\n                this.container.find('input[name=daterangepicker_start]').val(dates[0].format(this.locale.format));\n                this.container.find('input[name=daterangepicker_end]').val(dates[1].format(this.locale.format));\n            }\n\n        },\n\n        clickRange: function(e) {\n            var label = e.target.getAttribute('data-range-key');\n            this.chosenLabel = label;\n            if (label == this.locale.customRangeLabel) {\n                this.showCalendars();\n            } else {\n                var dates = this.ranges[label];\n                this.startDate = dates[0];\n                this.endDate = dates[1];\n\n                if (!this.timePicker) {\n                    this.startDate.startOf('day');\n                    this.endDate.endOf('day');\n                }\n\n                if (!this.alwaysShowCalendars)\n                    this.hideCalendars();\n                this.clickApply();\n            }\n        },\n\n        clickPrev: function(e) {\n            var cal = $(e.target).parents('.calendar');\n            if (cal.hasClass('left')) {\n                this.leftCalendar.month.subtract(1, 'month');\n                if (this.linkedCalendars)\n                    this.rightCalendar.month.subtract(1, 'month');\n            } else {\n                this.rightCalendar.month.subtract(1, 'month');\n            }\n            this.updateCalendars();\n        },\n\n        clickNext: function(e) {\n            var cal = $(e.target).parents('.calendar');\n            if (cal.hasClass('left')) {\n                this.leftCalendar.month.add(1, 'month');\n            } else {\n                this.rightCalendar.month.add(1, 'month');\n                if (this.linkedCalendars)\n                    this.leftCalendar.month.add(1, 'month');\n            }\n            this.updateCalendars();\n        },\n\n        hoverDate: function(e) {\n\n            //ignore mouse movements while an above-calendar text input has focus\n            //if (this.container.find('input[name=daterangepicker_start]').is(\":focus\") || this.container.find('input[name=daterangepicker_end]').is(\":focus\"))\n            //    return;\n\n            //ignore dates that can't be selected\n            if (!$(e.target).hasClass('available')) return;\n\n            //have the text inputs above calendars reflect the date being hovered over\n            var title = $(e.target).attr('data-title');\n            var row = title.substr(1, 1);\n            var col = title.substr(3, 1);\n            var cal = $(e.target).parents('.calendar');\n            var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col];\n\n            if (this.endDate && !this.container.find('input[name=daterangepicker_start]').is(\":focus\")) {\n                this.container.find('input[name=daterangepicker_start]').val(date.format(this.locale.format));\n            } else if (!this.endDate && !this.container.find('input[name=daterangepicker_end]').is(\":focus\")) {\n                this.container.find('input[name=daterangepicker_end]').val(date.format(this.locale.format));\n            }\n\n            //highlight the dates between the start date and the date being hovered as a potential end date\n            var leftCalendar = this.leftCalendar;\n            var rightCalendar = this.rightCalendar;\n            var startDate = this.startDate;\n            if (!this.endDate) {\n                this.container.find('.calendar tbody td').each(function(index, el) {\n\n                    //skip week numbers, only look at dates\n                    if ($(el).hasClass('week')) return;\n\n                    var title = $(el).attr('data-title');\n                    var row = title.substr(1, 1);\n                    var col = title.substr(3, 1);\n                    var cal = $(el).parents('.calendar');\n                    var dt = cal.hasClass('left') ? leftCalendar.calendar[row][col] : rightCalendar.calendar[row][col];\n\n                    if ((dt.isAfter(startDate) && dt.isBefore(date)) || dt.isSame(date, 'day')) {\n                        $(el).addClass('in-range');\n                    } else {\n                        $(el).removeClass('in-range');\n                    }\n\n                });\n            }\n\n        },\n\n        clickDate: function(e) {\n\n            if (!$(e.target).hasClass('available')) return;\n\n            var title = $(e.target).attr('data-title');\n            var row = title.substr(1, 1);\n            var col = title.substr(3, 1);\n            var cal = $(e.target).parents('.calendar');\n            var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col];\n\n            //\n            // this function needs to do a few things:\n            // * alternate between selecting a start and end date for the range,\n            // * if the time picker is enabled, apply the hour/minute/second from the select boxes to the clicked date\n            // * if autoapply is enabled, and an end date was chosen, apply the selection\n            // * if single date picker mode, and time picker isn't enabled, apply the selection immediately\n            // * if one of the inputs above the calendars was focused, cancel that manual input\n            //\n\n            if (this.endDate || date.isBefore(this.startDate, 'day')) { //picking start\n                if (this.timePicker) {\n                    var hour = parseInt(this.container.find('.left .hourselect').val(), 10);\n                    if (!this.timePicker24Hour) {\n                        var ampm = this.container.find('.left .ampmselect').val();\n                        if (ampm === 'PM' && hour < 12)\n                            hour += 12;\n                        if (ampm === 'AM' && hour === 12)\n                            hour = 0;\n                    }\n                    var minute = parseInt(this.container.find('.left .minuteselect').val(), 10);\n                    var second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0;\n                    date = date.clone().hour(hour).minute(minute).second(second);\n                }\n                this.endDate = null;\n                this.setStartDate(date.clone());\n            } else if (!this.endDate && date.isBefore(this.startDate)) {\n                //special case: clicking the same date for start/end,\n                //but the time of the end date is before the start date\n                this.setEndDate(this.startDate.clone());\n            } else { // picking end\n                if (this.timePicker) {\n                    var hour = parseInt(this.container.find('.right .hourselect').val(), 10);\n                    if (!this.timePicker24Hour) {\n                        var ampm = this.container.find('.right .ampmselect').val();\n                        if (ampm === 'PM' && hour < 12)\n                            hour += 12;\n                        if (ampm === 'AM' && hour === 12)\n                            hour = 0;\n                    }\n                    var minute = parseInt(this.container.find('.right .minuteselect').val(), 10);\n                    var second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0;\n                    date = date.clone().hour(hour).minute(minute).second(second);\n                }\n                this.setEndDate(date.clone());\n                if (this.autoApply) {\n                  this.calculateChosenLabel();\n                  this.clickApply();\n                }\n            }\n\n            if (this.singleDatePicker) {\n                this.setEndDate(this.startDate);\n                if (!this.timePicker)\n                    this.clickApply();\n            }\n\n            this.updateView();\n\n            //This is to cancel the blur event handler if the mouse was in one of the inputs\n            e.stopPropagation();\n\n        },\n\n        calculateChosenLabel: function () {\n            var customRange = true;\n            var i = 0;\n            for (var range in this.ranges) {\n              if (this.timePicker) {\n                    var format = this.timePickerSeconds ? \"YYYY-MM-DD hh:mm:ss\" : \"YYYY-MM-DD hh:mm\";\n                    //ignore times when comparing dates if time picker seconds is not enabled\n                    if (this.startDate.format(format) == this.ranges[range][0].format(format) && this.endDate.format(format) == this.ranges[range][1].format(format)) {\n                        customRange = false;\n                        this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html();\n                        break;\n                    }\n                } else {\n                    //ignore times when comparing dates if time picker is not enabled\n                    if (this.startDate.format('YYYY-MM-DD') == this.ranges[range][0].format('YYYY-MM-DD') && this.endDate.format('YYYY-MM-DD') == this.ranges[range][1].format('YYYY-MM-DD')) {\n                        customRange = false;\n                        this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html();\n                        break;\n                    }\n                }\n                i++;\n            }\n            if (customRange) {\n                if (this.showCustomRangeLabel) {\n                    this.chosenLabel = this.container.find('.ranges li:last').addClass('active').html();\n                } else {\n                    this.chosenLabel = null;\n                }\n                this.showCalendars();\n            }\n        },\n\n        clickApply: function(e) {\n            this.hide();\n            this.element.trigger('apply.daterangepicker', this);\n        },\n\n        clickCancel: function(e) {\n            this.startDate = this.oldStartDate;\n            this.endDate = this.oldEndDate;\n            this.hide();\n            this.element.trigger('cancel.daterangepicker', this);\n        },\n\n        monthOrYearChanged: function(e) {\n            var isLeft = $(e.target).closest('.calendar').hasClass('left'),\n                leftOrRight = isLeft ? 'left' : 'right',\n                cal = this.container.find('.calendar.'+leftOrRight);\n\n            // Month must be Number for new moment versions\n            var month = parseInt(cal.find('.monthselect').val(), 10);\n            var year = cal.find('.yearselect').val();\n\n            if (!isLeft) {\n                if (year < this.startDate.year() || (year == this.startDate.year() && month < this.startDate.month())) {\n                    month = this.startDate.month();\n                    year = this.startDate.year();\n                }\n            }\n\n            if (this.minDate) {\n                if (year < this.minDate.year() || (year == this.minDate.year() && month < this.minDate.month())) {\n                    month = this.minDate.month();\n                    year = this.minDate.year();\n                }\n            }\n\n            if (this.maxDate) {\n                if (year > this.maxDate.year() || (year == this.maxDate.year() && month > this.maxDate.month())) {\n                    month = this.maxDate.month();\n                    year = this.maxDate.year();\n                }\n            }\n\n            if (isLeft) {\n                this.leftCalendar.month.month(month).year(year);\n                if (this.linkedCalendars)\n                    this.rightCalendar.month = this.leftCalendar.month.clone().add(1, 'month');\n            } else {\n                this.rightCalendar.month.month(month).year(year);\n                if (this.linkedCalendars)\n                    this.leftCalendar.month = this.rightCalendar.month.clone().subtract(1, 'month');\n            }\n            this.updateCalendars();\n        },\n\n        timeChanged: function(e) {\n\n            var cal = $(e.target).closest('.calendar'),\n                isLeft = cal.hasClass('left');\n\n            var hour = parseInt(cal.find('.hourselect').val(), 10);\n            var minute = parseInt(cal.find('.minuteselect').val(), 10);\n            var second = this.timePickerSeconds ? parseInt(cal.find('.secondselect').val(), 10) : 0;\n\n            if (!this.timePicker24Hour) {\n                var ampm = cal.find('.ampmselect').val();\n                if (ampm === 'PM' && hour < 12)\n                    hour += 12;\n                if (ampm === 'AM' && hour === 12)\n                    hour = 0;\n            }\n\n            if (isLeft) {\n                var start = this.startDate.clone();\n                start.hour(hour);\n                start.minute(minute);\n                start.second(second);\n                this.setStartDate(start);\n                if (this.singleDatePicker) {\n                    this.endDate = this.startDate.clone();\n                } else if (this.endDate && this.endDate.format('YYYY-MM-DD') == start.format('YYYY-MM-DD') && this.endDate.isBefore(start)) {\n                    this.setEndDate(start.clone());\n                }\n            } else if (this.endDate) {\n                var end = this.endDate.clone();\n                end.hour(hour);\n                end.minute(minute);\n                end.second(second);\n                this.setEndDate(end);\n            }\n\n            //update the calendars so all clickable dates reflect the new time component\n            this.updateCalendars();\n\n            //update the form inputs above the calendars with the new time\n            this.updateFormInputs();\n\n            //re-render the time pickers because changing one selection can affect what's enabled in another\n            this.renderTimePicker('left');\n            this.renderTimePicker('right');\n\n        },\n\n        formInputsChanged: function(e) {\n            var isRight = $(e.target).closest('.calendar').hasClass('right');\n            var start = moment(this.container.find('input[name=\"daterangepicker_start\"]').val(), this.locale.format);\n            var end = moment(this.container.find('input[name=\"daterangepicker_end\"]').val(), this.locale.format);\n\n            if (start.isValid() && end.isValid()) {\n\n                if (isRight && end.isBefore(start))\n                    start = end.clone();\n\n                this.setStartDate(start);\n                this.setEndDate(end);\n\n                if (isRight) {\n                    this.container.find('input[name=\"daterangepicker_start\"]').val(this.startDate.format(this.locale.format));\n                } else {\n                    this.container.find('input[name=\"daterangepicker_end\"]').val(this.endDate.format(this.locale.format));\n                }\n\n            }\n\n            this.updateView();\n        },\n\n        formInputsFocused: function(e) {\n\n            // Highlight the focused input\n            this.container.find('input[name=\"daterangepicker_start\"], input[name=\"daterangepicker_end\"]').removeClass('active');\n            $(e.target).addClass('active');\n\n            // Set the state such that if the user goes back to using a mouse, \n            // the calendars are aware we're selecting the end of the range, not\n            // the start. This allows someone to edit the end of a date range without\n            // re-selecting the beginning, by clicking on the end date input then\n            // using the calendar.\n            var isRight = $(e.target).closest('.calendar').hasClass('right');\n            if (isRight) {\n                this.endDate = null;\n                this.setStartDate(this.startDate.clone());\n                this.updateView();\n            }\n\n        },\n\n        formInputsBlurred: function(e) {\n\n            // this function has one purpose right now: if you tab from the first\n            // text input to the second in the UI, the endDate is nulled so that\n            // you can click another, but if you tab out without clicking anything\n            // or changing the input value, the old endDate should be retained\n\n            if (!this.endDate) {\n                var val = this.container.find('input[name=\"daterangepicker_end\"]').val();\n                var end = moment(val, this.locale.format);\n                if (end.isValid()) {\n                    this.setEndDate(end);\n                    this.updateView();\n                }\n            }\n\n        },\n\n        formInputsKeydown: function(e) {\n            // This function ensures that if the 'enter' key was pressed in the input, then the calendars\n            // are updated with the startDate and endDate.\n            // This behaviour is automatic in Chrome/Firefox/Edge but not in IE 11 hence why this exists.\n            // Other browsers and versions of IE are untested and the behaviour is unknown.\n            if (e.keyCode === 13) {\n                // Prevent the calendar from being updated twice on Chrome/Firefox/Edge\n                e.preventDefault(); \n                this.formInputsChanged(e);\n            }\n        },\n\n\n        elementChanged: function() {\n            if (!this.element.is('input')) return;\n            if (!this.element.val().length) return;\n\n            var dateString = this.element.val().split(this.locale.separator),\n                start = null,\n                end = null;\n\n            if (dateString.length === 2) {\n                start = moment(dateString[0], this.locale.format);\n                end = moment(dateString[1], this.locale.format);\n            }\n\n            if (this.singleDatePicker || start === null || end === null) {\n                start = moment(this.element.val(), this.locale.format);\n                end = start;\n            }\n\n            if (!start.isValid() || !end.isValid()) return;\n\n            this.setStartDate(start);\n            this.setEndDate(end);\n            this.updateView();\n        },\n\n        keydown: function(e) {\n            //hide on tab or enter\n            if ((e.keyCode === 9) || (e.keyCode === 13)) {\n                this.hide();\n            }\n\n            //hide on esc and prevent propagation\n            if (e.keyCode === 27) {\n                e.preventDefault();\n                e.stopPropagation();\n\n                this.hide();\n            }\n        },\n\n        updateElement: function() {\n            if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) {\n                this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));\n                this.element.trigger('change');\n            } else if (this.element.is('input') && this.autoUpdateInput) {\n                this.element.val(this.startDate.format(this.locale.format));\n                this.element.trigger('change');\n            }\n        },\n\n        remove: function() {\n            this.container.remove();\n            this.element.off('.daterangepicker');\n            this.element.removeData();\n        }\n\n    };\n\n    $.fn.daterangepicker = function(options, callback) {\n        var implementOptions = $.extend(true, {}, $.fn.daterangepicker.defaultOptions, options);\n        this.each(function() {\n            var el = $(this);\n            if (el.data('daterangepicker'))\n                el.data('daterangepicker').remove();\n            el.data('daterangepicker', new DateRangePicker(el, implementOptions, callback));\n        });\n        return this;\n    };\n\n    return DateRangePicker;\n\n}));\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/adminlte/bower_components/fastclick/fastclick.js",
    "content": ";(function () {\n\t'use strict';\n\n\t/**\n\t * @preserve FastClick: polyfill to remove click delays on browsers with touch UIs.\n\t *\n\t * @codingstandard ftlabs-jsv2\n\t * @copyright The Financial Times Limited [All Rights Reserved]\n\t * @license MIT License (see LICENSE.txt)\n\t */\n\n\t/*jslint browser:true, node:true*/\n\t/*global define, Event, Node*/\n\n\n\t/**\n\t * Instantiate fast-clicking listeners on the specified layer.\n\t *\n\t * @constructor\n\t * @param {Element} layer The layer to listen on\n\t * @param {Object} [options={}] The options to override the defaults\n\t */\n\tfunction FastClick(layer, options) {\n\t\tvar oldOnClick;\n\n\t\toptions = options || {};\n\n\t\t/**\n\t\t * Whether a click is currently being tracked.\n\t\t *\n\t\t * @type boolean\n\t\t */\n\t\tthis.trackingClick = false;\n\n\n\t\t/**\n\t\t * Timestamp for when click tracking started.\n\t\t *\n\t\t * @type number\n\t\t */\n\t\tthis.trackingClickStart = 0;\n\n\n\t\t/**\n\t\t * The element being tracked for a click.\n\t\t *\n\t\t * @type EventTarget\n\t\t */\n\t\tthis.targetElement = null;\n\n\n\t\t/**\n\t\t * X-coordinate of touch start event.\n\t\t *\n\t\t * @type number\n\t\t */\n\t\tthis.touchStartX = 0;\n\n\n\t\t/**\n\t\t * Y-coordinate of touch start event.\n\t\t *\n\t\t * @type number\n\t\t */\n\t\tthis.touchStartY = 0;\n\n\n\t\t/**\n\t\t * ID of the last touch, retrieved from Touch.identifier.\n\t\t *\n\t\t * @type number\n\t\t */\n\t\tthis.lastTouchIdentifier = 0;\n\n\n\t\t/**\n\t\t * Touchmove boundary, beyond which a click will be cancelled.\n\t\t *\n\t\t * @type number\n\t\t */\n\t\tthis.touchBoundary = options.touchBoundary || 10;\n\n\n\t\t/**\n\t\t * The FastClick layer.\n\t\t *\n\t\t * @type Element\n\t\t */\n\t\tthis.layer = layer;\n\n\t\t/**\n\t\t * The minimum time between tap(touchstart and touchend) events\n\t\t *\n\t\t * @type number\n\t\t */\n\t\tthis.tapDelay = options.tapDelay || 200;\n\n\t\t/**\n\t\t * The maximum time for a tap\n\t\t *\n\t\t * @type number\n\t\t */\n\t\tthis.tapTimeout = options.tapTimeout || 700;\n\n\t\tif (FastClick.notNeeded(layer)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Some old versions of Android don't have Function.prototype.bind\n\t\tfunction bind(method, context) {\n\t\t\treturn function() { return method.apply(context, arguments); };\n\t\t}\n\n\n\t\tvar methods = ['onMouse', 'onClick', 'onTouchStart', 'onTouchMove', 'onTouchEnd', 'onTouchCancel'];\n\t\tvar context = this;\n\t\tfor (var i = 0, l = methods.length; i < l; i++) {\n\t\t\tcontext[methods[i]] = bind(context[methods[i]], context);\n\t\t}\n\n\t\t// Set up event handlers as required\n\t\tif (deviceIsAndroid) {\n\t\t\tlayer.addEventListener('mouseover', this.onMouse, true);\n\t\t\tlayer.addEventListener('mousedown', this.onMouse, true);\n\t\t\tlayer.addEventListener('mouseup', this.onMouse, true);\n\t\t}\n\n\t\tlayer.addEventListener('click', this.onClick, true);\n\t\tlayer.addEventListener('touchstart', this.onTouchStart, false);\n\t\tlayer.addEventListener('touchmove', this.onTouchMove, false);\n\t\tlayer.addEventListener('touchend', this.onTouchEnd, false);\n\t\tlayer.addEventListener('touchcancel', this.onTouchCancel, false);\n\n\t\t// Hack is required for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)\n\t\t// which is how FastClick normally stops click events bubbling to callbacks registered on the FastClick\n\t\t// layer when they are cancelled.\n\t\tif (!Event.prototype.stopImmediatePropagation) {\n\t\t\tlayer.removeEventListener = function(type, callback, capture) {\n\t\t\t\tvar rmv = Node.prototype.removeEventListener;\n\t\t\t\tif (type === 'click') {\n\t\t\t\t\trmv.call(layer, type, callback.hijacked || callback, capture);\n\t\t\t\t} else {\n\t\t\t\t\trmv.call(layer, type, callback, capture);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tlayer.addEventListener = function(type, callback, capture) {\n\t\t\t\tvar adv = Node.prototype.addEventListener;\n\t\t\t\tif (type === 'click') {\n\t\t\t\t\tadv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {\n\t\t\t\t\t\tif (!event.propagationStopped) {\n\t\t\t\t\t\t\tcallback(event);\n\t\t\t\t\t\t}\n\t\t\t\t\t}), capture);\n\t\t\t\t} else {\n\t\t\t\t\tadv.call(layer, type, callback, capture);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\t// If a handler is already declared in the element's onclick attribute, it will be fired before\n\t\t// FastClick's onClick handler. Fix this by pulling out the user-defined handler function and\n\t\t// adding it as listener.\n\t\tif (typeof layer.onclick === 'function') {\n\n\t\t\t// Android browser on at least 3.2 requires a new reference to the function in layer.onclick\n\t\t\t// - the old one won't work if passed to addEventListener directly.\n\t\t\toldOnClick = layer.onclick;\n\t\t\tlayer.addEventListener('click', function(event) {\n\t\t\t\toldOnClick(event);\n\t\t\t}, false);\n\t\t\tlayer.onclick = null;\n\t\t}\n\t}\n\n\t/**\n\t* Windows Phone 8.1 fakes user agent string to look like Android and iPhone.\n\t*\n\t* @type boolean\n\t*/\n\tvar deviceIsWindowsPhone = navigator.userAgent.indexOf(\"Windows Phone\") >= 0;\n\n\t/**\n\t * Android requires exceptions.\n\t *\n\t * @type boolean\n\t */\n\tvar deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0 && !deviceIsWindowsPhone;\n\n\n\t/**\n\t * iOS requires exceptions.\n\t *\n\t * @type boolean\n\t */\n\tvar deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent) && !deviceIsWindowsPhone;\n\n\n\t/**\n\t * iOS 4 requires an exception for select elements.\n\t *\n\t * @type boolean\n\t */\n\tvar deviceIsIOS4 = deviceIsIOS && (/OS 4_\\d(_\\d)?/).test(navigator.userAgent);\n\n\n\t/**\n\t * iOS 6.0-7.* requires the target element to be manually derived\n\t *\n\t * @type boolean\n\t */\n\tvar deviceIsIOSWithBadTarget = deviceIsIOS && (/OS [6-7]_\\d/).test(navigator.userAgent);\n\n\t/**\n\t * BlackBerry requires exceptions.\n\t *\n\t * @type boolean\n\t */\n\tvar deviceIsBlackBerry10 = navigator.userAgent.indexOf('BB10') > 0;\n\n\t/**\n\t * Determine whether a given element requires a native click.\n\t *\n\t * @param {EventTarget|Element} target Target DOM element\n\t * @returns {boolean} Returns true if the element needs a native click\n\t */\n\tFastClick.prototype.needsClick = function(target) {\n\t\tswitch (target.nodeName.toLowerCase()) {\n\n\t\t// Don't send a synthetic click to disabled inputs (issue #62)\n\t\tcase 'button':\n\t\tcase 'select':\n\t\tcase 'textarea':\n\t\t\tif (target.disabled) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tbreak;\n\t\tcase 'input':\n\n\t\t\t// File inputs need real clicks on iOS 6 due to a browser bug (issue #68)\n\t\t\tif ((deviceIsIOS && target.type === 'file') || target.disabled) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tbreak;\n\t\tcase 'label':\n\t\tcase 'iframe': // iOS8 homescreen apps can prevent events bubbling into frames\n\t\tcase 'video':\n\t\t\treturn true;\n\t\t}\n\n\t\treturn (/\\bneedsclick\\b/).test(target.className);\n\t};\n\n\n\t/**\n\t * Determine whether a given element requires a call to focus to simulate click into element.\n\t *\n\t * @param {EventTarget|Element} target Target DOM element\n\t * @returns {boolean} Returns true if the element requires a call to focus to simulate native click.\n\t */\n\tFastClick.prototype.needsFocus = function(target) {\n\t\tswitch (target.nodeName.toLowerCase()) {\n\t\tcase 'textarea':\n\t\t\treturn true;\n\t\tcase 'select':\n\t\t\treturn !deviceIsAndroid;\n\t\tcase 'input':\n\t\t\tswitch (target.type) {\n\t\t\tcase 'button':\n\t\t\tcase 'checkbox':\n\t\t\tcase 'file':\n\t\t\tcase 'image':\n\t\t\tcase 'radio':\n\t\t\tcase 'submit':\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// No point in attempting to focus disabled inputs\n\t\t\treturn !target.disabled && !target.readOnly;\n\t\tdefault:\n\t\t\treturn (/\\bneedsfocus\\b/).test(target.className);\n\t\t}\n\t};\n\n\n\t/**\n\t * Send a click event to the specified element.\n\t *\n\t * @param {EventTarget|Element} targetElement\n\t * @param {Event} event\n\t */\n\tFastClick.prototype.sendClick = function(targetElement, event) {\n\t\tvar clickEvent, touch;\n\n\t\t// On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24)\n\t\tif (document.activeElement && document.activeElement !== targetElement) {\n\t\t\tdocument.activeElement.blur();\n\t\t}\n\n\t\ttouch = event.changedTouches[0];\n\n\t\t// Synthesise a click event, with an extra attribute so it can be tracked\n\t\tclickEvent = document.createEvent('MouseEvents');\n\t\tclickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);\n\t\tclickEvent.forwardedTouchEvent = true;\n\t\ttargetElement.dispatchEvent(clickEvent);\n\t};\n\n\tFastClick.prototype.determineEventType = function(targetElement) {\n\n\t\t//Issue #159: Android Chrome Select Box does not open with a synthetic click event\n\t\tif (deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') {\n\t\t\treturn 'mousedown';\n\t\t}\n\n\t\treturn 'click';\n\t};\n\n\n\t/**\n\t * @param {EventTarget|Element} targetElement\n\t */\n\tFastClick.prototype.focus = function(targetElement) {\n\t\tvar length;\n\n\t\t// Issue #160: on iOS 7, some input elements (e.g. date datetime month) throw a vague TypeError on setSelectionRange. These elements don't have an integer value for the selectionStart and selectionEnd properties, but unfortunately that can't be used for detection because accessing the properties also throws a TypeError. Just check the type instead. Filed as Apple bug #15122724.\n\t\tif (deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time' && targetElement.type !== 'month') {\n\t\t\tlength = targetElement.value.length;\n\t\t\ttargetElement.setSelectionRange(length, length);\n\t\t} else {\n\t\t\ttargetElement.focus();\n\t\t}\n\t};\n\n\n\t/**\n\t * Check whether the given target element is a child of a scrollable layer and if so, set a flag on it.\n\t *\n\t * @param {EventTarget|Element} targetElement\n\t */\n\tFastClick.prototype.updateScrollParent = function(targetElement) {\n\t\tvar scrollParent, parentElement;\n\n\t\tscrollParent = targetElement.fastClickScrollParent;\n\n\t\t// Attempt to discover whether the target element is contained within a scrollable layer. Re-check if the\n\t\t// target element was moved to another parent.\n\t\tif (!scrollParent || !scrollParent.contains(targetElement)) {\n\t\t\tparentElement = targetElement;\n\t\t\tdo {\n\t\t\t\tif (parentElement.scrollHeight > parentElement.offsetHeight) {\n\t\t\t\t\tscrollParent = parentElement;\n\t\t\t\t\ttargetElement.fastClickScrollParent = parentElement;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tparentElement = parentElement.parentElement;\n\t\t\t} while (parentElement);\n\t\t}\n\n\t\t// Always update the scroll top tracker if possible.\n\t\tif (scrollParent) {\n\t\t\tscrollParent.fastClickLastScrollTop = scrollParent.scrollTop;\n\t\t}\n\t};\n\n\n\t/**\n\t * @param {EventTarget} targetElement\n\t * @returns {Element|EventTarget}\n\t */\n\tFastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) {\n\n\t\t// On some older browsers (notably Safari on iOS 4.1 - see issue #56) the event target may be a text node.\n\t\tif (eventTarget.nodeType === Node.TEXT_NODE) {\n\t\t\treturn eventTarget.parentNode;\n\t\t}\n\n\t\treturn eventTarget;\n\t};\n\n\n\t/**\n\t * On touch start, record the position and scroll offset.\n\t *\n\t * @param {Event} event\n\t * @returns {boolean}\n\t */\n\tFastClick.prototype.onTouchStart = function(event) {\n\t\tvar targetElement, touch, selection;\n\n\t\t// Ignore multiple touches, otherwise pinch-to-zoom is prevented if both fingers are on the FastClick element (issue #111).\n\t\tif (event.targetTouches.length > 1) {\n\t\t\treturn true;\n\t\t}\n\n\t\ttargetElement = this.getTargetElementFromEventTarget(event.target);\n\t\ttouch = event.targetTouches[0];\n\n\t\tif (deviceIsIOS) {\n\n\t\t\t// Only trusted events will deselect text on iOS (issue #49)\n\t\t\tselection = window.getSelection();\n\t\t\tif (selection.rangeCount && !selection.isCollapsed) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (!deviceIsIOS4) {\n\n\t\t\t\t// Weird things happen on iOS when an alert or confirm dialog is opened from a click event callback (issue #23):\n\t\t\t\t// when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched\n\t\t\t\t// with the same identifier as the touch event that previously triggered the click that triggered the alert.\n\t\t\t\t// Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an\n\t\t\t\t// immediately preceeding touch event (issue #52), so this fix is unavailable on that platform.\n\t\t\t\t// Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string,\n\t\t\t\t// which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long,\n\t\t\t\t// random integers, it's safe to to continue if the identifier is 0 here.\n\t\t\t\tif (touch.identifier && touch.identifier === this.lastTouchIdentifier) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tthis.lastTouchIdentifier = touch.identifier;\n\n\t\t\t\t// If the target element is a child of a scrollable layer (using -webkit-overflow-scrolling: touch) and:\n\t\t\t\t// 1) the user does a fling scroll on the scrollable layer\n\t\t\t\t// 2) the user stops the fling scroll with another tap\n\t\t\t\t// then the event.target of the last 'touchend' event will be the element that was under the user's finger\n\t\t\t\t// when the fling scroll was started, causing FastClick to send a click event to that layer - unless a check\n\t\t\t\t// is made to ensure that a parent layer was not scrolled before sending a synthetic click (issue #42).\n\t\t\t\tthis.updateScrollParent(targetElement);\n\t\t\t}\n\t\t}\n\n\t\tthis.trackingClick = true;\n\t\tthis.trackingClickStart = event.timeStamp;\n\t\tthis.targetElement = targetElement;\n\n\t\tthis.touchStartX = touch.pageX;\n\t\tthis.touchStartY = touch.pageY;\n\n\t\t// Prevent phantom clicks on fast double-tap (issue #36)\n\t\tif ((event.timeStamp - this.lastClickTime) < this.tapDelay) {\n\t\t\tevent.preventDefault();\n\t\t}\n\n\t\treturn true;\n\t};\n\n\n\t/**\n\t * Based on a touchmove event object, check whether the touch has moved past a boundary since it started.\n\t *\n\t * @param {Event} event\n\t * @returns {boolean}\n\t */\n\tFastClick.prototype.touchHasMoved = function(event) {\n\t\tvar touch = event.changedTouches[0], boundary = this.touchBoundary;\n\n\t\tif (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t};\n\n\n\t/**\n\t * Update the last position.\n\t *\n\t * @param {Event} event\n\t * @returns {boolean}\n\t */\n\tFastClick.prototype.onTouchMove = function(event) {\n\t\tif (!this.trackingClick) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// If the touch has moved, cancel the click tracking\n\t\tif (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) {\n\t\t\tthis.trackingClick = false;\n\t\t\tthis.targetElement = null;\n\t\t}\n\n\t\treturn true;\n\t};\n\n\n\t/**\n\t * Attempt to find the labelled control for the given label element.\n\t *\n\t * @param {EventTarget|HTMLLabelElement} labelElement\n\t * @returns {Element|null}\n\t */\n\tFastClick.prototype.findControl = function(labelElement) {\n\n\t\t// Fast path for newer browsers supporting the HTML5 control attribute\n\t\tif (labelElement.control !== undefined) {\n\t\t\treturn labelElement.control;\n\t\t}\n\n\t\t// All browsers under test that support touch events also support the HTML5 htmlFor attribute\n\t\tif (labelElement.htmlFor) {\n\t\t\treturn document.getElementById(labelElement.htmlFor);\n\t\t}\n\n\t\t// If no for attribute exists, attempt to retrieve the first labellable descendant element\n\t\t// the list of which is defined here: http://www.w3.org/TR/html5/forms.html#category-label\n\t\treturn labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea');\n\t};\n\n\n\t/**\n\t * On touch end, determine whether to send a click event at once.\n\t *\n\t * @param {Event} event\n\t * @returns {boolean}\n\t */\n\tFastClick.prototype.onTouchEnd = function(event) {\n\t\tvar forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;\n\n\t\tif (!this.trackingClick) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Prevent phantom clicks on fast double-tap (issue #36)\n\t\tif ((event.timeStamp - this.lastClickTime) < this.tapDelay) {\n\t\t\tthis.cancelNextClick = true;\n\t\t\treturn true;\n\t\t}\n\n\t\tif ((event.timeStamp - this.trackingClickStart) > this.tapTimeout) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Reset to prevent wrong click cancel on input (issue #156).\n\t\tthis.cancelNextClick = false;\n\n\t\tthis.lastClickTime = event.timeStamp;\n\n\t\ttrackingClickStart = this.trackingClickStart;\n\t\tthis.trackingClick = false;\n\t\tthis.trackingClickStart = 0;\n\n\t\t// On some iOS devices, the targetElement supplied with the event is invalid if the layer\n\t\t// is performing a transition or scroll, and has to be re-detected manually. Note that\n\t\t// for this to function correctly, it must be called *after* the event target is checked!\n\t\t// See issue #57; also filed as rdar://13048589 .\n\t\tif (deviceIsIOSWithBadTarget) {\n\t\t\ttouch = event.changedTouches[0];\n\n\t\t\t// In certain cases arguments of elementFromPoint can be negative, so prevent setting targetElement to null\n\t\t\ttargetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement;\n\t\t\ttargetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;\n\t\t}\n\n\t\ttargetTagName = targetElement.tagName.toLowerCase();\n\t\tif (targetTagName === 'label') {\n\t\t\tforElement = this.findControl(targetElement);\n\t\t\tif (forElement) {\n\t\t\t\tthis.focus(targetElement);\n\t\t\t\tif (deviceIsAndroid) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\ttargetElement = forElement;\n\t\t\t}\n\t\t} else if (this.needsFocus(targetElement)) {\n\n\t\t\t// Case 1: If the touch started a while ago (best guess is 100ms based on tests for issue #36) then focus will be triggered anyway. Return early and unset the target element reference so that the subsequent click will be allowed through.\n\t\t\t// Case 2: Without this exception for input elements tapped when the document is contained in an iframe, then any inputted text won't be visible even though the value attribute is updated as the user types (issue #37).\n\t\t\tif ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) {\n\t\t\t\tthis.targetElement = null;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis.focus(targetElement);\n\t\t\tthis.sendClick(targetElement, event);\n\n\t\t\t// Select elements need the event to go through on iOS 4, otherwise the selector menu won't open.\n\t\t\t// Also this breaks opening selects when VoiceOver is active on iOS6, iOS7 (and possibly others)\n\t\t\tif (!deviceIsIOS || targetTagName !== 'select') {\n\t\t\t\tthis.targetElement = null;\n\t\t\t\tevent.preventDefault();\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tif (deviceIsIOS && !deviceIsIOS4) {\n\n\t\t\t// Don't send a synthetic click event if the target element is contained within a parent layer that was scrolled\n\t\t\t// and this tap is being used to stop the scrolling (usually initiated by a fling - issue #42).\n\t\t\tscrollParent = targetElement.fastClickScrollParent;\n\t\t\tif (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t// Prevent the actual click from going though - unless the target node is marked as requiring\n\t\t// real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted.\n\t\tif (!this.needsClick(targetElement)) {\n\t\t\tevent.preventDefault();\n\t\t\tthis.sendClick(targetElement, event);\n\t\t}\n\n\t\treturn false;\n\t};\n\n\n\t/**\n\t * On touch cancel, stop tracking the click.\n\t *\n\t * @returns {void}\n\t */\n\tFastClick.prototype.onTouchCancel = function() {\n\t\tthis.trackingClick = false;\n\t\tthis.targetElement = null;\n\t};\n\n\n\t/**\n\t * Determine mouse events which should be permitted.\n\t *\n\t * @param {Event} event\n\t * @returns {boolean}\n\t */\n\tFastClick.prototype.onMouse = function(event) {\n\n\t\t// If a target element was never set (because a touch event was never fired) allow the event\n\t\tif (!this.targetElement) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (event.forwardedTouchEvent) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Programmatically generated events targeting a specific element should be permitted\n\t\tif (!event.cancelable) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Derive and check the target element to see whether the mouse event needs to be permitted;\n\t\t// unless explicitly enabled, prevent non-touch click events from triggering actions,\n\t\t// to prevent ghost/doubleclicks.\n\t\tif (!this.needsClick(this.targetElement) || this.cancelNextClick) {\n\n\t\t\t// Prevent any user-added listeners declared on FastClick element from being fired.\n\t\t\tif (event.stopImmediatePropagation) {\n\t\t\t\tevent.stopImmediatePropagation();\n\t\t\t} else {\n\n\t\t\t\t// Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)\n\t\t\t\tevent.propagationStopped = true;\n\t\t\t}\n\n\t\t\t// Cancel the event\n\t\t\tevent.stopPropagation();\n\t\t\tevent.preventDefault();\n\n\t\t\treturn false;\n\t\t}\n\n\t\t// If the mouse event is permitted, return true for the action to go through.\n\t\treturn true;\n\t};\n\n\n\t/**\n\t * On actual clicks, determine whether this is a touch-generated click, a click action occurring\n\t * naturally after a delay after a touch (which needs to be cancelled to avoid duplication), or\n\t * an actual click which should be permitted.\n\t *\n\t * @param {Event} event\n\t * @returns {boolean}\n\t */\n\tFastClick.prototype.onClick = function(event) {\n\t\tvar permitted;\n\n\t\t// It's possible for another FastClick-like library delivered with third-party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early.\n\t\tif (this.trackingClick) {\n\t\t\tthis.targetElement = null;\n\t\t\tthis.trackingClick = false;\n\t\t\treturn true;\n\t\t}\n\n\t\t// Very odd behaviour on iOS (issue #18): if a submit element is present inside a form and the user hits enter in the iOS simulator or clicks the Go button on the pop-up OS keyboard the a kind of 'fake' click event will be triggered with the submit-type input element as the target.\n\t\tif (event.target.type === 'submit' && event.detail === 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\tpermitted = this.onMouse(event);\n\n\t\t// Only unset targetElement if the click is not permitted. This will ensure that the check for !targetElement in onMouse fails and the browser's click doesn't go through.\n\t\tif (!permitted) {\n\t\t\tthis.targetElement = null;\n\t\t}\n\n\t\t// If clicks are permitted, return true for the action to go through.\n\t\treturn permitted;\n\t};\n\n\n\t/**\n\t * Remove all FastClick's event listeners.\n\t *\n\t * @returns {void}\n\t */\n\tFastClick.prototype.destroy = function() {\n\t\tvar layer = this.layer;\n\n\t\tif (deviceIsAndroid) {\n\t\t\tlayer.removeEventListener('mouseover', this.onMouse, true);\n\t\t\tlayer.removeEventListener('mousedown', this.onMouse, true);\n\t\t\tlayer.removeEventListener('mouseup', this.onMouse, true);\n\t\t}\n\n\t\tlayer.removeEventListener('click', this.onClick, true);\n\t\tlayer.removeEventListener('touchstart', this.onTouchStart, false);\n\t\tlayer.removeEventListener('touchmove', this.onTouchMove, false);\n\t\tlayer.removeEventListener('touchend', this.onTouchEnd, false);\n\t\tlayer.removeEventListener('touchcancel', this.onTouchCancel, false);\n\t};\n\n\n\t/**\n\t * Check whether FastClick is needed.\n\t *\n\t * @param {Element} layer The layer to listen on\n\t */\n\tFastClick.notNeeded = function(layer) {\n\t\tvar metaViewport;\n\t\tvar chromeVersion;\n\t\tvar blackberryVersion;\n\t\tvar firefoxVersion;\n\n\t\t// Devices that don't support touch don't need FastClick\n\t\tif (typeof window.ontouchstart === 'undefined') {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Chrome version - zero for other browsers\n\t\tchromeVersion = +(/Chrome\\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];\n\n\t\tif (chromeVersion) {\n\n\t\t\tif (deviceIsAndroid) {\n\t\t\t\tmetaViewport = document.querySelector('meta[name=viewport]');\n\n\t\t\t\tif (metaViewport) {\n\t\t\t\t\t// Chrome on Android with user-scalable=\"no\" doesn't need FastClick (issue #89)\n\t\t\t\t\tif (metaViewport.content.indexOf('user-scalable=no') !== -1) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\t// Chrome 32 and above with width=device-width or less don't need FastClick\n\t\t\t\t\tif (chromeVersion > 31 && document.documentElement.scrollWidth <= window.outerWidth) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t// Chrome desktop doesn't need FastClick (issue #15)\n\t\t\t} else {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tif (deviceIsBlackBerry10) {\n\t\t\tblackberryVersion = navigator.userAgent.match(/Version\\/([0-9]*)\\.([0-9]*)/);\n\n\t\t\t// BlackBerry 10.3+ does not require Fastclick library.\n\t\t\t// https://github.com/ftlabs/fastclick/issues/251\n\t\t\tif (blackberryVersion[1] >= 10 && blackberryVersion[2] >= 3) {\n\t\t\t\tmetaViewport = document.querySelector('meta[name=viewport]');\n\n\t\t\t\tif (metaViewport) {\n\t\t\t\t\t// user-scalable=no eliminates click delay.\n\t\t\t\t\tif (metaViewport.content.indexOf('user-scalable=no') !== -1) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\t// width=device-width (or less than device-width) eliminates click delay.\n\t\t\t\t\tif (document.documentElement.scrollWidth <= window.outerWidth) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// IE10 with -ms-touch-action: none or manipulation, which disables double-tap-to-zoom (issue #97)\n\t\tif (layer.style.msTouchAction === 'none' || layer.style.touchAction === 'manipulation') {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Firefox version - zero for other browsers\n\t\tfirefoxVersion = +(/Firefox\\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];\n\n\t\tif (firefoxVersion >= 27) {\n\t\t\t// Firefox 27+ does not have tap delay if the content is not zoomable - https://bugzilla.mozilla.org/show_bug.cgi?id=922896\n\n\t\t\tmetaViewport = document.querySelector('meta[name=viewport]');\n\t\t\tif (metaViewport && (metaViewport.content.indexOf('user-scalable=no') !== -1 || document.documentElement.scrollWidth <= window.outerWidth)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t// IE11: prefixed -ms-touch-action is no longer supported and it's recomended to use non-prefixed version\n\t\t// http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx\n\t\tif (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t};\n\n\n\t/**\n\t * Factory method for creating a FastClick object\n\t *\n\t * @param {Element} layer The layer to listen on\n\t * @param {Object} [options={}] The options to override the defaults\n\t */\n\tFastClick.attach = function(layer, options) {\n\t\treturn new FastClick(layer, options);\n\t};\n\n\n\tif (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {\n\n\t\t// AMD. Register as an anonymous module.\n\t\tdefine(function() {\n\t\t\treturn FastClick;\n\t\t});\n\t} else if (typeof module !== 'undefined' && module.exports) {\n\t\tmodule.exports = FastClick.attach;\n\t\tmodule.exports.FastClick = FastClick;\n\t} else {\n\t\twindow.FastClick = FastClick;\n\t}\n}());\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/adminlte/plugins/iCheck/square/blue.css",
    "content": "/* iCheck plugin Square skin, blue\n----------------------------------- */\n.icheckbox_square-blue,\n.iradio_square-blue {\n    display: inline-block;\n    *display: inline;\n    vertical-align: middle;\n    margin: 0;\n    padding: 0;\n    width: 22px;\n    height: 22px;\n    background: url(blue.png) no-repeat;\n    border: none;\n    cursor: pointer;\n}\n\n.icheckbox_square-blue {\n    background-position: 0 0;\n}\n    .icheckbox_square-blue.hover {\n        background-position: -24px 0;\n    }\n    .icheckbox_square-blue.checked {\n        background-position: -48px 0;\n    }\n    .icheckbox_square-blue.disabled {\n        background-position: -72px 0;\n        cursor: default;\n    }\n    .icheckbox_square-blue.checked.disabled {\n        background-position: -96px 0;\n    }\n\n.iradio_square-blue {\n    background-position: -120px 0;\n}\n    .iradio_square-blue.hover {\n        background-position: -144px 0;\n    }\n    .iradio_square-blue.checked {\n        background-position: -168px 0;\n    }\n    .iradio_square-blue.disabled {\n        background-position: -192px 0;\n        cursor: default;\n    }\n    .iradio_square-blue.checked.disabled {\n        background-position: -216px 0;\n    }\n\n/* Retina support */\n@media only screen and (-webkit-min-device-pixel-ratio: 1.5),\n       only screen and (-moz-min-device-pixel-ratio: 1.5),\n       only screen and (-o-min-device-pixel-ratio: 3/2),\n       only screen and (min-device-pixel-ratio: 1.5) {\n    .icheckbox_square-blue,\n    .iradio_square-blue {\n        background-image: url(blue@2x.png);\n        -webkit-background-size: 240px 24px;\n        background-size: 240px 24px;\n    }\n}"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/common.1.js",
    "content": "$(function(){\n\n\t// logout\n\t$(\"#logoutBtn\").click(function(){\n\t\tlayer.confirm( I18n.logout_confirm , {\n\t\t\ticon: 3,\n\t\t\ttitle: I18n.system_tips ,\n            btn: [ I18n.system_ok, I18n.system_cancel ]\n\t\t}, function(index){\n\t\t\tlayer.close(index);\n\n\t\t\t$.post(base_url + \"/logout\", function(data, status) {\n\t\t\t\tif (data.code == \"200\") {\n                    layer.msg( I18n.logout_success );\n                    setTimeout(function(){\n                        window.location.href = base_url + \"/\";\n                    }, 500);\n\t\t\t\t} else {\n\t\t\t\t\tlayer.open({\n\t\t\t\t\t\ttitle: I18n.system_tips ,\n                        btn: [ I18n.system_ok ],\n\t\t\t\t\t\tcontent: (data.msg || I18n.logout_fail),\n\t\t\t\t\t\ticon: '2'\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t});\n\n\t// slideToTop\n\tvar slideToTop = $(\"<div />\");\n\tslideToTop.html('<i class=\"fa fa-chevron-up\"></i>');\n\tslideToTop.css({\n\t\tposition: 'fixed',\n\t\tbottom: '20px',\n\t\tright: '25px',\n\t\twidth: '40px',\n\t\theight: '40px',\n\t\tcolor: '#eee',\n\t\t'font-size': '',\n\t\t'line-height': '40px',\n\t\t'text-align': 'center',\n\t\t'background-color': '#222d32',\n\t\tcursor: 'pointer',\n\t\t'border-radius': '5px',\n\t\t'z-index': '99999',\n\t\topacity: '.7',\n\t\t'display': 'none'\n\t});\n\tslideToTop.on('mouseenter', function () {\n\t\t$(this).css('opacity', '1');\n\t});\n\tslideToTop.on('mouseout', function () {\n\t\t$(this).css('opacity', '.7');\n\t});\n\t$('.wrapper').append(slideToTop);\n\t$(window).scroll(function () {\n\t\tif ($(window).scrollTop() >= 150) {\n\t\t\tif (!$(slideToTop).is(':visible')) {\n\t\t\t\t$(slideToTop).fadeIn(500);\n\t\t\t}\n\t\t} else {\n\t\t\t$(slideToTop).fadeOut(500);\n\t\t}\n\t});\n\t$(slideToTop).click(function () {\n\t\t$(\"html,body\").animate({\t\t// firefox ie not support body, chrome support body. but found that new version chrome not support body too.\n\t\t\tscrollTop: 0\n\t\t}, 100);\n\t});\n\n\t// left menu status v: js + server + cookie\n\t$('.sidebar-toggle').click(function(){\n\t\tvar xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings');\t// on=open，off=close\n\t\tif ('off' == xxljob_adminlte_settings) {\n            xxljob_adminlte_settings = 'on';\n\t\t} else {\n            xxljob_adminlte_settings = 'off';\n\t\t}\n\t\t$.cookie('xxljob_adminlte_settings', xxljob_adminlte_settings, { expires: 7 });\t//$.cookie('the_cookie', '', { expires: -1 });\n\t});\n\n\t// left menu status v1: js + cookie\n\t/*\n\t var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings');\n\t if (xxljob_adminlte_settings == 'off') {\n\t \t$('body').addClass('sidebar-collapse');\n\t }\n\t */\n\n\n    // update pwd\n    $('#updatePwd').on('click', function(){\n        $('#updatePwdModal').modal({backdrop: false, keyboard: false}).modal('show');\n    });\n    var updatePwdModalValidate = $(\"#updatePwdModal .form\").validate({\n        errorElement : 'span',\n        errorClass : 'help-block',\n        focusInvalid : true,\n        rules : {\n            password : {\n                required : true ,\n                rangelength:[4,50]\n            }\n        },\n        messages : {\n            password : {\n                required : '请输入密码'  ,\n                rangelength : \"密码长度限制为4~50\"\n            }\n        },\n        highlight : function(element) {\n            $(element).closest('.form-group').addClass('has-error');\n        },\n        success : function(label) {\n            label.closest('.form-group').removeClass('has-error');\n            label.remove();\n        },\n        errorPlacement : function(error, element) {\n            element.parent('div').append(error);\n        },\n        submitHandler : function(form) {\n            $.post(base_url + \"/user/updatePwd\",  $(\"#updatePwdModal .form\").serialize(), function(data, status) {\n                if (data.code == 200) {\n                    $('#updatePwdModal').modal('hide');\n\n                    layer.msg( I18n.change_pwd_suc_to_logout );\n                    setTimeout(function(){\n                        $.post(base_url + \"/logout\", function(data, status) {\n                            if (data.code == 200) {\n                                window.location.href = base_url + \"/\";\n                            } else {\n                                layer.open({\n                                    icon: '2',\n                                    content: (data.msg|| I18n.logout_fail)\n                                });\n                            }\n                        });\n                    }, 500);\n                } else {\n                    layer.open({\n                        icon: '2',\n                        content: (data.msg|| I18n.change_pwd + I18n.system_fail )\n                    });\n                }\n            });\n        }\n    });\n    $(\"#updatePwdModal\").on('hide.bs.modal', function () {\n        $(\"#updatePwdModal .form\")[0].reset();\n        updatePwdModalValidate.resetForm();\n        $(\"#updatePwdModal .form .form-group\").removeClass(\"has-error\");\n    });\n\t\n});\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/index.js",
    "content": "/**\n * Created by xuxueli on 17/4/24.\n */\n$(function () {\n\n    // filter Time\n    var rangesConf = {};\n    rangesConf[I18n.daterangepicker_ranges_today] = [moment().startOf('day'), moment().endOf('day')];\n    rangesConf[I18n.daterangepicker_ranges_yesterday] = [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').endOf('day')];\n    rangesConf[I18n.daterangepicker_ranges_this_month] = [moment().startOf('month'), moment().endOf('month')];\n    rangesConf[I18n.daterangepicker_ranges_last_month] = [moment().subtract(1, 'months').startOf('month'), moment().subtract(1, 'months').endOf('month')];\n    rangesConf[I18n.daterangepicker_ranges_recent_week] = [moment().subtract(1, 'weeks').startOf('day'), moment().endOf('day')];\n    rangesConf[I18n.daterangepicker_ranges_recent_month] = [moment().subtract(1, 'months').startOf('day'), moment().endOf('day')];\n\n    $('#filterTime').daterangepicker({\n        autoApply:false,\n        singleDatePicker:false,\n        showDropdowns:false,        // 是否显示年月选择条件\n        timePicker: true, \t\t\t// 是否显示小时和分钟选择条件\n        timePickerIncrement: 10, \t// 时间的增量，单位为分钟\n        timePicker24Hour : true,\n        opens : 'left', //日期选择框的弹出位置\n        ranges: rangesConf,\n        locale : {\n            format: 'YYYY-MM-DD HH:mm:ss',\n            separator : ' - ',\n            customRangeLabel : I18n.daterangepicker_custom_name ,\n            applyLabel : I18n.system_ok ,\n            cancelLabel : I18n.system_cancel ,\n            fromLabel : I18n.daterangepicker_custom_starttime ,\n            toLabel : I18n.daterangepicker_custom_endtime ,\n            daysOfWeek : I18n.daterangepicker_custom_daysofweek.split(',') ,        // '日', '一', '二', '三', '四', '五', '六'\n            monthNames : I18n.daterangepicker_custom_monthnames.split(',') ,        // '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'\n            firstDay : 1\n        },\n        startDate: rangesConf[I18n.daterangepicker_ranges_recent_week][0] ,\n        endDate: rangesConf[I18n.daterangepicker_ranges_recent_week][1]\n    }, function (start, end, label) {\n        freshChartDate(start, end);\n    });\n    freshChartDate(rangesConf[I18n.daterangepicker_ranges_recent_week][0], rangesConf[I18n.daterangepicker_ranges_recent_week][1]);\n\n    /**\n     * fresh Chart Date\n     *\n     * @param startDate\n     * @param endDate\n     */\n    function freshChartDate(startDate, endDate) {\n        $.ajax({\n            type : 'POST',\n            url : base_url + '/chartInfo',\n            data : {\n                'startDate':startDate.format('YYYY-MM-DD HH:mm:ss'),\n                'endDate':endDate.format('YYYY-MM-DD HH:mm:ss')\n            },\n            dataType : \"json\",\n            success : function(data){\n                if (data.code == 200) {\n                    lineChartInit(data)\n                    pieChartInit(data);\n                } else {\n                    layer.open({\n                        title: I18n.system_tips ,\n                        btn: [ I18n.system_ok ],\n                        content: (data.msg || I18n.job_dashboard_report_loaddata_fail ),\n                        icon: '2'\n                    });\n                }\n            }\n        });\n    }\n\n    /**\n     * line Chart Init\n     */\n    function lineChartInit(data) {\n        var option = {\n               title: {\n                   text: I18n.job_dashboard_date_report\n               },\n               tooltip : {\n                   trigger: 'axis',\n                   axisPointer: {\n                       type: 'cross',\n                       label: {\n                           backgroundColor: '#6a7985'\n                       }\n                   }\n               },\n               legend: {\n                   data:[I18n.joblog_status_suc, I18n.joblog_status_fail, I18n.joblog_status_running]\n               },\n               toolbox: {\n                   feature: {\n                       /*saveAsImage: {}*/\n                   }\n               },\n               grid: {\n                   left: '3%',\n                   right: '4%',\n                   bottom: '3%',\n                   containLabel: true\n               },\n               xAxis : [\n                   {\n                       type : 'category',\n                       boundaryGap : false,\n                       data : data.content.triggerDayList\n                   }\n               ],\n               yAxis : [\n                   {\n                       type : 'value'\n                   }\n               ],\n               series : [\n                   {\n                       name:I18n.joblog_status_suc,\n                       type:'line',\n                       stack: 'Total',\n                       areaStyle: {normal: {}},\n                       data: data.content.triggerDayCountSucList\n                   },\n                   {\n                       name:I18n.joblog_status_fail,\n                       type:'line',\n                       stack: 'Total',\n                       label: {\n                           normal: {\n                               show: true,\n                               position: 'top'\n                           }\n                       },\n                       areaStyle: {normal: {}},\n                       data: data.content.triggerDayCountFailList\n                   },\n                   {\n                       name:I18n.joblog_status_running,\n                       type:'line',\n                       stack: 'Total',\n                       areaStyle: {normal: {}},\n                       data: data.content.triggerDayCountRunningList\n                   }\n               ],\n                color:['#00A65A', '#c23632', '#F39C12']\n        };\n\n        var lineChart = echarts.init(document.getElementById('lineChart'));\n        lineChart.setOption(option);\n    }\n\n    /**\n     * pie Chart Init\n     */\n    function pieChartInit(data) {\n        var option = {\n            title : {\n                text: I18n.job_dashboard_rate_report ,\n                /*subtext: 'subtext',*/\n                x:'center'\n            },\n            tooltip : {\n                trigger: 'item',\n                formatter: \"{b} : {c} ({d}%)\"\n            },\n            legend: {\n                orient: 'vertical',\n                left: 'left',\n                data: [I18n.joblog_status_suc, I18n.joblog_status_fail, I18n.joblog_status_running ]\n            },\n            series : [\n                {\n                    //name: '分布比例',\n                    type: 'pie',\n                    radius : '55%',\n                    center: ['50%', '60%'],\n                    data:[\n                        {\n                            name:I18n.joblog_status_suc,\n                            value:data.content.triggerCountSucTotal\n                        },\n                        {\n                            name:I18n.joblog_status_fail,\n                            value:data.content.triggerCountFailTotal\n                        },\n                        {\n                            name:I18n.joblog_status_running,\n                            value:data.content.triggerCountRunningTotal\n                        }\n                    ],\n                    itemStyle: {\n                        emphasis: {\n                            shadowBlur: 10,\n                            shadowOffsetX: 0,\n                            shadowColor: 'rgba(0, 0, 0, 0.5)'\n                        }\n                    }\n                }\n            ],\n            color:['#00A65A', '#c23632', '#F39C12']\n        };\n        var pieChart = echarts.init(document.getElementById('pieChart'));\n        pieChart.setOption(option);\n    }\n\n});\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/jobcode.index.1.js",
    "content": "$(function() {\n\n\t// init code editor\n\tvar codeEditor;\n\tfunction initIde(glueSource) {\n\t\tif (codeEditor == null) {\n            codeEditor = CodeMirror(document.getElementById(\"ideWindow\"), {\n                mode : ideMode,\n                lineNumbers : true,\n                matchBrackets : true,\n                value: glueSource\n            });\n\t\t} else {\n            codeEditor.setValue(glueSource);\n\t\t}\n\t}\n\n\tinitIde($(\"#version_now\").val());\n\n\t// code change\n\t$(\".source_version\").click(function(){\n\t\tvar sourceId = $(this).attr('version');\n\t\tvar temp = $( \"#\" + sourceId ).val();\n\n\t\t//codeEditor.setValue('');\n\t\tinitIde(temp);\n\t});\n\n\t// code source save\n\t$(\"#save\").click(function() {\n\t\t$('#saveModal').modal({backdrop: false, keyboard: false}).modal('show');\n\t});\n\n\t$(\"#saveModal .ok\").click(function() {\n\n\t\tvar glueSource = codeEditor.getValue();\n\t\tvar glueRemark = $(\"#glueRemark\").val();\n\t\t\n\t\tif (!glueRemark) {\n\t\t\tlayer.open({\n\t\t\t\ttitle: I18n.system_tips,\n                btn: [ I18n.system_ok],\n\t\t\t\tcontent: I18n.system_please_input + I18n.jobinfo_glue_remark ,\n\t\t\t\ticon: '2'\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (glueRemark.length <4 || glueRemark.length > 100) {\n\t\t\tlayer.open({\n\t\t\t\ttitle: I18n.system_tips ,\n                btn: [ I18n.system_ok ],\n\t\t\t\tcontent: I18n.jobinfo_glue_remark_limit ,\n\t\t\t\ticon: '2'\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t$.ajax({\n\t\t\ttype : 'POST',\n\t\t\turl : base_url + '/jobcode/save',\n\t\t\tdata : {\n\t\t\t\t'id' : id,\n\t\t\t\t'glueSource' : glueSource,\n\t\t\t\t'glueRemark' : glueRemark\n\t\t\t},\n\t\t\tdataType : \"json\",\n\t\t\tsuccess : function(data){\n\t\t\t\tif (data.code == 200) {\n\t\t\t\t\tlayer.open({\n\t\t\t\t\t\ttitle: I18n.system_tips,\n                        btn: [ I18n.system_ok ],\n\t\t\t\t\t\tcontent: (I18n.system_save + I18n.system_success) ,\n\t\t\t\t\t\ticon: '1',\n\t\t\t\t\t\tend: function(layero, index){\n\t\t\t\t\t\t\t//$(window).unbind('beforeunload');\n\t\t\t\t\t\t\twindow.location.reload();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tlayer.open({\n\t\t\t\t\t\ttitle: I18n.system_tips,\n                        btn: [ I18n.system_ok ],\n\t\t\t\t\t\tcontent: (data.msg || (I18n.system_save + I18n.system_fail) ),\n\t\t\t\t\t\ticon: '2'\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t});\n\t\n\t// before upload\n\t/*$(window).bind('beforeunload',function(){\n\t\treturn 'Glue尚未保存，确定离开Glue编辑器？';\n\t});*/\n\t\n});\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/jobgroup.index.1.js",
    "content": "$(function() {\n\n\t// init date tables\n\tvar jobGroupTable = $(\"#jobgroup_list\").dataTable({\n\t\t\"deferRender\": true,\n\t\t\"processing\" : true,\n\t\t\"serverSide\": true,\n\t\t\"ajax\": {\n\t\t\turl: base_url + \"/jobgroup/pageList\",\n\t\t\ttype:\"post\",\n\t\t\tdata : function ( d ) {\n\t\t\t\tvar obj = {};\n\t\t\t\tobj.appname = $('#appname').val();\n\t\t\t\tobj.title = $('#title').val();\n\t\t\t\tobj.start = d.start;\n\t\t\t\tobj.length = d.length;\n\t\t\t\treturn obj;\n\t\t\t}\n\t\t},\n\t\t\"searching\": false,\n\t\t\"ordering\": false,\n\t\t//\"scrollX\": true,\t// scroll x，close self-adaption\n\t\t\"columns\": [\n\t\t\t{\n\t\t\t\t\"data\": 'id',\n\t\t\t\t\"visible\" : false\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"data\": 'appname',\n\t\t\t\t\"visible\" : true,\n\t\t\t\t\"width\":'30%'\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"data\": 'title',\n\t\t\t\t\"visible\" : true,\n\t\t\t\t\"width\":'30%'\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"data\": 'addressType',\n\t\t\t\t\"width\":'10%',\n\t\t\t\t\"visible\" : true,\n\t\t\t\t\"render\": function ( data, type, row ) {\n\t\t\t\t\tif (row.addressType == 0) {\n\t\t\t\t\t\treturn I18n.jobgroup_field_addressType_0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn I18n.jobgroup_field_addressType_1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"data\": 'registryList',\n\t\t\t\t\"width\":'15%',\n\t\t\t\t\"visible\" : true,\n\t\t\t\t\"render\": function ( data, type, row ) {\n\t\t\t\t\treturn row.registryList\n\t\t\t\t\t\t?'<a class=\"show_registryList\" href=\"javascript:;\" _id=\"'+ row.id +'\" >'\n\t\t\t\t\t\t\t+ I18n.system_show +' ( ' + row.registryList.length+ ' ）</a>'\n\t\t\t\t\t\t:I18n.system_empty;\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"data\": I18n.system_opt ,\n\t\t\t\t\"width\":'15%',\n\t\t\t\t\"render\": function ( data, type, row ) {\n\t\t\t\t\treturn function(){\n\t\t\t\t\t\t// data\n\t\t\t\t\t\ttableData['key'+row.id] = row;\n\n\t\t\t\t\t\t// opt\n\t\t\t\t\t\tvar html = '<div class=\"btn-group\">\\n' +\n\t\t\t\t\t\t\t'     <button type=\"button\" class=\"btn btn-primary btn-sm\">'+ I18n.system_opt +'</button>\\n' +\n\t\t\t\t\t\t\t'     <button type=\"button\" class=\"btn btn-primary btn-sm dropdown-toggle\" data-toggle=\"dropdown\">\\n' +\n\t\t\t\t\t\t\t'       <span class=\"caret\"></span>\\n' +\n\t\t\t\t\t\t\t'       <span class=\"sr-only\">Toggle Dropdown</span>\\n' +\n\t\t\t\t\t\t\t'     </button>\\n' +\n\t\t\t\t\t\t\t'     <ul class=\"dropdown-menu\" role=\"menu\" _id=\"'+ row.id +'\" >\\n' +\n\t\t\t\t\t\t\t'       <li><a href=\"javascript:void(0);\" class=\"opt_edit\" >'+ I18n.system_opt_edit +'</a></li>\\n' +\n\t\t\t\t\t\t\t'       <li><a href=\"javascript:void(0);\" class=\"opt_del\" >'+ I18n.system_opt_del +'</a></li>\\n' +\n\t\t\t\t\t\t\t'     </ul>\\n' +\n\t\t\t\t\t\t\t'   </div>';\n\n\t\t\t\t\t\treturn html;\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t],\n\t\t\"language\" : {\n\t\t\t\"sProcessing\" : I18n.dataTable_sProcessing ,\n\t\t\t\"sLengthMenu\" : I18n.dataTable_sLengthMenu ,\n\t\t\t\"sZeroRecords\" : I18n.dataTable_sZeroRecords ,\n\t\t\t\"sInfo\" : I18n.dataTable_sInfo ,\n\t\t\t\"sInfoEmpty\" : I18n.dataTable_sInfoEmpty ,\n\t\t\t\"sInfoFiltered\" : I18n.dataTable_sInfoFiltered ,\n\t\t\t\"sInfoPostFix\" : \"\",\n\t\t\t\"sSearch\" : I18n.dataTable_sSearch ,\n\t\t\t\"sUrl\" : \"\",\n\t\t\t\"sEmptyTable\" : I18n.dataTable_sEmptyTable ,\n\t\t\t\"sLoadingRecords\" : I18n.dataTable_sLoadingRecords ,\n\t\t\t\"sInfoThousands\" : \",\",\n\t\t\t\"oPaginate\" : {\n\t\t\t\t\"sFirst\" : I18n.dataTable_sFirst ,\n\t\t\t\t\"sPrevious\" : I18n.dataTable_sPrevious ,\n\t\t\t\t\"sNext\" : I18n.dataTable_sNext ,\n\t\t\t\t\"sLast\" : I18n.dataTable_sLast\n\t\t\t},\n\t\t\t\"oAria\" : {\n\t\t\t\t\"sSortAscending\" : I18n.dataTable_sSortAscending ,\n\t\t\t\t\"sSortDescending\" : I18n.dataTable_sSortDescending\n\t\t\t}\n\t\t}\n\t});\n\n\t// table data\n\tvar tableData = {};\n\n\t// search btn\n\t$('#searchBtn').on('click', function(){\n\t\tjobGroupTable.fnDraw();\n\t});\n\n\t// job registryinfo\n\t$(\"#jobgroup_list\").on('click', '.show_registryList',function() {\n\t\tvar id = $(this).attr(\"_id\");\n\t\tvar row = tableData['key'+id];\n\n\t\tvar html = '<div>';\n\t\tif (row.registryList) {\n\t\t\tfor (var index in row.registryList) {\n\t\t\t\thtml += (parseInt(index)+1) + '. <span class=\"badge bg-green\" >' + row.registryList[index] + '</span><br>';\n\t\t\t}\n\t\t}\n\t\thtml += '</div>';\n\n\t\tlayer.open({\n\t\t\ttitle: I18n.jobinfo_opt_registryinfo ,\n\t\t\tbtn: [ I18n.system_ok ],\n\t\t\tcontent: html\n\t\t});\n\n\t});\n\n\n\t// opt_del\n\t$(\"#jobgroup_list\").on('click', '.opt_del',function() {\n\t\tvar id = $(this).parents('ul').attr(\"_id\");\n\n\t\tlayer.confirm( (I18n.system_ok + I18n.jobgroup_del + '？') , {\n\t\t\ticon: 3,\n\t\t\ttitle: I18n.system_tips ,\n\t\t\tbtn: [ I18n.system_ok, I18n.system_cancel ]\n\t\t}, function(index){\n\t\t\tlayer.close(index);\n\n\t\t\t$.ajax({\n\t\t\t\ttype : 'POST',\n\t\t\t\turl : base_url + '/jobgroup/remove',\n\t\t\t\tdata : {\"id\":id},\n\t\t\t\tdataType : \"json\",\n\t\t\t\tsuccess : function(data){\n\t\t\t\t\tif (data.code == 200) {\n\t\t\t\t\t\tlayer.open({\n\t\t\t\t\t\t\ttitle: I18n.system_tips ,\n\t\t\t\t\t\t\tbtn: [ I18n.system_ok ],\n\t\t\t\t\t\t\tcontent: (I18n.jobgroup_del + I18n.system_success),\n\t\t\t\t\t\t\ticon: '1',\n\t\t\t\t\t\t\tend: function(layero, index){\n\t\t\t\t\t\t\t\tjobGroupTable.fnDraw();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlayer.open({\n\t\t\t\t\t\t\ttitle: I18n.system_tips,\n\t\t\t\t\t\t\tbtn: [ I18n.system_ok ],\n\t\t\t\t\t\t\tcontent: (data.msg || (I18n.jobgroup_del + I18n.system_fail)),\n\t\t\t\t\t\t\ticon: '2'\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t});\n\n\n\t// jquery.validate “low letters start, limit contants、 letters、numbers and line-through.”\n\tjQuery.validator.addMethod(\"myValid01\", function(value, element) {\n\t\tvar length = value.length;\n\t\tvar valid = /^[a-z][a-zA-Z0-9-]*$/;\n\t\treturn this.optional(element) || valid.test(value);\n\t}, I18n.jobgroup_field_appname_limit );\n\n\t$('.add').on('click', function(){\n\t\t$('#addModal').modal({backdrop: false, keyboard: false}).modal('show');\n\t});\n\tvar addModalValidate = $(\"#addModal .form\").validate({\n\t\terrorElement : 'span',\n\t\terrorClass : 'help-block',\n\t\tfocusInvalid : true,\n\t\trules : {\n\t\t\tappname : {\n\t\t\t\trequired : true,\n\t\t\t\trangelength:[4,64],\n\t\t\t\tmyValid01 : true\n\t\t\t},\n\t\t\ttitle : {\n\t\t\t\trequired : true,\n\t\t\t\trangelength:[4, 12]\n\t\t\t}\n\t\t},\n\t\tmessages : {\n\t\t\tappname : {\n\t\t\t\trequired : I18n.system_please_input+\"AppName\",\n\t\t\t\trangelength: I18n.jobgroup_field_appname_length ,\n\t\t\t\tmyValid01: I18n.jobgroup_field_appname_limit\n\t\t\t},\n\t\t\ttitle : {\n\t\t\t\trequired : I18n.system_please_input + I18n.jobgroup_field_title ,\n\t\t\t\trangelength: I18n.jobgroup_field_title_length\n\t\t\t}\n\t\t},\n\t\thighlight : function(element) {\n\t\t\t$(element).closest('.form-group').addClass('has-error');\n\t\t},\n\t\tsuccess : function(label) {\n\t\t\tlabel.closest('.form-group').removeClass('has-error');\n\t\t\tlabel.remove();\n\t\t},\n\t\terrorPlacement : function(error, element) {\n\t\t\telement.parent('div').append(error);\n\t\t},\n\t\tsubmitHandler : function(form) {\n\t\t\t$.post(base_url + \"/jobgroup/save\",  $(\"#addModal .form\").serialize(), function(data, status) {\n\t\t\t\tif (data.code == \"200\") {\n\t\t\t\t\t$('#addModal').modal('hide');\n\t\t\t\t\tlayer.open({\n\t\t\t\t\t\ttitle: I18n.system_tips ,\n                        btn: [ I18n.system_ok ],\n\t\t\t\t\t\tcontent: I18n.system_add_suc ,\n\t\t\t\t\t\ticon: '1',\n\t\t\t\t\t\tend: function(layero, index){\n\t\t\t\t\t\t\tjobGroupTable.fnDraw();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tlayer.open({\n\t\t\t\t\t\ttitle: I18n.system_tips,\n                        btn: [ I18n.system_ok ],\n\t\t\t\t\t\tcontent: (data.msg || I18n.system_add_fail  ),\n\t\t\t\t\t\ticon: '2'\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t});\n\t$(\"#addModal\").on('hide.bs.modal', function () {\n\t\t$(\"#addModal .form\")[0].reset();\n\t\taddModalValidate.resetForm();\n\t\t$(\"#addModal .form .form-group\").removeClass(\"has-error\");\n\t});\n\n\t// addressType change\n\t$(\"#addModal input[name=addressType], #updateModal input[name=addressType]\").click(function(){\n\t\tvar addressType = $(this).val();\n\t\tvar $addressList = $(this).parents(\"form\").find(\"textarea[name=addressList]\");\n\t\tif (addressType == 0) {\n            $addressList.css(\"background-color\", \"#eee\");\t// 自动注册\n            $addressList.attr(\"readonly\",\"readonly\");\n\t\t\t$addressList.val(\"\");\n\t\t} else {\n            $addressList.css(\"background-color\", \"white\");\n\t\t\t$addressList.removeAttr(\"readonly\");\n\t\t}\n\t});\n\n\t// opt_edit\n\t$(\"#jobgroup_list\").on('click', '.opt_edit',function() {\n\t\tvar id = $(this).parents('ul').attr(\"_id\");\n\t\tvar row = tableData['key'+id];\n\n\t\t$(\"#updateModal .form input[name='id']\").val( row.id );\n\t\t$(\"#updateModal .form input[name='appname']\").val( row.appname );\n\t\t$(\"#updateModal .form input[name='title']\").val( row.title );\n\n\t\t// 注册方式\n\t\t$(\"#updateModal .form input[name='addressType']\").removeAttr('checked');\n\t\t$(\"#updateModal .form input[name='addressType'][value='\"+ row.addressType +\"']\").click();\n\t\t// 机器地址\n\t\t$(\"#updateModal .form textarea[name='addressList']\").val( row.addressList );\n\n\t\t$('#updateModal').modal({backdrop: false, keyboard: false}).modal('show');\n\t});\n\tvar updateModalValidate = $(\"#updateModal .form\").validate({\n\t\terrorElement : 'span',\n\t\terrorClass : 'help-block',\n\t\tfocusInvalid : true,\n\t\trules : {\n\t\t\tappname : {\n\t\t\t\trequired : true,\n\t\t\t\trangelength:[4,64],\n\t\t\t\tmyValid01 : true\n\t\t\t},\n\t\t\ttitle : {\n\t\t\t\trequired : true,\n\t\t\t\trangelength:[4, 12]\n\t\t\t}\n\t\t},\n\t\tmessages : {\n\t\t\tappname : {\n                required : I18n.system_please_input+\"AppName\",\n                rangelength: I18n.jobgroup_field_appname_length ,\n                myValid01: I18n.jobgroup_field_appname_limit\n            },\n            title : {\n                required : I18n.system_please_input + I18n.jobgroup_field_title ,\n                rangelength: I18n.jobgroup_field_title_length\n            }\n\t\t},\n\t\thighlight : function(element) {\n\t\t\t$(element).closest('.form-group').addClass('has-error');\n\t\t},\n\t\tsuccess : function(label) {\n\t\t\tlabel.closest('.form-group').removeClass('has-error');\n\t\t\tlabel.remove();\n\t\t},\n\t\terrorPlacement : function(error, element) {\n\t\t\telement.parent('div').append(error);\n\t\t},\n\t\tsubmitHandler : function(form) {\n\t\t\t$.post(base_url + \"/jobgroup/update\",  $(\"#updateModal .form\").serialize(), function(data, status) {\n\t\t\t\tif (data.code == \"200\") {\n\t\t\t\t\t$('#updateModal').modal('hide');\n\n\t\t\t\t\tlayer.open({\n\t\t\t\t\t\ttitle: I18n.system_tips ,\n                        btn: [ I18n.system_ok ],\n\t\t\t\t\t\tcontent: I18n.system_update_suc ,\n\t\t\t\t\t\ticon: '1',\n\t\t\t\t\t\tend: function(layero, index){\n\t\t\t\t\t\t\tjobGroupTable.fnDraw();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tlayer.open({\n\t\t\t\t\t\ttitle: I18n.system_tips,\n                        btn: [ I18n.system_ok ],\n\t\t\t\t\t\tcontent: (data.msg || I18n.system_update_fail  ),\n\t\t\t\t\t\ticon: '2'\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t});\n\t$(\"#updateModal\").on('hide.bs.modal', function () {\n\t\t$(\"#updateModal .form\")[0].reset();\n\t\taddModalValidate.resetForm();\n\t\t$(\"#updateModal .form .form-group\").removeClass(\"has-error\");\n\t});\n\n\t\n});\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/jobinfo.index.1.js",
    "content": "$(function() {\n\n\t// init date tables\n\tvar jobTable = $(\"#job_list\").dataTable({\n\t\t\"deferRender\": true,\n\t\t\"processing\" : true,\n\t    \"serverSide\": true,\n\t\t\"ajax\": {\n\t\t\turl: base_url + \"/jobinfo/pageList\",\n\t\t\ttype:\"post\",\n\t        data : function ( d ) {\n\t        \tvar obj = {};\n\t        \tobj.jobGroup = $('#jobGroup').val();\n                obj.triggerStatus = $('#triggerStatus').val();\n                obj.jobDesc = $('#jobDesc').val();\n\t        \tobj.executorHandler = $('#executorHandler').val();\n                obj.author = $('#author').val();\n\t        \tobj.start = d.start;\n\t        \tobj.length = d.length;\n                return obj;\n            }\n\t    },\n\t    \"searching\": false,\n\t    \"ordering\": false,\n\t    //\"scrollX\": true,\t// scroll x，close self-adaption\n\t    \"columns\": [\n\t                {\n\t                \t\"data\": 'id',\n\t\t\t\t\t\t\"bSortable\": false,\n\t\t\t\t\t\t\"visible\" : true,\n\t\t\t\t\t\t\"width\":'7%'\n\t\t\t\t\t},\n\t                {\n\t                \t\"data\": 'jobGroup',\n\t                \t\"visible\" : false,\n\t                \t\"render\": function ( data, type, row ) {\n\t            \t\t\tvar groupMenu = $(\"#jobGroup\").find(\"option\");\n\t            \t\t\tfor ( var index in $(\"#jobGroup\").find(\"option\")) {\n\t            \t\t\t\tif ($(groupMenu[index]).attr('value') == data) {\n\t\t\t\t\t\t\t\t\treturn $(groupMenu[index]).html();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t            \t\t\treturn data;\n\t            \t\t}\n            \t\t},\n\t                {\n\t                \t\"data\": 'jobDesc',\n\t\t\t\t\t\t\"visible\" : true,\n\t\t\t\t\t\t\"width\":'25%'\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"data\": 'scheduleType',\n\t\t\t\t\t\t\"visible\" : true,\n\t\t\t\t\t\t\"width\":'13%',\n\t\t\t\t\t\t\"render\": function ( data, type, row ) {\n\t\t\t\t\t\t\tif (row.scheduleConf) {\n\t\t\t\t\t\t\t\treturn row.scheduleType + '：'+ row.scheduleConf;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\treturn row.scheduleType;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"data\": 'glueType',\n\t\t\t\t\t\t\"width\":'25%',\n\t\t\t\t\t\t\"visible\" : true,\n\t\t\t\t\t\t\"render\": function ( data, type, row ) {\n\t\t\t\t\t\t\tvar glueTypeTitle = findGlueTypeTitle(row.glueType);\n                            if (row.executorHandler) {\n                                return glueTypeTitle +\"：\" + row.executorHandler;\n                            } else {\n                                return glueTypeTitle;\n                            }\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t                { \"data\": 'executorParam', \"visible\" : false},\n\t                {\n\t                \t\"data\": 'addTime',\n\t                \t\"visible\" : false,\n\t                \t\"render\": function ( data, type, row ) {\n\t                \t\treturn data?moment(new Date(data)).format(\"YYYY-MM-DD HH:mm:ss\"):\"\";\n\t                \t}\n\t                },\n\t                {\n\t                \t\"data\": 'updateTime',\n\t                \t\"visible\" : false,\n\t                \t\"render\": function ( data, type, row ) {\n\t                \t\treturn data?moment(new Date(data)).format(\"YYYY-MM-DD HH:mm:ss\"):\"\";\n\t                \t}\n\t                },\n\t                { \"data\": 'author', \"visible\" : true, \"width\":'10%'},\n\t                { \"data\": 'alarmEmail', \"visible\" : false},\n\t                {\n\t                \t\"data\": 'triggerStatus',\n\t\t\t\t\t\t\"width\":'10%',\n\t                \t\"visible\" : true,\n\t                \t\"render\": function ( data, type, row ) {\n                            // status\n                            if (1 == data) {\n                                return '<small class=\"label label-success\" >RUNNING</small>';\n                            } else {\n                                return '<small class=\"label label-default\" >STOP</small>';\n                            }\n\t                \t\treturn data;\n\t                \t}\n\t                },\n\t                {\n\t\t\t\t\t\t\"data\": I18n.system_opt ,\n\t\t\t\t\t\t\"width\":'10%',\n\t                \t\"render\": function ( data, type, row ) {\n\t                \t\treturn function(){\n\n                                // status\n                                var start_stop_div = \"\";\n                                if (1 == row.triggerStatus ) {\n                                    start_stop_div = '<li><a href=\"javascript:void(0);\" class=\"job_operate\" _type=\"job_pause\" >'+ I18n.jobinfo_opt_stop +'</a></li>\\n';\n                                } else {\n                                    start_stop_div = '<li><a href=\"javascript:void(0);\" class=\"job_operate\" _type=\"job_resume\" >'+ I18n.jobinfo_opt_start +'</a></li>\\n';\n                                }\n\n                                // job_next_time_html\n\t\t\t\t\t\t\t\tvar job_next_time_html = '';\n\t\t\t\t\t\t\t\tif (row.scheduleType == 'CRON' || row.scheduleType == 'FIX_RATE') {\n\t\t\t\t\t\t\t\t\tjob_next_time_html = '<li><a href=\"javascript:void(0);\" class=\"job_next_time\" >' + I18n.jobinfo_opt_next_time + '</a></li>\\n';\n\t\t\t\t\t\t\t\t}\n\n                                // log url\n                                var logHref = base_url +'/joblog?jobId='+ row.id;\n\n                                // code url\n                                var codeBtn = \"\";\n                                if ('BEAN' != row.glueType) {\n                                    var codeUrl = base_url +'/jobcode?jobId='+ row.id;\n                                    codeBtn = '<li><a href=\"'+ codeUrl +'\" target=\"_blank\" >GLUE IDE</a></li>\\n';\n                                    codeBtn += '<li class=\"divider\"></li>\\n';\n                                }\n\n                                // data\n                                tableData['key'+row.id] = row;\n\n                                // opt\n                                var html = '<div class=\"btn-group\">\\n' +\n                                    '     <button type=\"button\" class=\"btn btn-primary btn-sm\">'+ I18n.system_opt +'</button>\\n' +\n                                    '     <button type=\"button\" class=\"btn btn-primary btn-sm dropdown-toggle\" data-toggle=\"dropdown\">\\n' +\n                                    '       <span class=\"caret\"></span>\\n' +\n                                    '       <span class=\"sr-only\">Toggle Dropdown</span>\\n' +\n                                    '     </button>\\n' +\n                                    '     <ul class=\"dropdown-menu\" role=\"menu\" _id=\"'+ row.id +'\" >\\n' +\n                                    '       <li><a href=\"javascript:void(0);\" class=\"job_trigger\" >'+ I18n.jobinfo_opt_run +'</a></li>\\n' +\n                                    '       <li><a href=\"'+ logHref +'\">'+ I18n.jobinfo_opt_log +'</a></li>\\n' +\n                                    '       <li><a href=\"javascript:void(0);\" class=\"job_registryinfo\" >' + I18n.jobinfo_opt_registryinfo + '</a></li>\\n' +\n\t\t\t\t\t\t\t\t\tjob_next_time_html +\n                                    '       <li class=\"divider\"></li>\\n' +\n                                    codeBtn +\n                                    start_stop_div +\n                                    '       <li><a href=\"javascript:void(0);\" class=\"update\" >'+ I18n.system_opt_edit +'</a></li>\\n' +\n                                    '       <li><a href=\"javascript:void(0);\" class=\"job_operate\" _type=\"job_del\" >'+ I18n.system_opt_del +'</a></li>\\n' +\n\t\t\t\t\t\t\t\t\t'       <li><a href=\"javascript:void(0);\" class=\"job_copy\" >'+ I18n.system_opt_copy +'</a></li>\\n' +\n                                    '     </ul>\\n' +\n                                    '   </div>';\n\n\t                \t\t\treturn html;\n\t\t\t\t\t\t\t};\n\t                \t}\n\t                }\n\t            ],\n\t\t\"language\" : {\n\t\t\t\"sProcessing\" : I18n.dataTable_sProcessing ,\n\t\t\t\"sLengthMenu\" : I18n.dataTable_sLengthMenu ,\n\t\t\t\"sZeroRecords\" : I18n.dataTable_sZeroRecords ,\n\t\t\t\"sInfo\" : I18n.dataTable_sInfo ,\n\t\t\t\"sInfoEmpty\" : I18n.dataTable_sInfoEmpty ,\n\t\t\t\"sInfoFiltered\" : I18n.dataTable_sInfoFiltered ,\n\t\t\t\"sInfoPostFix\" : \"\",\n\t\t\t\"sSearch\" : I18n.dataTable_sSearch ,\n\t\t\t\"sUrl\" : \"\",\n\t\t\t\"sEmptyTable\" : I18n.dataTable_sEmptyTable ,\n\t\t\t\"sLoadingRecords\" : I18n.dataTable_sLoadingRecords ,\n\t\t\t\"sInfoThousands\" : \",\",\n\t\t\t\"oPaginate\" : {\n\t\t\t\t\"sFirst\" : I18n.dataTable_sFirst ,\n\t\t\t\t\"sPrevious\" : I18n.dataTable_sPrevious ,\n\t\t\t\t\"sNext\" : I18n.dataTable_sNext ,\n\t\t\t\t\"sLast\" : I18n.dataTable_sLast\n\t\t\t},\n\t\t\t\"oAria\" : {\n\t\t\t\t\"sSortAscending\" : I18n.dataTable_sSortAscending ,\n\t\t\t\t\"sSortDescending\" : I18n.dataTable_sSortDescending\n\t\t\t}\n\t\t}\n\t});\n\n    // table data\n    var tableData = {};\n\n\t// search btn\n\t$('#searchBtn').on('click', function(){\n\t\tjobTable.fnDraw();\n\t});\n\n\t// jobGroup change\n\t$('#jobGroup').on('change', function(){\n        //reload\n        var jobGroup = $('#jobGroup').val();\n        window.location.href = base_url + \"/jobinfo?jobGroup=\" + jobGroup;\n    });\n\n\t// job operate\n\t$(\"#job_list\").on('click', '.job_operate',function() {\n\t\tvar typeName;\n\t\tvar url;\n\t\tvar needFresh = false;\n\n\t\tvar type = $(this).attr(\"_type\");\n\t\tif (\"job_pause\" == type) {\n\t\t\ttypeName = I18n.jobinfo_opt_stop ;\n\t\t\turl = base_url + \"/jobinfo/stop\";\n\t\t\tneedFresh = true;\n\t\t} else if (\"job_resume\" == type) {\n\t\t\ttypeName = I18n.jobinfo_opt_start ;\n\t\t\turl = base_url + \"/jobinfo/start\";\n\t\t\tneedFresh = true;\n\t\t} else if (\"job_del\" == type) {\n\t\t\ttypeName = I18n.system_opt_del ;\n\t\t\turl = base_url + \"/jobinfo/remove\";\n\t\t\tneedFresh = true;\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\n\t\tvar id = $(this).parents('ul').attr(\"_id\");\n\n\t\tlayer.confirm( I18n.system_ok + typeName + '?', {\n\t\t\ticon: 3,\n\t\t\ttitle: I18n.system_tips ,\n            btn: [ I18n.system_ok, I18n.system_cancel ]\n\t\t}, function(index){\n\t\t\tlayer.close(index);\n\n\t\t\t$.ajax({\n\t\t\t\ttype : 'POST',\n\t\t\t\turl : url,\n\t\t\t\tdata : {\n\t\t\t\t\t\"id\" : id\n\t\t\t\t},\n\t\t\t\tdataType : \"json\",\n\t\t\t\tsuccess : function(data){\n\t\t\t\t\tif (data.code == 200) {\n                        layer.msg( typeName + I18n.system_success );\n                        if (needFresh) {\n                            //window.location.reload();\n                            jobTable.fnDraw(false);\n                        }\n\t\t\t\t\t} else {\n                        layer.msg( data.msg || typeName + I18n.system_fail );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t});\n\n    // job trigger\n    $(\"#job_list\").on('click', '.job_trigger',function() {\n        var id = $(this).parents('ul').attr(\"_id\");\n        var row = tableData['key'+id];\n\n        $(\"#jobTriggerModal .form input[name='id']\").val( row.id );\n        $(\"#jobTriggerModal .form textarea[name='executorParam']\").val( row.executorParam );\n\n        $('#jobTriggerModal').modal({backdrop: false, keyboard: false}).modal('show');\n    });\n    $(\"#jobTriggerModal .ok\").on('click',function() {\n        $.ajax({\n            type : 'POST',\n            url : base_url + \"/jobinfo/trigger\",\n            data : {\n                \"id\" : $(\"#jobTriggerModal .form input[name='id']\").val(),\n                \"executorParam\" : $(\"#jobTriggerModal .textarea[name='executorParam']\").val(),\n\t\t\t\t\"addressList\" : $(\"#jobTriggerModal .textarea[name='addressList']\").val()\n            },\n            dataType : \"json\",\n            success : function(data){\n                if (data.code == 200) {\n                    $('#jobTriggerModal').modal('hide');\n\n                    layer.msg( I18n.jobinfo_opt_run + I18n.system_success );\n                } else {\n                    layer.msg( data.msg || I18n.jobinfo_opt_run + I18n.system_fail );\n                }\n            }\n        });\n    });\n    $(\"#jobTriggerModal\").on('hide.bs.modal', function () {\n        $(\"#jobTriggerModal .form\")[0].reset();\n    });\n\n\n    // job registryinfo\n    $(\"#job_list\").on('click', '.job_registryinfo',function() {\n        var id = $(this).parents('ul').attr(\"_id\");\n        var row = tableData['key'+id];\n\n        var jobGroup = row.jobGroup;\n\n        $.ajax({\n            type : 'POST',\n            url : base_url + \"/jobgroup/loadById\",\n            data : {\n                \"id\" : jobGroup\n            },\n            dataType : \"json\",\n            success : function(data){\n\n                var html = '<div>';\n                if (data.code == 200 && data.content.registryList) {\n                    for (var index in data.content.registryList) {\n                        html += (parseInt(index)+1) + '. <span class=\"badge bg-green\" >' + data.content.registryList[index] + '</span><br>';\n                    }\n                }\n                html += '</div>';\n\n                layer.open({\n                    title: I18n.jobinfo_opt_registryinfo ,\n                    btn: [ I18n.system_ok ],\n                    content: html\n                });\n\n            }\n        });\n\n    });\n\n    // job_next_time\n    $(\"#job_list\").on('click', '.job_next_time',function() {\n        var id = $(this).parents('ul').attr(\"_id\");\n        var row = tableData['key'+id];\n\n        $.ajax({\n            type : 'POST',\n            url : base_url + \"/jobinfo/nextTriggerTime\",\n            data : {\n                \"scheduleType\" : row.scheduleType,\n\t\t\t\t\"scheduleConf\" : row.scheduleConf\n            },\n            dataType : \"json\",\n            success : function(data){\n\n            \tif (data.code != 200) {\n                    layer.open({\n                        title: I18n.jobinfo_opt_next_time ,\n                        btn: [ I18n.system_ok ],\n                        content: data.msg\n                    });\n\t\t\t\t} else {\n                    var html = '<center>';\n                    if (data.code == 200 && data.content) {\n                        for (var index in data.content) {\n                            html += '<span>' + data.content[index] + '</span><br>';\n                        }\n                    }\n                    html += '</center>';\n\n                    layer.open({\n                        title: I18n.jobinfo_opt_next_time ,\n                        btn: [ I18n.system_ok ],\n                        content: html\n                    });\n\t\t\t\t}\n\n            }\n        });\n\n    });\n\n\t// add\n\t$(\".add\").click(function(){\n\n\t\t// init-cronGen\n        $(\"#addModal .form input[name='schedule_conf_CRON']\").show().siblings().remove();\n        $(\"#addModal .form input[name='schedule_conf_CRON']\").cronGen({});\n\n\t\t// 》init scheduleType\n\t\t$(\"#updateModal .form select[name=scheduleType]\").change();\n\n\t\t// 》init glueType\n\t\t$(\"#updateModal .form select[name=glueType]\").change();\n\n\t\t$('#addModal').modal({backdrop: false, keyboard: false}).modal('show');\n\t});\n\tvar addModalValidate = $(\"#addModal .form\").validate({\n\t\terrorElement : 'span',\n        errorClass : 'help-block',\n        focusInvalid : true,\n        rules : {\n\t\t\tjobDesc : {\n\t\t\t\trequired : true,\n\t\t\t\tmaxlength: 50\n\t\t\t},\n\t\t\tauthor : {\n\t\t\t\trequired : true\n\t\t\t}/*,\n            executorTimeout : {\n                digits:true\n            },\n            executorFailRetryCount : {\n                digits:true\n            }*/\n        },\n        messages : {\n            jobDesc : {\n            \trequired : I18n.system_please_input + I18n.jobinfo_field_jobdesc\n            },\n            author : {\n            \trequired : I18n.system_please_input + I18n.jobinfo_field_author\n            }/*,\n            executorTimeout : {\n                digits: I18n.system_please_input + I18n.system_digits\n            },\n            executorFailRetryCount : {\n                digits: I18n.system_please_input + I18n.system_digits\n            }*/\n        },\n\t\thighlight : function(element) {\n            $(element).closest('.form-group').addClass('has-error');\n        },\n        success : function(label) {\n            label.closest('.form-group').removeClass('has-error');\n            label.remove();\n        },\n        errorPlacement : function(error, element) {\n            element.parent('div').append(error);\n        },\n        submitHandler : function(form) {\n\n\t\t\t// process executorTimeout+executorFailRetryCount\n            var executorTimeout = $(\"#addModal .form input[name='executorTimeout']\").val();\n            if(!/^\\d+$/.test(executorTimeout)) {\n                executorTimeout = 0;\n\t\t\t}\n            $(\"#addModal .form input[name='executorTimeout']\").val(executorTimeout);\n            var executorFailRetryCount = $(\"#addModal .form input[name='executorFailRetryCount']\").val();\n            if(!/^\\d+$/.test(executorFailRetryCount)) {\n                executorFailRetryCount = 0;\n            }\n            $(\"#addModal .form input[name='executorFailRetryCount']\").val(executorFailRetryCount);\n\n            // process schedule_conf\n\t\t\tvar scheduleType = $(\"#addModal .form select[name='scheduleType']\").val();\n\t\t\tvar scheduleConf;\n\t\t\tif (scheduleType == 'CRON') {\n\t\t\t\tscheduleConf = $(\"#addModal .form input[name='cronGen_display']\").val();\n\t\t\t} else if (scheduleType == 'FIX_RATE') {\n\t\t\t\tscheduleConf = $(\"#addModal .form input[name='schedule_conf_FIX_RATE']\").val();\n\t\t\t} else if (scheduleType == 'FIX_DELAY') {\n\t\t\t\tscheduleConf = $(\"#addModal .form input[name='schedule_conf_FIX_DELAY']\").val();\n\t\t\t}\n\t\t\t$(\"#addModal .form input[name='scheduleConf']\").val( scheduleConf );\n\n        \t$.post(base_url + \"/jobinfo/add\",  $(\"#addModal .form\").serialize(), function(data, status) {\n    \t\t\tif (data.code == \"200\") {\n\t\t\t\t\t$('#addModal').modal('hide');\n\t\t\t\t\tlayer.open({\n\t\t\t\t\t\ttitle: I18n.system_tips ,\n                        btn: [ I18n.system_ok ],\n\t\t\t\t\t\tcontent: I18n.system_add_suc ,\n\t\t\t\t\t\ticon: '1',\n\t\t\t\t\t\tend: function(layero, index){\n\t\t\t\t\t\t\tjobTable.fnDraw();\n\t\t\t\t\t\t\t//window.location.reload();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n    \t\t\t} else {\n\t\t\t\t\tlayer.open({\n\t\t\t\t\t\ttitle: I18n.system_tips ,\n                        btn: [ I18n.system_ok ],\n\t\t\t\t\t\tcontent: (data.msg || I18n.system_add_fail),\n\t\t\t\t\t\ticon: '2'\n\t\t\t\t\t});\n    \t\t\t}\n    \t\t});\n\t\t}\n\t});\n\t$(\"#addModal\").on('hide.bs.modal', function () {\n        addModalValidate.resetForm();\n\t\t$(\"#addModal .form\")[0].reset();\n\t\t$(\"#addModal .form .form-group\").removeClass(\"has-error\");\n\t\t$(\".remote_panel\").show();\t// remote\n\n\t\t$(\"#addModal .form input[name='executorHandler']\").removeAttr(\"readonly\");\n\t});\n\n\t// scheduleType change\n\t$(\".scheduleType\").change(function(){\n\t\tvar scheduleType = $(this).val();\n\t\t$(this).parents(\"form\").find(\".schedule_conf\").hide();\n\t\t$(this).parents(\"form\").find(\".schedule_conf_\" + scheduleType).show();\n\n\t});\n\n    // glueType change\n    $(\".glueType\").change(function(){\n\t\t// executorHandler\n        var $executorHandler = $(this).parents(\"form\").find(\"input[name='executorHandler']\");\n        var glueType = $(this).val();\n        if ('BEAN' != glueType) {\n            $executorHandler.val(\"\");\n            $executorHandler.attr(\"readonly\",\"readonly\");\n        } else {\n            $executorHandler.removeAttr(\"readonly\");\n        }\n    });\n\n\t$(\"#addModal .glueType\").change(function(){\n\t\t// glueSource\n\t\tvar glueType = $(this).val();\n\t\tif ('GLUE_GROOVY'==glueType){\n\t\t\t$(\"#addModal .form textarea[name='glueSource']\").val( $(\"#addModal .form .glueSource_java\").val() );\n\t\t} else if ('GLUE_SHELL'==glueType){\n\t\t\t$(\"#addModal .form textarea[name='glueSource']\").val( $(\"#addModal .form .glueSource_shell\").val() );\n\t\t} else if ('GLUE_PYTHON'==glueType){\n\t\t\t$(\"#addModal .form textarea[name='glueSource']\").val( $(\"#addModal .form .glueSource_python\").val() );\n\t\t} else if ('GLUE_PHP'==glueType){\n            $(\"#addModal .form textarea[name='glueSource']\").val( $(\"#addModal .form .glueSource_php\").val() );\n        } else if ('GLUE_NODEJS'==glueType){\n\t\t\t$(\"#addModal .form textarea[name='glueSource']\").val( $(\"#addModal .form .glueSource_nodejs\").val() );\n\t\t} else if ('GLUE_POWERSHELL'==glueType){\n            $(\"#addModal .form textarea[name='glueSource']\").val( $(\"#addModal .form .glueSource_powershell\").val() );\n        } else {\n            $(\"#addModal .form textarea[name='glueSource']\").val(\"\");\n\t\t}\n\t});\n\n\t// update\n\t$(\"#job_list\").on('click', '.update',function() {\n\n        var id = $(this).parents('ul').attr(\"_id\");\n        var row = tableData['key'+id];\n\n\t\t// fill base\n\t\t$(\"#updateModal .form input[name='id']\").val( row.id );\n\t\t$('#updateModal .form select[name=jobGroup] option[value='+ row.jobGroup +']').prop('selected', true);\n\t\t$(\"#updateModal .form input[name='jobDesc']\").val( row.jobDesc );\n\t\t$(\"#updateModal .form input[name='author']\").val( row.author );\n\t\t$(\"#updateModal .form input[name='alarmEmail']\").val( row.alarmEmail );\n\n\t\t// fill trigger\n\t\t$('#updateModal .form select[name=scheduleType] option[value='+ row.scheduleType +']').prop('selected', true);\n\t\t$(\"#updateModal .form input[name='scheduleConf']\").val( row.scheduleConf );\n\t\tif (row.scheduleType == 'CRON') {\n\t\t\t$(\"#updateModal .form input[name='schedule_conf_CRON']\").val( row.scheduleConf );\n\t\t} else if (row.scheduleType == 'FIX_RATE') {\n\t\t\t$(\"#updateModal .form input[name='schedule_conf_FIX_RATE']\").val( row.scheduleConf );\n\t\t} else if (row.scheduleType == 'FIX_DELAY') {\n\t\t\t$(\"#updateModal .form input[name='schedule_conf_FIX_DELAY']\").val( row.scheduleConf );\n\t\t}\n\n\t\t// 》init scheduleType\n\t\t$(\"#updateModal .form select[name=scheduleType]\").change();\n\n\t\t// fill job\n\t\t$('#updateModal .form select[name=glueType] option[value='+ row.glueType +']').prop('selected', true);\n\t\t$(\"#updateModal .form input[name='executorHandler']\").val( row.executorHandler );\n\t\t$(\"#updateModal .form textarea[name='executorParam']\").val( row.executorParam );\n\n\t\t// 》init glueType\n\t\t$(\"#updateModal .form select[name=glueType]\").change();\n\n\t\t// 》init-cronGen\n\t\t$(\"#updateModal .form input[name='schedule_conf_CRON']\").show().siblings().remove();\n\t\t$(\"#updateModal .form input[name='schedule_conf_CRON']\").cronGen({});\n\n\t\t// fill advanced\n\t\t$('#updateModal .form select[name=executorRouteStrategy] option[value='+ row.executorRouteStrategy +']').prop('selected', true);\n\t\t$(\"#updateModal .form input[name='childJobId']\").val( row.childJobId );\n\t\t$('#updateModal .form select[name=misfireStrategy] option[value='+ row.misfireStrategy +']').prop('selected', true);\n\t\t$('#updateModal .form select[name=executorBlockStrategy] option[value='+ row.executorBlockStrategy +']').prop('selected', true);\n\t\t$(\"#updateModal .form input[name='executorTimeout']\").val( row.executorTimeout );\n        $(\"#updateModal .form input[name='executorFailRetryCount']\").val( row.executorFailRetryCount );\n\n\t\t// show\n\t\t$('#updateModal').modal({backdrop: false, keyboard: false}).modal('show');\n\t});\n\tvar updateModalValidate = $(\"#updateModal .form\").validate({\n\t\terrorElement : 'span',\n        errorClass : 'help-block',\n        focusInvalid : true,\n\n\t\trules : {\n\t\t\tjobDesc : {\n\t\t\t\trequired : true,\n\t\t\t\tmaxlength: 50\n\t\t\t},\n\t\t\tauthor : {\n\t\t\t\trequired : true\n\t\t\t}\n\t\t},\n\t\tmessages : {\n\t\t\tjobDesc : {\n                required : I18n.system_please_input + I18n.jobinfo_field_jobdesc\n\t\t\t},\n\t\t\tauthor : {\n\t\t\t\trequired : I18n.system_please_input + I18n.jobinfo_field_author\n\t\t\t}\n\t\t},\n\t\thighlight : function(element) {\n            $(element).closest('.form-group').addClass('has-error');\n        },\n        success : function(label) {\n            label.closest('.form-group').removeClass('has-error');\n            label.remove();\n        },\n        errorPlacement : function(error, element) {\n            element.parent('div').append(error);\n        },\n        submitHandler : function(form) {\n\n            // process executorTimeout + executorFailRetryCount\n            var executorTimeout = $(\"#updateModal .form input[name='executorTimeout']\").val();\n            if(!/^\\d+$/.test(executorTimeout)) {\n                executorTimeout = 0;\n            }\n            $(\"#updateModal .form input[name='executorTimeout']\").val(executorTimeout);\n            var executorFailRetryCount = $(\"#updateModal .form input[name='executorFailRetryCount']\").val();\n            if(!/^\\d+$/.test(executorFailRetryCount)) {\n                executorFailRetryCount = 0;\n            }\n            $(\"#updateModal .form input[name='executorFailRetryCount']\").val(executorFailRetryCount);\n\n\n\t\t\t// process schedule_conf\n\t\t\tvar scheduleType = $(\"#updateModal .form select[name='scheduleType']\").val();\n\t\t\tvar scheduleConf;\n\t\t\tif (scheduleType == 'CRON') {\n\t\t\t\tscheduleConf = $(\"#updateModal .form input[name='cronGen_display']\").val();\n\t\t\t} else if (scheduleType == 'FIX_RATE') {\n\t\t\t\tscheduleConf = $(\"#updateModal .form input[name='schedule_conf_FIX_RATE']\").val();\n\t\t\t} else if (scheduleType == 'FIX_DELAY') {\n\t\t\t\tscheduleConf = $(\"#updateModal .form input[name='schedule_conf_FIX_DELAY']\").val();\n\t\t\t}\n\t\t\t$(\"#updateModal .form input[name='scheduleConf']\").val( scheduleConf );\n\n\t\t\t// post\n    \t\t$.post(base_url + \"/jobinfo/update\", $(\"#updateModal .form\").serialize(), function(data, status) {\n    \t\t\tif (data.code == \"200\") {\n\t\t\t\t\t$('#updateModal').modal('hide');\n\t\t\t\t\tlayer.open({\n\t\t\t\t\t\ttitle: I18n.system_tips ,\n                        btn: [ I18n.system_ok ],\n\t\t\t\t\t\tcontent: I18n.system_update_suc ,\n\t\t\t\t\t\ticon: '1',\n\t\t\t\t\t\tend: function(layero, index){\n\t\t\t\t\t\t\t//window.location.reload();\n\t\t\t\t\t\t\tjobTable.fnDraw();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n    \t\t\t} else {\n\t\t\t\t\tlayer.open({\n\t\t\t\t\t\ttitle: I18n.system_tips ,\n                        btn: [ I18n.system_ok ],\n\t\t\t\t\t\tcontent: (data.msg || I18n.system_update_fail ),\n\t\t\t\t\t\ticon: '2'\n\t\t\t\t\t});\n    \t\t\t}\n    \t\t});\n\t\t}\n\t});\n\t$(\"#updateModal\").on('hide.bs.modal', function () {\n        updateModalValidate.resetForm();\n        $(\"#updateModal .form\")[0].reset();\n        $(\"#updateModal .form .form-group\").removeClass(\"has-error\");\n\t});\n\n    /**\n\t * find title by name, GlueType\n     */\n\tfunction findGlueTypeTitle(glueType) {\n\t\tvar glueTypeTitle;\n        $(\"#addModal .form select[name=glueType] option\").each(function () {\n            var name = $(this).val();\n            var title = $(this).text();\n            if (glueType == name) {\n                glueTypeTitle = title;\n                return false\n            }\n        });\n        return glueTypeTitle;\n    }\n\n    // job_copy\n\t$(\"#job_list\").on('click', '.job_copy',function() {\n\n\t\tvar id = $(this).parents('ul').attr(\"_id\");\n\t\tvar row = tableData['key'+id];\n\n\t\t// fill base\n\t\t$('#addModal .form select[name=jobGroup] option[value='+ row.jobGroup +']').prop('selected', true);\n\t\t$(\"#addModal .form input[name='jobDesc']\").val( row.jobDesc );\n\t\t$(\"#addModal .form input[name='author']\").val( row.author );\n\t\t$(\"#addModal .form input[name='alarmEmail']\").val( row.alarmEmail );\n\n\t\t// fill trigger\n\t\t$('#addModal .form select[name=scheduleType] option[value='+ row.scheduleType +']').prop('selected', true);\n\t\t$(\"#addModal .form input[name='scheduleConf']\").val( row.scheduleConf );\n\t\tif (row.scheduleType == 'CRON') {\n\t\t\t$(\"#addModal .form input[name='schedule_conf_CRON']\").val( row.scheduleConf );\n\t\t} else if (row.scheduleType == 'FIX_RATE') {\n\t\t\t$(\"#addModal .form input[name='schedule_conf_FIX_RATE']\").val( row.scheduleConf );\n\t\t} else if (row.scheduleType == 'FIX_DELAY') {\n\t\t\t$(\"#addModal .form input[name='schedule_conf_FIX_DELAY']\").val( row.scheduleConf );\n\t\t}\n\n\t\t// 》init scheduleType\n\t\t$(\"#addModal .form select[name=scheduleType]\").change();\n\n\t\t// fill job\n\t\t$('#addModal .form select[name=glueType] option[value='+ row.glueType +']').prop('selected', true);\n\t\t$(\"#addModal .form input[name='executorHandler']\").val( row.executorHandler );\n\t\t$(\"#addModal .form textarea[name='executorParam']\").val( row.executorParam );\n\n\t\t// 》init glueType\n\t\t$(\"#addModal .form select[name=glueType]\").change();\n\n\t\t// 》init-cronGen\n\t\t$(\"#addModal .form input[name='schedule_conf_CRON']\").show().siblings().remove();\n\t\t$(\"#addModal .form input[name='schedule_conf_CRON']\").cronGen({});\n\n\t\t// fill advanced\n\t\t$('#addModal .form select[name=executorRouteStrategy] option[value='+ row.executorRouteStrategy +']').prop('selected', true);\n\t\t$(\"#addModal .form input[name='childJobId']\").val( row.childJobId );\n\t\t$('#addModal .form select[name=misfireStrategy] option[value='+ row.misfireStrategy +']').prop('selected', true);\n\t\t$('#addModal .form select[name=executorBlockStrategy] option[value='+ row.executorBlockStrategy +']').prop('selected', true);\n\t\t$(\"#addModal .form input[name='executorTimeout']\").val( row.executorTimeout );\n\t\t$(\"#addModal .form input[name='executorFailRetryCount']\").val( row.executorFailRetryCount );\n\n\t\t// show\n\t\t$('#addModal').modal({backdrop: false, keyboard: false}).modal('show');\n\t});\n\n});\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/joblog.detail.1.js",
    "content": "$(function() {\n\n    // trigger fail, end\n    if ( !(triggerCode == 200 || handleCode != 0) ) {\n        $('#logConsoleRunning').hide();\n        $('#logConsole').append('<span style=\"color: red;\">'+ I18n.joblog_rolling_log_triggerfail +'</span>');\n        return;\n    }\n\n    // pull log\n    var fromLineNum = 1;    // [from, to], start as 1\n    var pullFailCount = 0;\n    function pullLog() {\n        // pullFailCount, max=20\n        if (pullFailCount++ > 20) {\n            logRunStop('<span style=\"color: red;\">'+ I18n.joblog_rolling_log_failoften +'</span>');\n            return;\n        }\n\n        // load\n        console.log(\"pullLog, fromLineNum:\" + fromLineNum);\n\n        $.ajax({\n            type : 'POST',\n            async: false,   // sync, make log ordered\n            url : base_url + '/joblog/logDetailCat',\n            data : {\n                \"logId\":logId,\n                \"fromLineNum\":fromLineNum\n            },\n            dataType : \"json\",\n            success : function(data){\n\n                if (data.code == 200) {\n                    if (!data.content) {\n                        console.log('pullLog fail');\n                        return;\n                    }\n                    if (fromLineNum != data.content.fromLineNum) {\n                        console.log('pullLog fromLineNum not match');\n                        return;\n                    }\n                    if (fromLineNum > data.content.toLineNum ) {\n                        console.log('pullLog already line-end');\n\n                        // valid end\n                        if (data.content.end) {\n                            logRunStop('<br><span style=\"color: green;\">[Rolling Log Finish]</span>');\n                            return;\n                        }\n\n                        return;\n                    }\n\n                    // append content\n                    fromLineNum = data.content.toLineNum + 1;\n                    $('#logConsole').append(data.content.logContent);\n                    pullFailCount = 0;\n\n                    // scroll to bottom\n                    scrollTo(0, document.body.scrollHeight);        // $('#logConsolePre').scrollTop( document.body.scrollHeight + 300 );\n\n                } else {\n                    console.log('pullLog fail:'+data.msg);\n                }\n            }\n        });\n    }\n\n    // pull first page\n    pullLog();\n\n    // handler already callback, end\n    if (handleCode > 0) {\n        logRunStop('<br><span style=\"color: green;\">[Load Log Finish]</span>');\n        return;\n    }\n\n    // round until end\n    var logRun = setInterval(function () {\n        pullLog()\n    }, 3000);\n    function logRunStop(content){\n        $('#logConsoleRunning').hide();\n        logRun = window.clearInterval(logRun);\n        $('#logConsole').append(content);\n    }\n\n});\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/joblog.index.1.js",
    "content": "$(function() {\n\n\t// jobGroup change, job list init and select\n\t$(\"#jobGroup\").on(\"change\", function () {\n\t\tvar jobGroup = $(this).children('option:selected').val();\n\t\t$.ajax({\n\t\t\ttype : 'POST',\n            async: false,   // async, avoid js invoke pagelist before jobId data init\n\t\t\turl : base_url + '/joblog/getJobsByGroup',\n\t\t\tdata : {\"jobGroup\":jobGroup},\n\t\t\tdataType : \"json\",\n\t\t\tsuccess : function(data){\n\t\t\t\tif (data.code == 200) {\n\t\t\t\t\t$(\"#jobId\").html( '<option value=\"0\" >'+ I18n.system_all +'</option>' );\n\t\t\t\t\t$.each(data.content, function (n, value) {\n                        $(\"#jobId\").append('<option value=\"' + value.id + '\" >' + value.jobDesc + '</option>');\n                    });\n                    if ($(\"#jobId\").attr(\"paramVal\")){\n                        $(\"#jobId\").find(\"option[value='\" + $(\"#jobId\").attr(\"paramVal\") + \"']\").attr(\"selected\",true);\n                    }\n\t\t\t\t} else {\n\t\t\t\t\tlayer.open({\n\t\t\t\t\t\ttitle: I18n.system_tips ,\n                        btn: [ I18n.system_ok ],\n\t\t\t\t\t\tcontent: (data.msg || I18n.system_api_error ),\n\t\t\t\t\t\ticon: '2'\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t});\n\tif ($(\"#jobGroup\").attr(\"paramVal\")){\n\t\t$(\"#jobGroup\").find(\"option[value='\" + $(\"#jobGroup\").attr(\"paramVal\") + \"']\").attr(\"selected\",true);\n        $(\"#jobGroup\").change();\n\t}\n\n\t// filter Time\n    var rangesConf = {};\n    rangesConf[I18n.daterangepicker_ranges_recent_hour] = [moment().subtract(1, 'hours'), moment()];\n    rangesConf[I18n.daterangepicker_ranges_today] = [moment().startOf('day'), moment().endOf('day')];\n    rangesConf[I18n.daterangepicker_ranges_yesterday] = [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').endOf('day')];\n    rangesConf[I18n.daterangepicker_ranges_this_month] = [moment().startOf('month'), moment().endOf('month')];\n    rangesConf[I18n.daterangepicker_ranges_last_month] = [moment().subtract(1, 'months').startOf('month'), moment().subtract(1, 'months').endOf('month')];\n    rangesConf[I18n.daterangepicker_ranges_recent_week] = [moment().subtract(1, 'weeks').startOf('day'), moment().endOf('day')];\n    rangesConf[I18n.daterangepicker_ranges_recent_month] = [moment().subtract(1, 'months').startOf('day'), moment().endOf('day')];\n\n\t$('#filterTime').daterangepicker({\n        autoApply:false,\n        singleDatePicker:false,\n        showDropdowns:false,        // 是否显示年月选择条件\n\t\ttimePicker: true, \t\t\t// 是否显示小时和分钟选择条件\n\t\ttimePickerIncrement: 10, \t// 时间的增量，单位为分钟\n        timePicker24Hour : true,\n        opens : 'left', //日期选择框的弹出位置\n\t\tranges: rangesConf,\n        locale : {\n            format: 'YYYY-MM-DD HH:mm:ss',\n            separator : ' - ',\n            customRangeLabel : I18n.daterangepicker_custom_name ,\n            applyLabel : I18n.system_ok ,\n            cancelLabel : I18n.system_cancel ,\n            fromLabel : I18n.daterangepicker_custom_starttime ,\n            toLabel : I18n.daterangepicker_custom_endtime ,\n            daysOfWeek : I18n.daterangepicker_custom_daysofweek.split(',') ,        // '日', '一', '二', '三', '四', '五', '六'\n            monthNames : I18n.daterangepicker_custom_monthnames.split(',') ,        // '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'\n            firstDay : 1\n        },\n        startDate: rangesConf[I18n.daterangepicker_ranges_today][0],\n        endDate: rangesConf[I18n.daterangepicker_ranges_today][1]\n\t});\n\n\t// init date tables\n\tvar logTable = $(\"#joblog_list\").dataTable({\n\t\t\"deferRender\": true,\n\t\t\"processing\" : true, \n\t    \"serverSide\": true,\n\t\t\"ajax\": {\n\t        url: base_url + \"/joblog/pageList\" ,\n            type:\"post\",\n\t        data : function ( d ) {\n\t        \tvar obj = {};\n\t        \tobj.jobGroup = $('#jobGroup').val();\n\t        \tobj.jobId = $('#jobId').val();\n                obj.logStatus = $('#logStatus').val();\n\t\t\t\tobj.filterTime = $('#filterTime').val();\n\t        \tobj.start = d.start;\n\t        \tobj.length = d.length;\n                return obj;\n            }\n\t    },\n\t    \"searching\": false,\n\t    \"ordering\": false,\n\t    //\"scrollX\": false,\n\t    \"columns\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"data\": 'jobId',\n\t\t\t\t\t\t\"visible\" : true,\n                        \"width\":'10%',\n\t\t\t\t\t\t\"render\": function ( data, type, row ) {\n\n\t\t\t\t\t\t\tvar jobhandler = '';\n                            if (row.executorHandler) {\n                                jobhandler = \"<br>JobHandler：\" + row.executorHandler;\n                            }\n\n\t\t\t\t\t\t\tvar temp = '';\n\t\t\t\t\t\t\ttemp += I18n.joblog_field_executorAddress + '：' + (row.executorAddress?row.executorAddress:'');\n\t\t\t\t\t\t\ttemp += jobhandler;\n\t\t\t\t\t\t\ttemp += '<br>'+ I18n.jobinfo_field_executorparam +'：' + row.executorParam;\n\n\t\t\t\t\t\t\treturn '<a class=\"logTips\" href=\"javascript:;\" >'+ row.jobId +'<span style=\"display:none;\">'+ temp +'</span></a>';\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{ \"data\": 'jobGroup', \"visible\" : false},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"data\": 'triggerTime',\n                        \"width\":'20%',\n\t\t\t\t\t\t\"render\": function ( data, type, row ) {\n\t\t\t\t\t\t\treturn data?moment(data).format(\"YYYY-MM-DD HH:mm:ss\"):\"\";\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"data\": 'triggerCode',\n                        \"width\":'10%',\n\t\t\t\t\t\t\"render\": function ( data, type, row ) {\n\t\t\t\t\t\t\tvar html = data;\n\t\t\t\t\t\t\tif (data == 200) {\n\t\t\t\t\t\t\t\thtml = '<span style=\"color: green\">'+ I18n.system_success +'</span>';\n\t\t\t\t\t\t\t} else if (data == 500) {\n\t\t\t\t\t\t\t\thtml = '<span style=\"color: red\">'+ I18n.system_fail +'</span>';\n\t\t\t\t\t\t\t} else if (data == 0) {\n                                html = '';\n\t\t\t\t\t\t\t}\n                            return html;\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"data\": 'triggerMsg',\n                        \"width\":'10%',\n\t\t\t\t\t\t\"render\": function ( data, type, row ) {\n\t\t\t\t\t\t\treturn data?'<a class=\"logTips\" href=\"javascript:;\" >'+ I18n.system_show +'<span style=\"display:none;\">'+ data +'</span></a>':I18n.system_empty;\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t                { \n\t                \t\"data\": 'handleTime',\n                        \"width\":'20%',\n\t                \t\"render\": function ( data, type, row ) {\n\t                \t\treturn data?moment(data).format(\"YYYY-MM-DD HH:mm:ss\"):\"\";\n\t                \t}\n\t                },\n\t                {\n\t\t\t\t\t\t\"data\": 'handleCode',\n                        \"width\":'10%',\n\t\t\t\t\t\t\"render\": function ( data, type, row ) {\n                            var html = data;\n                            if (data == 200) {\n                                html = '<span style=\"color: green\">'+ I18n.joblog_handleCode_200 +'</span>';\n                            } else if (data == 500) {\n                                html = '<span style=\"color: red\">'+ I18n.joblog_handleCode_500 +'</span>';\n                            } else if (data == 502) {\n                                html = '<span style=\"color: red\">'+ I18n.joblog_handleCode_502 +'</span>';\n                            } else if (data == 0) {\n                                html = '';\n                            }\n                            return html;\n\t\t\t\t\t\t}\n\t                },\n\t                { \n\t                \t\"data\": 'handleMsg',\n                        \"width\":'10%',\n\t                \t\"render\": function ( data, type, row ) {\n\t                \t\treturn data?'<a class=\"logTips\" href=\"javascript:;\" >'+ I18n.system_show +'<span style=\"display:none;\">'+ data +'</span></a>':I18n.system_empty;\n\t                \t}\n\t                },\n\t                {\n\t\t\t\t\t\t\"data\": 'handleMsg' ,\n\t\t\t\t\t\t\"bSortable\": false,\n                        \"width\":'10%',\n\t                \t\"render\": function ( data, type, row ) {\n\t                \t\t// better support expression or string, not function\n\t                \t\treturn function () {\n\t\t                \t\tif (row.triggerCode == 200 || row.handleCode != 0){\n\n\t\t                \t\t\t/*var temp = '<a href=\"javascript:;\" class=\"logDetail\" _id=\"'+ row.id +'\">'+ I18n.joblog_rolling_log +'</a>';\n\t\t                \t\t\tif(row.handleCode == 0){\n\t\t                \t\t\t\ttemp += '<br><a href=\"javascript:;\" class=\"logKill\" _id=\"'+ row.id +'\" style=\"color: red;\" >'+ I18n.joblog_kill_log +'</a>';\n\t\t                \t\t\t}*/\n\t\t                \t\t\t//return temp;\n\n\t\t\t\t\t\t\t\t\tvar logKillDiv = '';\n\t\t\t\t\t\t\t\t\tif(row.handleCode == 0){\n\t\t\t\t\t\t\t\t\t\tlogKillDiv = '       <li class=\"divider\"></li>\\n' +\n\t\t\t\t\t\t\t\t\t\t\t'       <li><a href=\"javascript:void(0);\" class=\"logKill\" _id=\"'+ row.id +'\" >'+ I18n.joblog_kill_log +'</a></li>\\n';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tvar html = '<div class=\"btn-group\">\\n' +\n\t\t\t\t\t\t\t\t\t\t'     <button type=\"button\" class=\"btn btn-primary btn-sm\">'+ I18n.system_opt +'</button>\\n' +\n\t\t\t\t\t\t\t\t\t\t'     <button type=\"button\" class=\"btn btn-primary btn-sm dropdown-toggle\" data-toggle=\"dropdown\">\\n' +\n\t\t\t\t\t\t\t\t\t\t'       <span class=\"caret\"></span>\\n' +\n\t\t\t\t\t\t\t\t\t\t'       <span class=\"sr-only\">Toggle Dropdown</span>\\n' +\n\t\t\t\t\t\t\t\t\t\t'     </button>\\n' +\n\t\t\t\t\t\t\t\t\t\t'     <ul class=\"dropdown-menu\" role=\"menu\" _id=\"'+ row.id +'\" >\\n' +\n\t\t\t\t\t\t\t\t\t\t'       <li><a href=\"javascript:void(0);\" class=\"logDetail\" _id=\"'+ row.id +'\" >'+ I18n.joblog_rolling_log +'</a></li>\\n' +\n\t\t\t\t\t\t\t\t\t\tlogKillDiv +\n\t\t\t\t\t\t\t\t\t\t'     </ul>\\n' +\n\t\t\t\t\t\t\t\t\t\t'   </div>';\n\n\t\t                \t\t\treturn html;\n\t\t                \t\t}\n\t\t                \t\treturn null;\t\n\t                \t\t}\n\t                \t}\n\t                }\n\t            ],\n        \"language\" : {\n            \"sProcessing\" : I18n.dataTable_sProcessing ,\n            \"sLengthMenu\" : I18n.dataTable_sLengthMenu ,\n            \"sZeroRecords\" : I18n.dataTable_sZeroRecords ,\n            \"sInfo\" : I18n.dataTable_sInfo ,\n            \"sInfoEmpty\" : I18n.dataTable_sInfoEmpty ,\n            \"sInfoFiltered\" : I18n.dataTable_sInfoFiltered ,\n            \"sInfoPostFix\" : \"\",\n            \"sSearch\" : I18n.dataTable_sSearch ,\n            \"sUrl\" : \"\",\n            \"sEmptyTable\" : I18n.dataTable_sEmptyTable ,\n            \"sLoadingRecords\" : I18n.dataTable_sLoadingRecords ,\n            \"sInfoThousands\" : \",\",\n            \"oPaginate\" : {\n                \"sFirst\" : I18n.dataTable_sFirst ,\n                \"sPrevious\" : I18n.dataTable_sPrevious ,\n                \"sNext\" : I18n.dataTable_sNext ,\n                \"sLast\" : I18n.dataTable_sLast\n            },\n            \"oAria\" : {\n                \"sSortAscending\" : I18n.dataTable_sSortAscending ,\n                \"sSortDescending\" : I18n.dataTable_sSortDescending\n            }\n        }\n\t});\n    logTable.on('xhr.dt',function(e, settings, json, xhr) {\n        if (json.code && json.code != 200) {\n            layer.msg( json.msg || I18n.system_api_error );\n        }\n    });\n\t\n\t// logTips alert\n\t$('#joblog_list').on('click', '.logTips', function(){\n\t\tvar msg = $(this).find('span').html();\n\t\tComAlertTec.show(msg);\n\t});\n\t\n\t// search Btn\n\t$('#searchBtn').on('click', function(){\n\t\tlogTable.fnDraw();\n\t});\n\t\n\t// logDetail look\n\t$('#joblog_list').on('click', '.logDetail', function(){\n\t\tvar _id = $(this).attr('_id');\n\t\t\n\t\twindow.open(base_url + '/joblog/logDetailPage?id=' + _id);\n\t\treturn;\n\t});\n\n\t/**\n\t * log Kill\n\t */\n\t$('#joblog_list').on('click', '.logKill', function(){\n\t\tvar _id = $(this).attr('_id');\n\n        layer.confirm( (I18n.system_ok + I18n.joblog_kill_log + '?'), {\n        \ticon: 3,\n\t\t\ttitle: I18n.system_tips ,\n            btn: [ I18n.system_ok, I18n.system_cancel ]\n\t\t}, function(index){\n            layer.close(index);\n\n            $.ajax({\n                type : 'POST',\n                url : base_url + '/joblog/logKill',\n                data : {\"id\":_id},\n                dataType : \"json\",\n                success : function(data){\n                    if (data.code == 200) {\n                        layer.open({\n                            title: I18n.system_tips,\n                            btn: [ I18n.system_ok ],\n                            content: I18n.system_opt_suc ,\n                            icon: '1',\n                            end: function(layero, index){\n                                logTable.fnDraw();\n                            }\n                        });\n                    } else {\n                        layer.open({\n                            title: I18n.system_tips,\n                            btn: [ I18n.system_ok ],\n                            content: (data.msg || I18n.system_opt_fail ),\n                            icon: '2'\n                        });\n                    }\n                },\n            });\n        });\n\n\t});\n\n\t/**\n\t * clear Log\n\t */\n\t$('#clearLog').on('click', function(){\n\n\t\tvar jobGroup = $('#jobGroup').val();\n\t\tvar jobId = $('#jobId').val();\n\n\t\tvar jobGroupText = $(\"#jobGroup\").find(\"option:selected\").text();\n\t\tvar jobIdText = $(\"#jobId\").find(\"option:selected\").text();\n\n\t\t$('#clearLogModal input[name=jobGroup]').val(jobGroup);\n\t\t$('#clearLogModal input[name=jobId]').val(jobId);\n\n\t\t$('#clearLogModal .jobGroupText').val(jobGroupText);\n\t\t$('#clearLogModal .jobIdText').val(jobIdText);\n\n\t\t$('#clearLogModal').modal('show');\n\n\t});\n\t$(\"#clearLogModal .ok\").on('click', function(){\n\t\t$.post(base_url + \"/joblog/clearLog\",  $(\"#clearLogModal .form\").serialize(), function(data, status) {\n\t\t\tif (data.code == \"200\") {\n\t\t\t\t$('#clearLogModal').modal('hide');\n\t\t\t\tlayer.open({\n\t\t\t\t\ttitle: I18n.system_tips ,\n                    btn: [ I18n.system_ok ],\n\t\t\t\t\tcontent: (I18n.joblog_clean_log + I18n.system_success) ,\n\t\t\t\t\ticon: '1',\n\t\t\t\t\tend: function(layero, index){\n\t\t\t\t\t\tlogTable.fnDraw();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tlayer.open({\n\t\t\t\t\ttitle: I18n.system_tips ,\n                    btn: [ I18n.system_ok ],\n\t\t\t\t\tcontent: (data.msg || (I18n.joblog_clean_log + I18n.system_fail) ),\n\t\t\t\t\ticon: '2'\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t});\n\t$(\"#clearLogModal\").on('hide.bs.modal', function () {\n\t\t$(\"#clearLogModal .form\")[0].reset();\n\t});\n\n});\n\n\n// Com Alert by Tec theme\nvar ComAlertTec = {\n\thtml:function(){\n\t\tvar html =\n\t\t\t'<div class=\"modal fade\" id=\"ComAlertTec\" tabindex=\"-1\" role=\"dialog\" aria-labelledby=\"myModalLabel\" aria-hidden=\"true\">' +\n\t\t\t'\t<div class=\"modal-dialog modal-lg-\">' +\n\t\t\t'\t\t<div class=\"modal-content-tec\">' +\n\t\t\t'\t\t\t<div class=\"modal-body\">' +\n\t\t\t'\t\t\t\t<div class=\"alert\" style=\"color:#fff;word-wrap: break-word;\">' +\n\t\t\t'\t\t\t\t</div>' +\n\t\t\t'\t\t\t</div>' +\n\t\t\t'\t\t\t\t<div class=\"modal-footer\">' +\n\t\t\t'\t\t\t\t<div class=\"text-center\" >' +\n\t\t\t'\t\t\t\t\t<button type=\"button\" class=\"btn btn-info ok\" data-dismiss=\"modal\" >'+ I18n.system_ok +'</button>' +\n\t\t\t'\t\t\t\t</div>' +\n\t\t\t'\t\t\t</div>' +\n\t\t\t'\t\t</div>' +\n\t\t\t'\t</div>' +\n\t\t\t'</div>';\n\t\treturn html;\n\t},\n\tshow:function(msg, callback){\n\t\t// dom init\n\t\tif ($('#ComAlertTec').length == 0){\n\t\t\t$('body').append(ComAlertTec.html());\n\t\t}\n\n\t\t// init com alert\n\t\t$('#ComAlertTec .alert').html(msg);\n\t\t$('#ComAlertTec').modal('show');\n\n\t\t$('#ComAlertTec .ok').click(function(){\n\t\t\t$('#ComAlertTec').modal('hide');\n\t\t\tif(typeof callback == 'function') {\n\t\t\t\tcallback();\n\t\t\t}\n\t\t});\n\t}\n};\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/login.1.js",
    "content": "$(function(){\n\n\t// input iCheck\n    $('input').iCheck({\n      checkboxClass: 'icheckbox_square-blue',\n      radioClass: 'iradio_square-blue',\n      increaseArea: '20%' // optional\n    });\n    \n\t// login Form Valid\n\tvar loginFormValid = $(\"#loginForm\").validate({\n\t\terrorElement : 'span',  \n        errorClass : 'help-block',\n        focusInvalid : true,  \n        rules : {  \n        \tuserName : {  \n        \t\trequired : true ,\n                minlength: 4,\n                maxlength: 18\n            },  \n            password : {  \n            \trequired : true ,\n                minlength: 4,\n                maxlength: 18\n            } \n        }, \n        messages : {  \n        \tuserName : {  \n                required  : I18n.login_username_empty,\n                minlength : I18n.login_username_lt_4\n            },\n            password : {\n            \trequired  : I18n.login_password_empty  ,\n                minlength : I18n.login_password_lt_4\n                /*,maxlength:\"登录密码不应超过18位\"*/\n            }\n        }, \n\t\thighlight : function(element) {  \n            $(element).closest('.form-group').addClass('has-error');  \n        },\n        success : function(label) {  \n            label.closest('.form-group').removeClass('has-error');  \n            label.remove();  \n        },\n        errorPlacement : function(error, element) {  \n            element.parent('div').append(error);  \n        },\n        submitHandler : function(form) {\n\t\t\t$.post(base_url + \"/login\", $(\"#loginForm\").serialize(), function(data, status) {\n\t\t\t\tif (data.code == \"200\") {\n                    layer.msg( I18n.login_success );\n                    setTimeout(function(){\n                        window.location.href = base_url + \"/\";\n                    }, 500);\n\t\t\t\t} else {\n                    layer.open({\n                        title: I18n.system_tips,\n                        btn: [ I18n.system_ok ],\n                        content: (data.msg || I18n.login_fail ),\n                        icon: '2'\n                    });\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t});\n});"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/user.index.1.js",
    "content": "$(function() {\n\n\t// init date tables\n\tvar userListTable = $(\"#user_list\").dataTable({\n\t\t\"deferRender\": true,\n\t\t\"processing\" : true, \n\t    \"serverSide\": true,\n\t\t\"ajax\": {\n\t\t\turl: base_url + \"/user/pageList\",\n\t\t\ttype:\"post\",\n\t        data : function ( d ) {\n\t        \tvar obj = {};\n                obj.username = $('#username').val();\n                obj.role = $('#role').val();\n\t        \tobj.start = d.start;\n\t        \tobj.length = d.length;\n                return obj;\n            }\n\t    },\n\t    \"searching\": false,\n\t    \"ordering\": false,\n\t    //\"scrollX\": true,\t// scroll x，close self-adaption\n\t    \"columns\": [\n\t                {\n\t                \t\"data\": 'id',\n\t\t\t\t\t\t\"visible\" : false,\n\t\t\t\t\t\t\"width\":'10%'\n\t\t\t\t\t},\n\t                {\n\t                \t\"data\": 'username',\n\t\t\t\t\t\t\"visible\" : true,\n\t\t\t\t\t\t\"width\":'20%'\n\t\t\t\t\t},\n\t                {\n\t                \t\"data\": 'password',\n\t\t\t\t\t\t\"visible\" : false,\n                        \"width\":'20%',\n                        \"render\": function ( data, type, row ) {\n                            return '*********';\n                        }\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"data\": 'role',\n\t\t\t\t\t\t\"visible\" : true,\n\t\t\t\t\t\t\"width\":'10%',\n                        \"render\": function ( data, type, row ) {\n                            if (data == 1) {\n                                return I18n.user_role_admin\n                            } else {\n                                return I18n.user_role_normal\n                            }\n                        }\n\t\t\t\t\t},\n\t                {\n\t                \t\"data\": 'permission',\n\t\t\t\t\t\t\"width\":'10%',\n\t                \t\"visible\" : false\n\t                },\n\t                {\n\t\t\t\t\t\t\"data\": I18n.system_opt ,\n\t\t\t\t\t\t\"width\":'15%',\n\t                \t\"render\": function ( data, type, row ) {\n\t                \t\treturn function(){\n\t\t\t\t\t\t\t\t// html\n                                tableData['key'+row.id] = row;\n\t\t\t\t\t\t\t\tvar html = '<p id=\"'+ row.id +'\" >'+\n\t\t\t\t\t\t\t\t\t'<button class=\"btn btn-warning btn-xs update\" type=\"button\">'+ I18n.system_opt_edit +'</button>  '+\n\t\t\t\t\t\t\t\t\t'<button class=\"btn btn-danger btn-xs delete\" type=\"button\">'+ I18n.system_opt_del +'</button>  '+\n\t\t\t\t\t\t\t\t\t'</p>';\n\n\t                \t\t\treturn html;\n\t\t\t\t\t\t\t};\n\t                \t}\n\t                }\n\t            ],\n\t\t\"language\" : {\n\t\t\t\"sProcessing\" : I18n.dataTable_sProcessing ,\n\t\t\t\"sLengthMenu\" : I18n.dataTable_sLengthMenu ,\n\t\t\t\"sZeroRecords\" : I18n.dataTable_sZeroRecords ,\n\t\t\t\"sInfo\" : I18n.dataTable_sInfo ,\n\t\t\t\"sInfoEmpty\" : I18n.dataTable_sInfoEmpty ,\n\t\t\t\"sInfoFiltered\" : I18n.dataTable_sInfoFiltered ,\n\t\t\t\"sInfoPostFix\" : \"\",\n\t\t\t\"sSearch\" : I18n.dataTable_sSearch ,\n\t\t\t\"sUrl\" : \"\",\n\t\t\t\"sEmptyTable\" : I18n.dataTable_sEmptyTable ,\n\t\t\t\"sLoadingRecords\" : I18n.dataTable_sLoadingRecords ,\n\t\t\t\"sInfoThousands\" : \",\",\n\t\t\t\"oPaginate\" : {\n\t\t\t\t\"sFirst\" : I18n.dataTable_sFirst ,\n\t\t\t\t\"sPrevious\" : I18n.dataTable_sPrevious ,\n\t\t\t\t\"sNext\" : I18n.dataTable_sNext ,\n\t\t\t\t\"sLast\" : I18n.dataTable_sLast\n\t\t\t},\n\t\t\t\"oAria\" : {\n\t\t\t\t\"sSortAscending\" : I18n.dataTable_sSortAscending ,\n\t\t\t\t\"sSortDescending\" : I18n.dataTable_sSortDescending\n\t\t\t}\n\t\t}\n\t});\n\n    // table data\n    var tableData = {};\n\n\t// search btn\n\t$('#searchBtn').on('click', function(){\n        userListTable.fnDraw();\n\t});\n\t\n\t// job operate\n\t$(\"#user_list\").on('click', '.delete',function() {\n\t\tvar id = $(this).parent('p').attr(\"id\");\n\n\t\tlayer.confirm( I18n.system_ok + I18n.system_opt_del + '?', {\n\t\t\ticon: 3,\n\t\t\ttitle: I18n.system_tips ,\n            btn: [ I18n.system_ok, I18n.system_cancel ]\n\t\t}, function(index){\n\t\t\tlayer.close(index);\n\n\t\t\t$.ajax({\n\t\t\t\ttype : 'POST',\n\t\t\t\turl : base_url + \"/user/remove\",\n\t\t\t\tdata : {\n\t\t\t\t\t\"id\" : id\n\t\t\t\t},\n\t\t\t\tdataType : \"json\",\n\t\t\t\tsuccess : function(data){\n\t\t\t\t\tif (data.code == 200) {\n                        layer.msg( I18n.system_success );\n\t\t\t\t\t\tuserListTable.fnDraw(false);\n\t\t\t\t\t} else {\n                        layer.msg( data.msg || I18n.system_opt_del + I18n.system_fail );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t});\n\n\t// add role\n    $(\"#addModal .form input[name=role]\").change(function () {\n\t\tvar role = $(this).val();\n\t\tif (role == 1) {\n            $(\"#addModal .form input[name=permission]\").parents('.form-group').hide();\n\t\t} else {\n            $(\"#addModal .form input[name=permission]\").parents('.form-group').show();\n\t\t}\n        $(\"#addModal .form input[name='permission']\").prop(\"checked\",false);\n    });\n\n    jQuery.validator.addMethod(\"myValid01\", function(value, element) {\n        var length = value.length;\n        var valid = /^[a-z][a-z0-9]*$/;\n        return this.optional(element) || valid.test(value);\n    }, I18n.user_username_valid );\n\n\t// add\n\t$(\".add\").click(function(){\n\t\t$('#addModal').modal({backdrop: false, keyboard: false}).modal('show');\n\t});\n\tvar addModalValidate = $(\"#addModal .form\").validate({\n\t\terrorElement : 'span',  \n        errorClass : 'help-block',\n        focusInvalid : true,  \n        rules : {\n            username : {\n\t\t\t\trequired : true,\n                rangelength:[4, 20],\n                myValid01: true\n\t\t\t},\n            password : {\n                required : true,\n                rangelength:[4, 20]\n            }\n        }, \n        messages : {\n            username : {\n            \trequired : I18n.system_please_input + I18n.user_username,\n                rangelength: I18n.system_lengh_limit + \"[4-20]\"\n            },\n            password : {\n                required : I18n.system_please_input + I18n.user_password,\n                rangelength: I18n.system_lengh_limit + \"[4-20]\"\n            }\n        },\n\t\thighlight : function(element) {  \n            $(element).closest('.form-group').addClass('has-error');  \n        },\n        success : function(label) {  \n            label.closest('.form-group').removeClass('has-error');  \n            label.remove();  \n        },\n        errorPlacement : function(error, element) {  \n            element.parent('div').append(error);  \n        },\n        submitHandler : function(form) {\n\n            var permissionArr = [];\n            $(\"#addModal .form input[name=permission]:checked\").each(function(){\n                permissionArr.push($(this).val());\n            });\n\n\t\t\tvar paramData = {\n\t\t\t\t\"username\": $(\"#addModal .form input[name=username]\").val(),\n                \"password\": $(\"#addModal .form input[name=password]\").val(),\n                \"role\": $(\"#addModal .form input[name=role]:checked\").val(),\n                \"permission\": permissionArr.join(',')\n\t\t\t};\n\n        \t$.post(base_url + \"/user/add\", paramData, function(data, status) {\n    \t\t\tif (data.code == \"200\") {\n\t\t\t\t\t$('#addModal').modal('hide');\n\n                    layer.msg( I18n.system_add_suc );\n                    userListTable.fnDraw();\n    \t\t\t} else {\n\t\t\t\t\tlayer.open({\n\t\t\t\t\t\ttitle: I18n.system_tips ,\n                        btn: [ I18n.system_ok ],\n\t\t\t\t\t\tcontent: (data.msg || I18n.system_add_fail),\n\t\t\t\t\t\ticon: '2'\n\t\t\t\t\t});\n    \t\t\t}\n    \t\t});\n\t\t}\n\t});\n\t$(\"#addModal\").on('hide.bs.modal', function () {\n\t\t$(\"#addModal .form\")[0].reset();\n\t\taddModalValidate.resetForm();\n\t\t$(\"#addModal .form .form-group\").removeClass(\"has-error\");\n\t\t$(\".remote_panel\").show();\t// remote\n\n        $(\"#addModal .form input[name=permission]\").parents('.form-group').show();\n\t});\n\n    // update role\n    $(\"#updateModal .form input[name=role]\").change(function () {\n        var role = $(this).val();\n        if (role == 1) {\n            $(\"#updateModal .form input[name=permission]\").parents('.form-group').hide();\n        } else {\n            $(\"#updateModal .form input[name=permission]\").parents('.form-group').show();\n        }\n        $(\"#updateModal .form input[name='permission']\").prop(\"checked\",false);\n    });\n\n\t// update\n\t$(\"#user_list\").on('click', '.update',function() {\n\n        var id = $(this).parent('p').attr(\"id\");\n        var row = tableData['key'+id];\n\n\t\t// base data\n\t\t$(\"#updateModal .form input[name='id']\").val( row.id );\n\t\t$(\"#updateModal .form input[name='username']\").val( row.username );\n\t\t$(\"#updateModal .form input[name='password']\").val( '' );\n\t\t$(\"#updateModal .form input[name='role'][value='\"+ row.role +\"']\").click();\n        var permissionArr = [];\n        if (row.permission) {\n            permissionArr = row.permission.split(\",\");\n\t\t}\n        $(\"#updateModal .form input[name='permission']\").each(function () {\n            if($.inArray($(this).val(), permissionArr) > -1) {\n                $(this).prop(\"checked\",true);\n            } else {\n                $(this).prop(\"checked\",false);\n            }\n        });\n\n\t\t// show\n\t\t$('#updateModal').modal({backdrop: false, keyboard: false}).modal('show');\n\t});\n\tvar updateModalValidate = $(\"#updateModal .form\").validate({\n\t\terrorElement : 'span',  \n        errorClass : 'help-block',\n        focusInvalid : true,\n\t\thighlight : function(element) {\n            $(element).closest('.form-group').addClass('has-error');  \n        },\n        success : function(label) {  \n            label.closest('.form-group').removeClass('has-error');  \n            label.remove();  \n        },\n        errorPlacement : function(error, element) {  \n            element.parent('div').append(error);  \n        },\n        submitHandler : function(form) {\n\n            var permissionArr =[];\n            $(\"#updateModal .form input[name=permission]:checked\").each(function(){\n                permissionArr.push($(this).val());\n            });\n\n            var paramData = {\n                \"id\": $(\"#updateModal .form input[name=id]\").val(),\n                \"username\": $(\"#updateModal .form input[name=username]\").val(),\n                \"password\": $(\"#updateModal .form input[name=password]\").val(),\n                \"role\": $(\"#updateModal .form input[name=role]:checked\").val(),\n                \"permission\": permissionArr.join(',')\n            };\n\n            $.post(base_url + \"/user/update\", paramData, function(data, status) {\n                if (data.code == \"200\") {\n                    $('#updateModal').modal('hide');\n\n                    layer.msg( I18n.system_update_suc );\n                    userListTable.fnDraw();\n                } else {\n                    layer.open({\n                        title: I18n.system_tips ,\n                        btn: [ I18n.system_ok ],\n                        content: (data.msg || I18n.system_update_fail),\n                        icon: '2'\n                    });\n                }\n            });\n\t\t}\n\t});\n\t$(\"#updateModal\").on('hide.bs.modal', function () {\n        $(\"#updateModal .form\")[0].reset();\n        updateModalValidate.resetForm();\n        $(\"#updateModal .form .form-group\").removeClass(\"has-error\");\n        $(\".remote_panel\").show();\t// remote\n\n        $(\"#updateModal .form input[name=permission]\").parents('.form-group').show();\n\t});\n\n});\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/addon/hint/anyword-hint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var WORD = /[\\w$]+/, RANGE = 500;\n\n  CodeMirror.registerHelper(\"hint\", \"anyword\", function(editor, options) {\n    var word = options && options.word || WORD;\n    var range = options && options.range || RANGE;\n    var cur = editor.getCursor(), curLine = editor.getLine(cur.line);\n    var end = cur.ch, start = end;\n    while (start && word.test(curLine.charAt(start - 1))) --start;\n    var curWord = start != end && curLine.slice(start, end);\n\n    var list = options && options.list || [], seen = {};\n    var re = new RegExp(word.source, \"g\");\n    for (var dir = -1; dir <= 1; dir += 2) {\n      var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;\n      for (; line != endLine; line += dir) {\n        var text = editor.getLine(line), m;\n        while (m = re.exec(text)) {\n          if (line == cur.line && m[0] === curWord) continue;\n          if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) {\n            seen[m[0]] = true;\n            list.push(m[0]);\n          }\n        }\n      }\n    }\n    return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)};\n  });\n});\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/addon/hint/show-hint.css",
    "content": ".CodeMirror-hints {\n  position: absolute;\n  z-index: 10;\n  overflow: hidden;\n  list-style: none;\n\n  margin: 0;\n  padding: 2px;\n\n  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);\n  -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);\n  box-shadow: 2px 3px 5px rgba(0,0,0,.2);\n  border-radius: 3px;\n  border: 1px solid silver;\n\n  background: white;\n  font-size: 90%;\n  font-family: monospace;\n\n  max-height: 20em;\n  overflow-y: auto;\n}\n\n.CodeMirror-hint {\n  margin: 0;\n  padding: 0 4px;\n  border-radius: 2px;\n  white-space: pre;\n  color: black;\n  cursor: pointer;\n}\n\nli.CodeMirror-hint-active {\n  background: #08f;\n  color: white;\n}\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/addon/hint/show-hint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var HINT_ELEMENT_CLASS        = \"CodeMirror-hint\";\n  var ACTIVE_HINT_ELEMENT_CLASS = \"CodeMirror-hint-active\";\n\n  // This is the old interface, kept around for now to stay\n  // backwards-compatible.\n  CodeMirror.showHint = function(cm, getHints, options) {\n    if (!getHints) return cm.showHint(options);\n    if (options && options.async) getHints.async = true;\n    var newOpts = {hint: getHints};\n    if (options) for (var prop in options) newOpts[prop] = options[prop];\n    return cm.showHint(newOpts);\n  };\n\n  CodeMirror.defineExtension(\"showHint\", function(options) {\n    options = parseOptions(this, this.getCursor(\"start\"), options);\n    var selections = this.listSelections()\n    if (selections.length > 1) return;\n    // By default, don't allow completion when something is selected.\n    // A hint function can have a `supportsSelection` property to\n    // indicate that it can handle selections.\n    if (this.somethingSelected()) {\n      if (!options.hint.supportsSelection) return;\n      // Don't try with cross-line selections\n      for (var i = 0; i < selections.length; i++)\n        if (selections[i].head.line != selections[i].anchor.line) return;\n    }\n\n    if (this.state.completionActive) this.state.completionActive.close();\n    var completion = this.state.completionActive = new Completion(this, options);\n    if (!completion.options.hint) return;\n\n    CodeMirror.signal(this, \"startCompletion\", this);\n    completion.update(true);\n  });\n\n  function Completion(cm, options) {\n    this.cm = cm;\n    this.options = options;\n    this.widget = null;\n    this.debounce = 0;\n    this.tick = 0;\n    this.startPos = this.cm.getCursor(\"start\");\n    this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;\n\n    var self = this;\n    cm.on(\"cursorActivity\", this.activityFunc = function() { self.cursorActivity(); });\n  }\n\n  var requestAnimationFrame = window.requestAnimationFrame || function(fn) {\n    return setTimeout(fn, 1000/60);\n  };\n  var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;\n\n  Completion.prototype = {\n    close: function() {\n      if (!this.active()) return;\n      this.cm.state.completionActive = null;\n      this.tick = null;\n      this.cm.off(\"cursorActivity\", this.activityFunc);\n\n      if (this.widget && this.data) CodeMirror.signal(this.data, \"close\");\n      if (this.widget) this.widget.close();\n      CodeMirror.signal(this.cm, \"endCompletion\", this.cm);\n    },\n\n    active: function() {\n      return this.cm.state.completionActive == this;\n    },\n\n    pick: function(data, i) {\n      var completion = data.list[i];\n      if (completion.hint) completion.hint(this.cm, data, completion);\n      else this.cm.replaceRange(getText(completion), completion.from || data.from,\n                                completion.to || data.to, \"complete\");\n      CodeMirror.signal(data, \"pick\", completion);\n      this.close();\n    },\n\n    cursorActivity: function() {\n      if (this.debounce) {\n        cancelAnimationFrame(this.debounce);\n        this.debounce = 0;\n      }\n\n      var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);\n      if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||\n          pos.ch < this.startPos.ch || this.cm.somethingSelected() ||\n          (!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {\n        this.close();\n      } else {\n        var self = this;\n        this.debounce = requestAnimationFrame(function() {self.update();});\n        if (this.widget) this.widget.disable();\n      }\n    },\n\n    update: function(first) {\n      if (this.tick == null) return\n      var self = this, myTick = ++this.tick\n      fetchHints(this.options.hint, this.cm, this.options, function(data) {\n        if (self.tick == myTick) self.finishUpdate(data, first)\n      })\n    },\n\n    finishUpdate: function(data, first) {\n      if (this.data) CodeMirror.signal(this.data, \"update\");\n\n      var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);\n      if (this.widget) this.widget.close();\n\n      this.data = data;\n\n      if (data && data.list.length) {\n        if (picked && data.list.length == 1) {\n          this.pick(data, 0);\n        } else {\n          this.widget = new Widget(this, data);\n          CodeMirror.signal(data, \"shown\");\n        }\n      }\n    }\n  };\n\n  function parseOptions(cm, pos, options) {\n    var editor = cm.options.hintOptions;\n    var out = {};\n    for (var prop in defaultOptions) out[prop] = defaultOptions[prop];\n    if (editor) for (var prop in editor)\n      if (editor[prop] !== undefined) out[prop] = editor[prop];\n    if (options) for (var prop in options)\n      if (options[prop] !== undefined) out[prop] = options[prop];\n    if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)\n    return out;\n  }\n\n  function getText(completion) {\n    if (typeof completion == \"string\") return completion;\n    else return completion.text;\n  }\n\n  function buildKeyMap(completion, handle) {\n    var baseMap = {\n      Up: function() {handle.moveFocus(-1);},\n      Down: function() {handle.moveFocus(1);},\n      PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},\n      PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},\n      Home: function() {handle.setFocus(0);},\n      End: function() {handle.setFocus(handle.length - 1);},\n      Enter: handle.pick,\n      Tab: handle.pick,\n      Esc: handle.close\n    };\n    var custom = completion.options.customKeys;\n    var ourMap = custom ? {} : baseMap;\n    function addBinding(key, val) {\n      var bound;\n      if (typeof val != \"string\")\n        bound = function(cm) { return val(cm, handle); };\n      // This mechanism is deprecated\n      else if (baseMap.hasOwnProperty(val))\n        bound = baseMap[val];\n      else\n        bound = val;\n      ourMap[key] = bound;\n    }\n    if (custom)\n      for (var key in custom) if (custom.hasOwnProperty(key))\n        addBinding(key, custom[key]);\n    var extra = completion.options.extraKeys;\n    if (extra)\n      for (var key in extra) if (extra.hasOwnProperty(key))\n        addBinding(key, extra[key]);\n    return ourMap;\n  }\n\n  function getHintElement(hintsElement, el) {\n    while (el && el != hintsElement) {\n      if (el.nodeName.toUpperCase() === \"LI\" && el.parentNode == hintsElement) return el;\n      el = el.parentNode;\n    }\n  }\n\n  function Widget(completion, data) {\n    this.completion = completion;\n    this.data = data;\n    this.picked = false;\n    var widget = this, cm = completion.cm;\n\n    var hints = this.hints = document.createElement(\"ul\");\n    var theme = completion.cm.options.theme;\n    hints.className = \"CodeMirror-hints \" + theme;\n    this.selectedHint = data.selectedHint || 0;\n\n    var completions = data.list;\n    for (var i = 0; i < completions.length; ++i) {\n      var elt = hints.appendChild(document.createElement(\"li\")), cur = completions[i];\n      var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? \"\" : \" \" + ACTIVE_HINT_ELEMENT_CLASS);\n      if (cur.className != null) className = cur.className + \" \" + className;\n      elt.className = className;\n      if (cur.render) cur.render(elt, data, cur);\n      else elt.appendChild(document.createTextNode(cur.displayText || getText(cur)));\n      elt.hintId = i;\n    }\n\n    var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);\n    var left = pos.left, top = pos.bottom, below = true;\n    hints.style.left = left + \"px\";\n    hints.style.top = top + \"px\";\n    // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.\n    var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);\n    var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);\n    (completion.options.container || document.body).appendChild(hints);\n    var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;\n    var scrolls = hints.scrollHeight > hints.clientHeight + 1\n    var startScroll = cm.getScrollInfo();\n\n    if (overlapY > 0) {\n      var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);\n      if (curTop - height > 0) { // Fits above cursor\n        hints.style.top = (top = pos.top - height) + \"px\";\n        below = false;\n      } else if (height > winH) {\n        hints.style.height = (winH - 5) + \"px\";\n        hints.style.top = (top = pos.bottom - box.top) + \"px\";\n        var cursor = cm.getCursor();\n        if (data.from.ch != cursor.ch) {\n          pos = cm.cursorCoords(cursor);\n          hints.style.left = (left = pos.left) + \"px\";\n          box = hints.getBoundingClientRect();\n        }\n      }\n    }\n    var overlapX = box.right - winW;\n    if (overlapX > 0) {\n      if (box.right - box.left > winW) {\n        hints.style.width = (winW - 5) + \"px\";\n        overlapX -= (box.right - box.left) - winW;\n      }\n      hints.style.left = (left = pos.left - overlapX) + \"px\";\n    }\n    if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)\n      node.style.paddingRight = cm.display.nativeBarWidth + \"px\"\n\n    cm.addKeyMap(this.keyMap = buildKeyMap(completion, {\n      moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },\n      setFocus: function(n) { widget.changeActive(n); },\n      menuSize: function() { return widget.screenAmount(); },\n      length: completions.length,\n      close: function() { completion.close(); },\n      pick: function() { widget.pick(); },\n      data: data\n    }));\n\n    if (completion.options.closeOnUnfocus) {\n      var closingOnBlur;\n      cm.on(\"blur\", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });\n      cm.on(\"focus\", this.onFocus = function() { clearTimeout(closingOnBlur); });\n    }\n\n    cm.on(\"scroll\", this.onScroll = function() {\n      var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();\n      var newTop = top + startScroll.top - curScroll.top;\n      var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop);\n      if (!below) point += hints.offsetHeight;\n      if (point <= editor.top || point >= editor.bottom) return completion.close();\n      hints.style.top = newTop + \"px\";\n      hints.style.left = (left + startScroll.left - curScroll.left) + \"px\";\n    });\n\n    CodeMirror.on(hints, \"dblclick\", function(e) {\n      var t = getHintElement(hints, e.target || e.srcElement);\n      if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}\n    });\n\n    CodeMirror.on(hints, \"click\", function(e) {\n      var t = getHintElement(hints, e.target || e.srcElement);\n      if (t && t.hintId != null) {\n        widget.changeActive(t.hintId);\n        if (completion.options.completeOnSingleClick) widget.pick();\n      }\n    });\n\n    CodeMirror.on(hints, \"mousedown\", function() {\n      setTimeout(function(){cm.focus();}, 20);\n    });\n\n    CodeMirror.signal(data, \"select\", completions[this.selectedHint], hints.childNodes[this.selectedHint]);\n    return true;\n  }\n\n  Widget.prototype = {\n    close: function() {\n      if (this.completion.widget != this) return;\n      this.completion.widget = null;\n      this.hints.parentNode.removeChild(this.hints);\n      this.completion.cm.removeKeyMap(this.keyMap);\n\n      var cm = this.completion.cm;\n      if (this.completion.options.closeOnUnfocus) {\n        cm.off(\"blur\", this.onBlur);\n        cm.off(\"focus\", this.onFocus);\n      }\n      cm.off(\"scroll\", this.onScroll);\n    },\n\n    disable: function() {\n      this.completion.cm.removeKeyMap(this.keyMap);\n      var widget = this;\n      this.keyMap = {Enter: function() { widget.picked = true; }};\n      this.completion.cm.addKeyMap(this.keyMap);\n    },\n\n    pick: function() {\n      this.completion.pick(this.data, this.selectedHint);\n    },\n\n    changeActive: function(i, avoidWrap) {\n      if (i >= this.data.list.length)\n        i = avoidWrap ? this.data.list.length - 1 : 0;\n      else if (i < 0)\n        i = avoidWrap ? 0  : this.data.list.length - 1;\n      if (this.selectedHint == i) return;\n      var node = this.hints.childNodes[this.selectedHint];\n      if (node) node.className = node.className.replace(\" \" + ACTIVE_HINT_ELEMENT_CLASS, \"\");\n      node = this.hints.childNodes[this.selectedHint = i];\n      node.className += \" \" + ACTIVE_HINT_ELEMENT_CLASS;\n      if (node.offsetTop < this.hints.scrollTop)\n        this.hints.scrollTop = node.offsetTop - 3;\n      else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)\n        this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;\n      CodeMirror.signal(this.data, \"select\", this.data.list[this.selectedHint], node);\n    },\n\n    screenAmount: function() {\n      return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;\n    }\n  };\n\n  function applicableHelpers(cm, helpers) {\n    if (!cm.somethingSelected()) return helpers\n    var result = []\n    for (var i = 0; i < helpers.length; i++)\n      if (helpers[i].supportsSelection) result.push(helpers[i])\n    return result\n  }\n\n  function fetchHints(hint, cm, options, callback) {\n    if (hint.async) {\n      hint(cm, callback, options)\n    } else {\n      var result = hint(cm, options)\n      if (result && result.then) result.then(callback)\n      else callback(result)\n    }\n  }\n\n  function resolveAutoHints(cm, pos) {\n    var helpers = cm.getHelpers(pos, \"hint\"), words\n    if (helpers.length) {\n      var resolved = function(cm, callback, options) {\n        var app = applicableHelpers(cm, helpers);\n        function run(i) {\n          if (i == app.length) return callback(null)\n          fetchHints(app[i], cm, options, function(result) {\n            if (result && result.list.length > 0) callback(result)\n            else run(i + 1)\n          })\n        }\n        run(0)\n      }\n      resolved.async = true\n      resolved.supportsSelection = true\n      return resolved\n    } else if (words = cm.getHelper(cm.getCursor(), \"hintWords\")) {\n      return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) }\n    } else if (CodeMirror.hint.anyword) {\n      return function(cm, options) { return CodeMirror.hint.anyword(cm, options) }\n    } else {\n      return function() {}\n    }\n  }\n\n  CodeMirror.registerHelper(\"hint\", \"auto\", {\n    resolve: resolveAutoHints\n  });\n\n  CodeMirror.registerHelper(\"hint\", \"fromList\", function(cm, options) {\n    var cur = cm.getCursor(), token = cm.getTokenAt(cur)\n    var term, from = CodeMirror.Pos(cur.line, token.start), to = cur\n    if (token.start < cur.ch && /\\w/.test(token.string.charAt(cur.ch - token.start - 1))) {\n      term = token.string.substr(0, cur.ch - token.start)\n    } else {\n      term = \"\"\n      from = cur\n    }\n    var found = [];\n    for (var i = 0; i < options.words.length; i++) {\n      var word = options.words[i];\n      if (word.slice(0, term.length) == term)\n        found.push(word);\n    }\n\n    if (found.length) return {list: found, from: from, to: to};\n  });\n\n  CodeMirror.commands.autocomplete = CodeMirror.showHint;\n\n  var defaultOptions = {\n    hint: CodeMirror.hint.auto,\n    completeSingle: true,\n    alignWithWord: true,\n    closeCharacters: /[\\s()\\[\\]{};:>,]/,\n    closeOnUnfocus: true,\n    completeOnSingleClick: true,\n    container: null,\n    customKeys: null,\n    extraKeys: null\n  };\n\n  CodeMirror.defineOption(\"hintOptions\", null);\n});\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/lib/codemirror.css",
    "content": "/* BASICS */\n\n.CodeMirror {\n  /* Set height, width, borders, and global font properties here */\n  font-family: monospace;\n  height: 300px;\n  color: black;\n  direction: ltr;\n}\n\n/* PADDING */\n\n.CodeMirror-lines {\n  padding: 4px 0; /* Vertical padding around content */\n}\n.CodeMirror pre {\n  padding: 0 4px; /* Horizontal padding of content */\n}\n\n.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {\n  background-color: white; /* The little square between H and V scrollbars */\n}\n\n/* GUTTER */\n\n.CodeMirror-gutters {\n  border-right: 1px solid #ddd;\n  background-color: #f7f7f7;\n  white-space: nowrap;\n}\n.CodeMirror-linenumbers {}\n.CodeMirror-linenumber {\n  padding: 0 3px 0 5px;\n  min-width: 20px;\n  text-align: right;\n  color: #999;\n  white-space: nowrap;\n}\n\n.CodeMirror-guttermarker { color: black; }\n.CodeMirror-guttermarker-subtle { color: #999; }\n\n/* CURSOR */\n\n.CodeMirror-cursor {\n  border-left: 1px solid black;\n  border-right: none;\n  width: 0;\n}\n/* Shown when moving in bi-directional text */\n.CodeMirror div.CodeMirror-secondarycursor {\n  border-left: 1px solid silver;\n}\n.cm-fat-cursor .CodeMirror-cursor {\n  width: auto;\n  border: 0 !important;\n  background: #7e7;\n}\n.cm-fat-cursor div.CodeMirror-cursors {\n  z-index: 1;\n}\n.cm-fat-cursor-mark {\n  background-color: rgba(20, 255, 20, 0.5);\n  -webkit-animation: blink 1.06s steps(1) infinite;\n  -moz-animation: blink 1.06s steps(1) infinite;\n  animation: blink 1.06s steps(1) infinite;\n}\n.cm-animate-fat-cursor {\n  width: auto;\n  border: 0;\n  -webkit-animation: blink 1.06s steps(1) infinite;\n  -moz-animation: blink 1.06s steps(1) infinite;\n  animation: blink 1.06s steps(1) infinite;\n  background-color: #7e7;\n}\n@-moz-keyframes blink {\n  0% {}\n  50% { background-color: transparent; }\n  100% {}\n}\n@-webkit-keyframes blink {\n  0% {}\n  50% { background-color: transparent; }\n  100% {}\n}\n@keyframes blink {\n  0% {}\n  50% { background-color: transparent; }\n  100% {}\n}\n\n/* Can style cursor different in overwrite (non-insert) mode */\n.CodeMirror-overwrite .CodeMirror-cursor {}\n\n.cm-tab { display: inline-block; text-decoration: inherit; }\n\n.CodeMirror-rulers {\n  position: absolute;\n  left: 0; right: 0; top: -50px; bottom: -20px;\n  overflow: hidden;\n}\n.CodeMirror-ruler {\n  border-left: 1px solid #ccc;\n  top: 0; bottom: 0;\n  position: absolute;\n}\n\n/* DEFAULT THEME */\n\n.cm-s-default .cm-header {color: blue;}\n.cm-s-default .cm-quote {color: #090;}\n.cm-negative {color: #d44;}\n.cm-positive {color: #292;}\n.cm-header, .cm-strong {font-weight: bold;}\n.cm-em {font-style: italic;}\n.cm-link {text-decoration: underline;}\n.cm-strikethrough {text-decoration: line-through;}\n\n.cm-s-default .cm-keyword {color: #708;}\n.cm-s-default .cm-atom {color: #219;}\n.cm-s-default .cm-number {color: #164;}\n.cm-s-default .cm-def {color: #00f;}\n.cm-s-default .cm-variable,\n.cm-s-default .cm-punctuation,\n.cm-s-default .cm-property,\n.cm-s-default .cm-operator {}\n.cm-s-default .cm-variable-2 {color: #05a;}\n.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}\n.cm-s-default .cm-comment {color: #a50;}\n.cm-s-default .cm-string {color: #a11;}\n.cm-s-default .cm-string-2 {color: #f50;}\n.cm-s-default .cm-meta {color: #555;}\n.cm-s-default .cm-qualifier {color: #555;}\n.cm-s-default .cm-builtin {color: #30a;}\n.cm-s-default .cm-bracket {color: #997;}\n.cm-s-default .cm-tag {color: #170;}\n.cm-s-default .cm-attribute {color: #00c;}\n.cm-s-default .cm-hr {color: #999;}\n.cm-s-default .cm-link {color: #00c;}\n\n.cm-s-default .cm-error {color: #f00;}\n.cm-invalidchar {color: #f00;}\n\n.CodeMirror-composing { border-bottom: 2px solid; }\n\n/* Default styles for common addons */\n\ndiv.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}\ndiv.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}\n.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }\n.CodeMirror-activeline-background {background: #e8f2ff;}\n\n/* STOP */\n\n/* The rest of this file contains styles related to the mechanics of\n   the editor. You probably shouldn't touch them. */\n\n.CodeMirror {\n  position: relative;\n  overflow: hidden;\n  background: white;\n}\n\n.CodeMirror-scroll {\n  overflow: scroll !important; /* Things will break if this is overridden */\n  /* 30px is the magic margin used to hide the element's real scrollbars */\n  /* See overflow: hidden in .CodeMirror */\n  margin-bottom: -30px; margin-right: -30px;\n  padding-bottom: 30px;\n  height: 100%;\n  outline: none; /* Prevent dragging from highlighting the element */\n  position: relative;\n}\n.CodeMirror-sizer {\n  position: relative;\n  border-right: 30px solid transparent;\n}\n\n/* The fake, visible scrollbars. Used to force redraw during scrolling\n   before actual scrolling happens, thus preventing shaking and\n   flickering artifacts. */\n.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {\n  position: absolute;\n  z-index: 6;\n  display: none;\n}\n.CodeMirror-vscrollbar {\n  right: 0; top: 0;\n  overflow-x: hidden;\n  overflow-y: scroll;\n}\n.CodeMirror-hscrollbar {\n  bottom: 0; left: 0;\n  overflow-y: hidden;\n  overflow-x: scroll;\n}\n.CodeMirror-scrollbar-filler {\n  right: 0; bottom: 0;\n}\n.CodeMirror-gutter-filler {\n  left: 0; bottom: 0;\n}\n\n.CodeMirror-gutters {\n  position: absolute; left: 0; top: 0;\n  min-height: 100%;\n  z-index: 3;\n}\n.CodeMirror-gutter {\n  white-space: normal;\n  height: 100%;\n  display: inline-block;\n  vertical-align: top;\n  margin-bottom: -30px;\n}\n.CodeMirror-gutter-wrapper {\n  position: absolute;\n  z-index: 4;\n  background: none !important;\n  border: none !important;\n}\n.CodeMirror-gutter-background {\n  position: absolute;\n  top: 0; bottom: 0;\n  z-index: 4;\n}\n.CodeMirror-gutter-elt {\n  position: absolute;\n  cursor: default;\n  z-index: 4;\n}\n.CodeMirror-gutter-wrapper ::selection { background-color: transparent }\n.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }\n\n.CodeMirror-lines {\n  cursor: text;\n  min-height: 1px; /* prevents collapsing before first draw */\n}\n.CodeMirror pre {\n  /* Reset some styles that the rest of the page might have set */\n  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;\n  border-width: 0;\n  background: transparent;\n  font-family: inherit;\n  font-size: inherit;\n  margin: 0;\n  white-space: pre;\n  word-wrap: normal;\n  line-height: inherit;\n  color: inherit;\n  z-index: 2;\n  position: relative;\n  overflow: visible;\n  -webkit-tap-highlight-color: transparent;\n  -webkit-font-variant-ligatures: contextual;\n  font-variant-ligatures: contextual;\n}\n.CodeMirror-wrap pre {\n  word-wrap: break-word;\n  white-space: pre-wrap;\n  word-break: normal;\n}\n\n.CodeMirror-linebackground {\n  position: absolute;\n  left: 0; right: 0; top: 0; bottom: 0;\n  z-index: 0;\n}\n\n.CodeMirror-linewidget {\n  position: relative;\n  z-index: 2;\n  padding: 0.1px; /* Force widget margins to stay inside of the container */\n}\n\n.CodeMirror-widget {}\n\n.CodeMirror-rtl pre { direction: rtl; }\n\n.CodeMirror-code {\n  outline: none;\n}\n\n/* Force content-box sizing for the elements where we expect it */\n.CodeMirror-scroll,\n.CodeMirror-sizer,\n.CodeMirror-gutter,\n.CodeMirror-gutters,\n.CodeMirror-linenumber {\n  -moz-box-sizing: content-box;\n  box-sizing: content-box;\n}\n\n.CodeMirror-measure {\n  position: absolute;\n  width: 100%;\n  height: 0;\n  overflow: hidden;\n  visibility: hidden;\n}\n\n.CodeMirror-cursor {\n  position: absolute;\n  pointer-events: none;\n}\n.CodeMirror-measure pre { position: static; }\n\ndiv.CodeMirror-cursors {\n  visibility: hidden;\n  position: relative;\n  z-index: 3;\n}\ndiv.CodeMirror-dragcursors {\n  visibility: visible;\n}\n\n.CodeMirror-focused div.CodeMirror-cursors {\n  visibility: visible;\n}\n\n.CodeMirror-selected { background: #d9d9d9; }\n.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }\n.CodeMirror-crosshair { cursor: crosshair; }\n.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }\n.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }\n\n.cm-searching {\n  background-color: #ffa;\n  background-color: rgba(255, 255, 0, .4);\n}\n\n/* Used to force a border model for a node */\n.cm-force-border { padding-right: .1px; }\n\n@media print {\n  /* Hide the cursor when printing */\n  .CodeMirror div.CodeMirror-cursors {\n    visibility: hidden;\n  }\n}\n\n/* See issue #2901 */\n.cm-tab-wrap-hack:after { content: ''; }\n\n/* Help users use markselection to safely style text background */\nspan.CodeMirror-selectedtext { background: none; }\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/lib/codemirror.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n// This is CodeMirror (https://codemirror.net), a code editor\n// implemented in JavaScript on top of the browser's DOM.\n//\n// You can find some technical background for some of the code below\n// at http://marijnhaverbeke.nl/blog/#cm-internals .\n\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n  typeof define === 'function' && define.amd ? define(factory) :\n  (global.CodeMirror = factory());\n}(this, (function () { 'use strict';\n\n  // Kludges for bugs and behavior differences that can't be feature\n  // detected are enabled based on userAgent etc sniffing.\n  var userAgent = navigator.userAgent;\n  var platform = navigator.platform;\n\n  var gecko = /gecko\\/\\d/i.test(userAgent);\n  var ie_upto10 = /MSIE \\d/.test(userAgent);\n  var ie_11up = /Trident\\/(?:[7-9]|\\d{2,})\\..*rv:(\\d+)/.exec(userAgent);\n  var edge = /Edge\\/(\\d+)/.exec(userAgent);\n  var ie = ie_upto10 || ie_11up || edge;\n  var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);\n  var webkit = !edge && /WebKit\\//.test(userAgent);\n  var qtwebkit = webkit && /Qt\\/\\d+\\.\\d+/.test(userAgent);\n  var chrome = !edge && /Chrome\\//.test(userAgent);\n  var presto = /Opera\\//.test(userAgent);\n  var safari = /Apple Computer/.test(navigator.vendor);\n  var mac_geMountainLion = /Mac OS X 1\\d\\D([8-9]|\\d\\d)\\D/.test(userAgent);\n  var phantom = /PhantomJS/.test(userAgent);\n\n  var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\\/\\w+/.test(userAgent);\n  var android = /Android/.test(userAgent);\n  // This is woefully incomplete. Suggestions for alternative methods welcome.\n  var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);\n  var mac = ios || /Mac/.test(platform);\n  var chromeOS = /\\bCrOS\\b/.test(userAgent);\n  var windows = /win/i.test(platform);\n\n  var presto_version = presto && userAgent.match(/Version\\/(\\d*\\.\\d*)/);\n  if (presto_version) { presto_version = Number(presto_version[1]); }\n  if (presto_version && presto_version >= 15) { presto = false; webkit = true; }\n  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X\n  var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));\n  var captureRightClick = gecko || (ie && ie_version >= 9);\n\n  function classTest(cls) { return new RegExp(\"(^|\\\\s)\" + cls + \"(?:$|\\\\s)\\\\s*\") }\n\n  var rmClass = function(node, cls) {\n    var current = node.className;\n    var match = classTest(cls).exec(current);\n    if (match) {\n      var after = current.slice(match.index + match[0].length);\n      node.className = current.slice(0, match.index) + (after ? match[1] + after : \"\");\n    }\n  };\n\n  function removeChildren(e) {\n    for (var count = e.childNodes.length; count > 0; --count)\n      { e.removeChild(e.firstChild); }\n    return e\n  }\n\n  function removeChildrenAndAdd(parent, e) {\n    return removeChildren(parent).appendChild(e)\n  }\n\n  function elt(tag, content, className, style) {\n    var e = document.createElement(tag);\n    if (className) { e.className = className; }\n    if (style) { e.style.cssText = style; }\n    if (typeof content == \"string\") { e.appendChild(document.createTextNode(content)); }\n    else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }\n    return e\n  }\n  // wrapper for elt, which removes the elt from the accessibility tree\n  function eltP(tag, content, className, style) {\n    var e = elt(tag, content, className, style);\n    e.setAttribute(\"role\", \"presentation\");\n    return e\n  }\n\n  var range;\n  if (document.createRange) { range = function(node, start, end, endNode) {\n    var r = document.createRange();\n    r.setEnd(endNode || node, end);\n    r.setStart(node, start);\n    return r\n  }; }\n  else { range = function(node, start, end) {\n    var r = document.body.createTextRange();\n    try { r.moveToElementText(node.parentNode); }\n    catch(e) { return r }\n    r.collapse(true);\n    r.moveEnd(\"character\", end);\n    r.moveStart(\"character\", start);\n    return r\n  }; }\n\n  function contains(parent, child) {\n    if (child.nodeType == 3) // Android browser always returns false when child is a textnode\n      { child = child.parentNode; }\n    if (parent.contains)\n      { return parent.contains(child) }\n    do {\n      if (child.nodeType == 11) { child = child.host; }\n      if (child == parent) { return true }\n    } while (child = child.parentNode)\n  }\n\n  function activeElt() {\n    // IE and Edge may throw an \"Unspecified Error\" when accessing document.activeElement.\n    // IE < 10 will throw when accessed while the page is loading or in an iframe.\n    // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.\n    var activeElement;\n    try {\n      activeElement = document.activeElement;\n    } catch(e) {\n      activeElement = document.body || null;\n    }\n    while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)\n      { activeElement = activeElement.shadowRoot.activeElement; }\n    return activeElement\n  }\n\n  function addClass(node, cls) {\n    var current = node.className;\n    if (!classTest(cls).test(current)) { node.className += (current ? \" \" : \"\") + cls; }\n  }\n  function joinClasses(a, b) {\n    var as = a.split(\" \");\n    for (var i = 0; i < as.length; i++)\n      { if (as[i] && !classTest(as[i]).test(b)) { b += \" \" + as[i]; } }\n    return b\n  }\n\n  var selectInput = function(node) { node.select(); };\n  if (ios) // Mobile Safari apparently has a bug where select() is broken.\n    { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }\n  else if (ie) // Suppress mysterious IE10 errors\n    { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }\n\n  function bind(f) {\n    var args = Array.prototype.slice.call(arguments, 1);\n    return function(){return f.apply(null, args)}\n  }\n\n  function copyObj(obj, target, overwrite) {\n    if (!target) { target = {}; }\n    for (var prop in obj)\n      { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))\n        { target[prop] = obj[prop]; } }\n    return target\n  }\n\n  // Counts the column offset in a string, taking tabs into account.\n  // Used mostly to find indentation.\n  function countColumn(string, end, tabSize, startIndex, startValue) {\n    if (end == null) {\n      end = string.search(/[^\\s\\u00a0]/);\n      if (end == -1) { end = string.length; }\n    }\n    for (var i = startIndex || 0, n = startValue || 0;;) {\n      var nextTab = string.indexOf(\"\\t\", i);\n      if (nextTab < 0 || nextTab >= end)\n        { return n + (end - i) }\n      n += nextTab - i;\n      n += tabSize - (n % tabSize);\n      i = nextTab + 1;\n    }\n  }\n\n  var Delayed = function() {this.id = null;};\n  Delayed.prototype.set = function (ms, f) {\n    clearTimeout(this.id);\n    this.id = setTimeout(f, ms);\n  };\n\n  function indexOf(array, elt) {\n    for (var i = 0; i < array.length; ++i)\n      { if (array[i] == elt) { return i } }\n    return -1\n  }\n\n  // Number of pixels added to scroller and sizer to hide scrollbar\n  var scrollerGap = 30;\n\n  // Returned or thrown by various protocols to signal 'I'm not\n  // handling this'.\n  var Pass = {toString: function(){return \"CodeMirror.Pass\"}};\n\n  // Reused option objects for setSelection & friends\n  var sel_dontScroll = {scroll: false}, sel_mouse = {origin: \"*mouse\"}, sel_move = {origin: \"+move\"};\n\n  // The inverse of countColumn -- find the offset that corresponds to\n  // a particular column.\n  function findColumn(string, goal, tabSize) {\n    for (var pos = 0, col = 0;;) {\n      var nextTab = string.indexOf(\"\\t\", pos);\n      if (nextTab == -1) { nextTab = string.length; }\n      var skipped = nextTab - pos;\n      if (nextTab == string.length || col + skipped >= goal)\n        { return pos + Math.min(skipped, goal - col) }\n      col += nextTab - pos;\n      col += tabSize - (col % tabSize);\n      pos = nextTab + 1;\n      if (col >= goal) { return pos }\n    }\n  }\n\n  var spaceStrs = [\"\"];\n  function spaceStr(n) {\n    while (spaceStrs.length <= n)\n      { spaceStrs.push(lst(spaceStrs) + \" \"); }\n    return spaceStrs[n]\n  }\n\n  function lst(arr) { return arr[arr.length-1] }\n\n  function map(array, f) {\n    var out = [];\n    for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }\n    return out\n  }\n\n  function insertSorted(array, value, score) {\n    var pos = 0, priority = score(value);\n    while (pos < array.length && score(array[pos]) <= priority) { pos++; }\n    array.splice(pos, 0, value);\n  }\n\n  function nothing() {}\n\n  function createObj(base, props) {\n    var inst;\n    if (Object.create) {\n      inst = Object.create(base);\n    } else {\n      nothing.prototype = base;\n      inst = new nothing();\n    }\n    if (props) { copyObj(props, inst); }\n    return inst\n  }\n\n  var nonASCIISingleCaseWordChar = /[\\u00df\\u0587\\u0590-\\u05f4\\u0600-\\u06ff\\u3040-\\u309f\\u30a0-\\u30ff\\u3400-\\u4db5\\u4e00-\\u9fcc\\uac00-\\ud7af]/;\n  function isWordCharBasic(ch) {\n    return /\\w/.test(ch) || ch > \"\\x80\" &&\n      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))\n  }\n  function isWordChar(ch, helper) {\n    if (!helper) { return isWordCharBasic(ch) }\n    if (helper.source.indexOf(\"\\\\w\") > -1 && isWordCharBasic(ch)) { return true }\n    return helper.test(ch)\n  }\n\n  function isEmpty(obj) {\n    for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }\n    return true\n  }\n\n  // Extending unicode characters. A series of a non-extending char +\n  // any number of extending chars is treated as a single unit as far\n  // as editing and measuring is concerned. This is not fully correct,\n  // since some scripts/fonts/browsers also treat other configurations\n  // of code points as a group.\n  var extendingChars = /[\\u0300-\\u036f\\u0483-\\u0489\\u0591-\\u05bd\\u05bf\\u05c1\\u05c2\\u05c4\\u05c5\\u05c7\\u0610-\\u061a\\u064b-\\u065e\\u0670\\u06d6-\\u06dc\\u06de-\\u06e4\\u06e7\\u06e8\\u06ea-\\u06ed\\u0711\\u0730-\\u074a\\u07a6-\\u07b0\\u07eb-\\u07f3\\u0816-\\u0819\\u081b-\\u0823\\u0825-\\u0827\\u0829-\\u082d\\u0900-\\u0902\\u093c\\u0941-\\u0948\\u094d\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09bc\\u09be\\u09c1-\\u09c4\\u09cd\\u09d7\\u09e2\\u09e3\\u0a01\\u0a02\\u0a3c\\u0a41\\u0a42\\u0a47\\u0a48\\u0a4b-\\u0a4d\\u0a51\\u0a70\\u0a71\\u0a75\\u0a81\\u0a82\\u0abc\\u0ac1-\\u0ac5\\u0ac7\\u0ac8\\u0acd\\u0ae2\\u0ae3\\u0b01\\u0b3c\\u0b3e\\u0b3f\\u0b41-\\u0b44\\u0b4d\\u0b56\\u0b57\\u0b62\\u0b63\\u0b82\\u0bbe\\u0bc0\\u0bcd\\u0bd7\\u0c3e-\\u0c40\\u0c46-\\u0c48\\u0c4a-\\u0c4d\\u0c55\\u0c56\\u0c62\\u0c63\\u0cbc\\u0cbf\\u0cc2\\u0cc6\\u0ccc\\u0ccd\\u0cd5\\u0cd6\\u0ce2\\u0ce3\\u0d3e\\u0d41-\\u0d44\\u0d4d\\u0d57\\u0d62\\u0d63\\u0dca\\u0dcf\\u0dd2-\\u0dd4\\u0dd6\\u0ddf\\u0e31\\u0e34-\\u0e3a\\u0e47-\\u0e4e\\u0eb1\\u0eb4-\\u0eb9\\u0ebb\\u0ebc\\u0ec8-\\u0ecd\\u0f18\\u0f19\\u0f35\\u0f37\\u0f39\\u0f71-\\u0f7e\\u0f80-\\u0f84\\u0f86\\u0f87\\u0f90-\\u0f97\\u0f99-\\u0fbc\\u0fc6\\u102d-\\u1030\\u1032-\\u1037\\u1039\\u103a\\u103d\\u103e\\u1058\\u1059\\u105e-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108d\\u109d\\u135f\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17b7-\\u17bd\\u17c6\\u17c9-\\u17d3\\u17dd\\u180b-\\u180d\\u18a9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193b\\u1a17\\u1a18\\u1a56\\u1a58-\\u1a5e\\u1a60\\u1a62\\u1a65-\\u1a6c\\u1a73-\\u1a7c\\u1a7f\\u1b00-\\u1b03\\u1b34\\u1b36-\\u1b3a\\u1b3c\\u1b42\\u1b6b-\\u1b73\\u1b80\\u1b81\\u1ba2-\\u1ba5\\u1ba8\\u1ba9\\u1c2c-\\u1c33\\u1c36\\u1c37\\u1cd0-\\u1cd2\\u1cd4-\\u1ce0\\u1ce2-\\u1ce8\\u1ced\\u1dc0-\\u1de6\\u1dfd-\\u1dff\\u200c\\u200d\\u20d0-\\u20f0\\u2cef-\\u2cf1\\u2de0-\\u2dff\\u302a-\\u302f\\u3099\\u309a\\ua66f-\\ua672\\ua67c\\ua67d\\ua6f0\\ua6f1\\ua802\\ua806\\ua80b\\ua825\\ua826\\ua8c4\\ua8e0-\\ua8f1\\ua926-\\ua92d\\ua947-\\ua951\\ua980-\\ua982\\ua9b3\\ua9b6-\\ua9b9\\ua9bc\\uaa29-\\uaa2e\\uaa31\\uaa32\\uaa35\\uaa36\\uaa43\\uaa4c\\uaab0\\uaab2-\\uaab4\\uaab7\\uaab8\\uaabe\\uaabf\\uaac1\\uabe5\\uabe8\\uabed\\udc00-\\udfff\\ufb1e\\ufe00-\\ufe0f\\ufe20-\\ufe26\\uff9e\\uff9f]/;\n  function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }\n\n  // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.\n  function skipExtendingChars(str, pos, dir) {\n    while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }\n    return pos\n  }\n\n  // Returns the value from the range [`from`; `to`] that satisfies\n  // `pred` and is closest to `from`. Assumes that at least `to`\n  // satisfies `pred`. Supports `from` being greater than `to`.\n  function findFirst(pred, from, to) {\n    // At any point we are certain `to` satisfies `pred`, don't know\n    // whether `from` does.\n    var dir = from > to ? -1 : 1;\n    for (;;) {\n      if (from == to) { return from }\n      var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);\n      if (mid == from) { return pred(mid) ? from : to }\n      if (pred(mid)) { to = mid; }\n      else { from = mid + dir; }\n    }\n  }\n\n  // The display handles the DOM integration, both for input reading\n  // and content drawing. It holds references to DOM nodes and\n  // display-related state.\n\n  function Display(place, doc, input) {\n    var d = this;\n    this.input = input;\n\n    // Covers bottom-right square when both scrollbars are present.\n    d.scrollbarFiller = elt(\"div\", null, \"CodeMirror-scrollbar-filler\");\n    d.scrollbarFiller.setAttribute(\"cm-not-content\", \"true\");\n    // Covers bottom of gutter when coverGutterNextToScrollbar is on\n    // and h scrollbar is present.\n    d.gutterFiller = elt(\"div\", null, \"CodeMirror-gutter-filler\");\n    d.gutterFiller.setAttribute(\"cm-not-content\", \"true\");\n    // Will contain the actual code, positioned to cover the viewport.\n    d.lineDiv = eltP(\"div\", null, \"CodeMirror-code\");\n    // Elements are added to these to represent selection and cursors.\n    d.selectionDiv = elt(\"div\", null, null, \"position: relative; z-index: 1\");\n    d.cursorDiv = elt(\"div\", null, \"CodeMirror-cursors\");\n    // A visibility: hidden element used to find the size of things.\n    d.measure = elt(\"div\", null, \"CodeMirror-measure\");\n    // When lines outside of the viewport are measured, they are drawn in this.\n    d.lineMeasure = elt(\"div\", null, \"CodeMirror-measure\");\n    // Wraps everything that needs to exist inside the vertically-padded coordinate system\n    d.lineSpace = eltP(\"div\", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],\n                      null, \"position: relative; outline: none\");\n    var lines = eltP(\"div\", [d.lineSpace], \"CodeMirror-lines\");\n    // Moved around its parent to cover visible view.\n    d.mover = elt(\"div\", [lines], null, \"position: relative\");\n    // Set to the height of the document, allowing scrolling.\n    d.sizer = elt(\"div\", [d.mover], \"CodeMirror-sizer\");\n    d.sizerWidth = null;\n    // Behavior of elts with overflow: auto and padding is\n    // inconsistent across browsers. This is used to ensure the\n    // scrollable area is big enough.\n    d.heightForcer = elt(\"div\", null, null, \"position: absolute; height: \" + scrollerGap + \"px; width: 1px;\");\n    // Will contain the gutters, if any.\n    d.gutters = elt(\"div\", null, \"CodeMirror-gutters\");\n    d.lineGutter = null;\n    // Actual scrollable element.\n    d.scroller = elt(\"div\", [d.sizer, d.heightForcer, d.gutters], \"CodeMirror-scroll\");\n    d.scroller.setAttribute(\"tabIndex\", \"-1\");\n    // The element in which the editor lives.\n    d.wrapper = elt(\"div\", [d.scrollbarFiller, d.gutterFiller, d.scroller], \"CodeMirror\");\n\n    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)\n    if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }\n    if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }\n\n    if (place) {\n      if (place.appendChild) { place.appendChild(d.wrapper); }\n      else { place(d.wrapper); }\n    }\n\n    // Current rendered range (may be bigger than the view window).\n    d.viewFrom = d.viewTo = doc.first;\n    d.reportedViewFrom = d.reportedViewTo = doc.first;\n    // Information about the rendered lines.\n    d.view = [];\n    d.renderedView = null;\n    // Holds info about a single rendered line when it was rendered\n    // for measurement, while not in view.\n    d.externalMeasured = null;\n    // Empty space (in pixels) above the view\n    d.viewOffset = 0;\n    d.lastWrapHeight = d.lastWrapWidth = 0;\n    d.updateLineNumbers = null;\n\n    d.nativeBarWidth = d.barHeight = d.barWidth = 0;\n    d.scrollbarsClipped = false;\n\n    // Used to only resize the line number gutter when necessary (when\n    // the amount of lines crosses a boundary that makes its width change)\n    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;\n    // Set to true when a non-horizontal-scrolling line widget is\n    // added. As an optimization, line widget aligning is skipped when\n    // this is false.\n    d.alignWidgets = false;\n\n    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;\n\n    // Tracks the maximum line length so that the horizontal scrollbar\n    // can be kept static when scrolling.\n    d.maxLine = null;\n    d.maxLineLength = 0;\n    d.maxLineChanged = false;\n\n    // Used for measuring wheel scrolling granularity\n    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;\n\n    // True when shift is held down.\n    d.shift = false;\n\n    // Used to track whether anything happened since the context menu\n    // was opened.\n    d.selForContextMenu = null;\n\n    d.activeTouch = null;\n\n    input.init(d);\n  }\n\n  // Find the line object corresponding to the given line number.\n  function getLine(doc, n) {\n    n -= doc.first;\n    if (n < 0 || n >= doc.size) { throw new Error(\"There is no line \" + (n + doc.first) + \" in the document.\") }\n    var chunk = doc;\n    while (!chunk.lines) {\n      for (var i = 0;; ++i) {\n        var child = chunk.children[i], sz = child.chunkSize();\n        if (n < sz) { chunk = child; break }\n        n -= sz;\n      }\n    }\n    return chunk.lines[n]\n  }\n\n  // Get the part of a document between two positions, as an array of\n  // strings.\n  function getBetween(doc, start, end) {\n    var out = [], n = start.line;\n    doc.iter(start.line, end.line + 1, function (line) {\n      var text = line.text;\n      if (n == end.line) { text = text.slice(0, end.ch); }\n      if (n == start.line) { text = text.slice(start.ch); }\n      out.push(text);\n      ++n;\n    });\n    return out\n  }\n  // Get the lines between from and to, as array of strings.\n  function getLines(doc, from, to) {\n    var out = [];\n    doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value\n    return out\n  }\n\n  // Update the height of a line, propagating the height change\n  // upwards to parent nodes.\n  function updateLineHeight(line, height) {\n    var diff = height - line.height;\n    if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }\n  }\n\n  // Given a line object, find its line number by walking up through\n  // its parent links.\n  function lineNo(line) {\n    if (line.parent == null) { return null }\n    var cur = line.parent, no = indexOf(cur.lines, line);\n    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {\n      for (var i = 0;; ++i) {\n        if (chunk.children[i] == cur) { break }\n        no += chunk.children[i].chunkSize();\n      }\n    }\n    return no + cur.first\n  }\n\n  // Find the line at the given vertical position, using the height\n  // information in the document tree.\n  function lineAtHeight(chunk, h) {\n    var n = chunk.first;\n    outer: do {\n      for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {\n        var child = chunk.children[i$1], ch = child.height;\n        if (h < ch) { chunk = child; continue outer }\n        h -= ch;\n        n += child.chunkSize();\n      }\n      return n\n    } while (!chunk.lines)\n    var i = 0;\n    for (; i < chunk.lines.length; ++i) {\n      var line = chunk.lines[i], lh = line.height;\n      if (h < lh) { break }\n      h -= lh;\n    }\n    return n + i\n  }\n\n  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}\n\n  function lineNumberFor(options, i) {\n    return String(options.lineNumberFormatter(i + options.firstLineNumber))\n  }\n\n  // A Pos instance represents a position within the text.\n  function Pos(line, ch, sticky) {\n    if ( sticky === void 0 ) sticky = null;\n\n    if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }\n    this.line = line;\n    this.ch = ch;\n    this.sticky = sticky;\n  }\n\n  // Compare two positions, return 0 if they are the same, a negative\n  // number when a is less, and a positive number otherwise.\n  function cmp(a, b) { return a.line - b.line || a.ch - b.ch }\n\n  function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }\n\n  function copyPos(x) {return Pos(x.line, x.ch)}\n  function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }\n  function minPos(a, b) { return cmp(a, b) < 0 ? a : b }\n\n  // Most of the external API clips given positions to make sure they\n  // actually exist within the document.\n  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}\n  function clipPos(doc, pos) {\n    if (pos.line < doc.first) { return Pos(doc.first, 0) }\n    var last = doc.first + doc.size - 1;\n    if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }\n    return clipToLen(pos, getLine(doc, pos.line).text.length)\n  }\n  function clipToLen(pos, linelen) {\n    var ch = pos.ch;\n    if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }\n    else if (ch < 0) { return Pos(pos.line, 0) }\n    else { return pos }\n  }\n  function clipPosArray(doc, array) {\n    var out = [];\n    for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }\n    return out\n  }\n\n  // Optimize some code when these features are not used.\n  var sawReadOnlySpans = false, sawCollapsedSpans = false;\n\n  function seeReadOnlySpans() {\n    sawReadOnlySpans = true;\n  }\n\n  function seeCollapsedSpans() {\n    sawCollapsedSpans = true;\n  }\n\n  // TEXTMARKER SPANS\n\n  function MarkedSpan(marker, from, to) {\n    this.marker = marker;\n    this.from = from; this.to = to;\n  }\n\n  // Search an array of spans for a span matching the given marker.\n  function getMarkedSpanFor(spans, marker) {\n    if (spans) { for (var i = 0; i < spans.length; ++i) {\n      var span = spans[i];\n      if (span.marker == marker) { return span }\n    } }\n  }\n  // Remove a span from an array, returning undefined if no spans are\n  // left (we don't store arrays for lines without spans).\n  function removeMarkedSpan(spans, span) {\n    var r;\n    for (var i = 0; i < spans.length; ++i)\n      { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }\n    return r\n  }\n  // Add a span to a line.\n  function addMarkedSpan(line, span) {\n    line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];\n    span.marker.attachLine(line);\n  }\n\n  // Used for the algorithm that adjusts markers for a change in the\n  // document. These functions cut an array of spans at a given\n  // character position, returning an array of remaining chunks (or\n  // undefined if nothing remains).\n  function markedSpansBefore(old, startCh, isInsert) {\n    var nw;\n    if (old) { for (var i = 0; i < old.length; ++i) {\n      var span = old[i], marker = span.marker;\n      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);\n      if (startsBefore || span.from == startCh && marker.type == \"bookmark\" && (!isInsert || !span.marker.insertLeft)) {\n        var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)\n        ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));\n      }\n    } }\n    return nw\n  }\n  function markedSpansAfter(old, endCh, isInsert) {\n    var nw;\n    if (old) { for (var i = 0; i < old.length; ++i) {\n      var span = old[i], marker = span.marker;\n      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);\n      if (endsAfter || span.from == endCh && marker.type == \"bookmark\" && (!isInsert || span.marker.insertLeft)) {\n        var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)\n        ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,\n                                              span.to == null ? null : span.to - endCh));\n      }\n    } }\n    return nw\n  }\n\n  // Given a change object, compute the new set of marker spans that\n  // cover the line in which the change took place. Removes spans\n  // entirely within the change, reconnects spans belonging to the\n  // same marker that appear on both sides of the change, and cuts off\n  // spans partially within the change. Returns an array of span\n  // arrays with one element for each line in (after) the change.\n  function stretchSpansOverChange(doc, change) {\n    if (change.full) { return null }\n    var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;\n    var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;\n    if (!oldFirst && !oldLast) { return null }\n\n    var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;\n    // Get the spans that 'stick out' on both sides\n    var first = markedSpansBefore(oldFirst, startCh, isInsert);\n    var last = markedSpansAfter(oldLast, endCh, isInsert);\n\n    // Next, merge those two ends\n    var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);\n    if (first) {\n      // Fix up .to properties of first\n      for (var i = 0; i < first.length; ++i) {\n        var span = first[i];\n        if (span.to == null) {\n          var found = getMarkedSpanFor(last, span.marker);\n          if (!found) { span.to = startCh; }\n          else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }\n        }\n      }\n    }\n    if (last) {\n      // Fix up .from in last (or move them into first in case of sameLine)\n      for (var i$1 = 0; i$1 < last.length; ++i$1) {\n        var span$1 = last[i$1];\n        if (span$1.to != null) { span$1.to += offset; }\n        if (span$1.from == null) {\n          var found$1 = getMarkedSpanFor(first, span$1.marker);\n          if (!found$1) {\n            span$1.from = offset;\n            if (sameLine) { (first || (first = [])).push(span$1); }\n          }\n        } else {\n          span$1.from += offset;\n          if (sameLine) { (first || (first = [])).push(span$1); }\n        }\n      }\n    }\n    // Make sure we didn't create any zero-length spans\n    if (first) { first = clearEmptySpans(first); }\n    if (last && last != first) { last = clearEmptySpans(last); }\n\n    var newMarkers = [first];\n    if (!sameLine) {\n      // Fill gap with whole-line-spans\n      var gap = change.text.length - 2, gapMarkers;\n      if (gap > 0 && first)\n        { for (var i$2 = 0; i$2 < first.length; ++i$2)\n          { if (first[i$2].to == null)\n            { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }\n      for (var i$3 = 0; i$3 < gap; ++i$3)\n        { newMarkers.push(gapMarkers); }\n      newMarkers.push(last);\n    }\n    return newMarkers\n  }\n\n  // Remove spans that are empty and don't have a clearWhenEmpty\n  // option of false.\n  function clearEmptySpans(spans) {\n    for (var i = 0; i < spans.length; ++i) {\n      var span = spans[i];\n      if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)\n        { spans.splice(i--, 1); }\n    }\n    if (!spans.length) { return null }\n    return spans\n  }\n\n  // Used to 'clip' out readOnly ranges when making a change.\n  function removeReadOnlyRanges(doc, from, to) {\n    var markers = null;\n    doc.iter(from.line, to.line + 1, function (line) {\n      if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {\n        var mark = line.markedSpans[i].marker;\n        if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))\n          { (markers || (markers = [])).push(mark); }\n      } }\n    });\n    if (!markers) { return null }\n    var parts = [{from: from, to: to}];\n    for (var i = 0; i < markers.length; ++i) {\n      var mk = markers[i], m = mk.find(0);\n      for (var j = 0; j < parts.length; ++j) {\n        var p = parts[j];\n        if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }\n        var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);\n        if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)\n          { newParts.push({from: p.from, to: m.from}); }\n        if (dto > 0 || !mk.inclusiveRight && !dto)\n          { newParts.push({from: m.to, to: p.to}); }\n        parts.splice.apply(parts, newParts);\n        j += newParts.length - 3;\n      }\n    }\n    return parts\n  }\n\n  // Connect or disconnect spans from a line.\n  function detachMarkedSpans(line) {\n    var spans = line.markedSpans;\n    if (!spans) { return }\n    for (var i = 0; i < spans.length; ++i)\n      { spans[i].marker.detachLine(line); }\n    line.markedSpans = null;\n  }\n  function attachMarkedSpans(line, spans) {\n    if (!spans) { return }\n    for (var i = 0; i < spans.length; ++i)\n      { spans[i].marker.attachLine(line); }\n    line.markedSpans = spans;\n  }\n\n  // Helpers used when computing which overlapping collapsed span\n  // counts as the larger one.\n  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }\n  function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }\n\n  // Returns a number indicating which of two overlapping collapsed\n  // spans is larger (and thus includes the other). Falls back to\n  // comparing ids when the spans cover exactly the same range.\n  function compareCollapsedMarkers(a, b) {\n    var lenDiff = a.lines.length - b.lines.length;\n    if (lenDiff != 0) { return lenDiff }\n    var aPos = a.find(), bPos = b.find();\n    var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);\n    if (fromCmp) { return -fromCmp }\n    var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);\n    if (toCmp) { return toCmp }\n    return b.id - a.id\n  }\n\n  // Find out whether a line ends or starts in a collapsed span. If\n  // so, return the marker for that span.\n  function collapsedSpanAtSide(line, start) {\n    var sps = sawCollapsedSpans && line.markedSpans, found;\n    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {\n      sp = sps[i];\n      if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&\n          (!found || compareCollapsedMarkers(found, sp.marker) < 0))\n        { found = sp.marker; }\n    } }\n    return found\n  }\n  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }\n  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }\n\n  function collapsedSpanAround(line, ch) {\n    var sps = sawCollapsedSpans && line.markedSpans, found;\n    if (sps) { for (var i = 0; i < sps.length; ++i) {\n      var sp = sps[i];\n      if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&\n          (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }\n    } }\n    return found\n  }\n\n  // Test whether there exists a collapsed span that partially\n  // overlaps (covers the start or end, but not both) of a new span.\n  // Such overlap is not allowed.\n  function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {\n    var line = getLine(doc, lineNo$$1);\n    var sps = sawCollapsedSpans && line.markedSpans;\n    if (sps) { for (var i = 0; i < sps.length; ++i) {\n      var sp = sps[i];\n      if (!sp.marker.collapsed) { continue }\n      var found = sp.marker.find(0);\n      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);\n      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);\n      if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }\n      if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||\n          fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))\n        { return true }\n    } }\n  }\n\n  // A visual line is a line as drawn on the screen. Folding, for\n  // example, can cause multiple logical lines to appear on the same\n  // visual line. This finds the start of the visual line that the\n  // given line is part of (usually that is the line itself).\n  function visualLine(line) {\n    var merged;\n    while (merged = collapsedSpanAtStart(line))\n      { line = merged.find(-1, true).line; }\n    return line\n  }\n\n  function visualLineEnd(line) {\n    var merged;\n    while (merged = collapsedSpanAtEnd(line))\n      { line = merged.find(1, true).line; }\n    return line\n  }\n\n  // Returns an array of logical lines that continue the visual line\n  // started by the argument, or undefined if there are no such lines.\n  function visualLineContinued(line) {\n    var merged, lines;\n    while (merged = collapsedSpanAtEnd(line)) {\n      line = merged.find(1, true).line\n      ;(lines || (lines = [])).push(line);\n    }\n    return lines\n  }\n\n  // Get the line number of the start of the visual line that the\n  // given line number is part of.\n  function visualLineNo(doc, lineN) {\n    var line = getLine(doc, lineN), vis = visualLine(line);\n    if (line == vis) { return lineN }\n    return lineNo(vis)\n  }\n\n  // Get the line number of the start of the next visual line after\n  // the given line.\n  function visualLineEndNo(doc, lineN) {\n    if (lineN > doc.lastLine()) { return lineN }\n    var line = getLine(doc, lineN), merged;\n    if (!lineIsHidden(doc, line)) { return lineN }\n    while (merged = collapsedSpanAtEnd(line))\n      { line = merged.find(1, true).line; }\n    return lineNo(line) + 1\n  }\n\n  // Compute whether a line is hidden. Lines count as hidden when they\n  // are part of a visual line that starts with another line, or when\n  // they are entirely covered by collapsed, non-widget span.\n  function lineIsHidden(doc, line) {\n    var sps = sawCollapsedSpans && line.markedSpans;\n    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {\n      sp = sps[i];\n      if (!sp.marker.collapsed) { continue }\n      if (sp.from == null) { return true }\n      if (sp.marker.widgetNode) { continue }\n      if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))\n        { return true }\n    } }\n  }\n  function lineIsHiddenInner(doc, line, span) {\n    if (span.to == null) {\n      var end = span.marker.find(1, true);\n      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))\n    }\n    if (span.marker.inclusiveRight && span.to == line.text.length)\n      { return true }\n    for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {\n      sp = line.markedSpans[i];\n      if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&\n          (sp.to == null || sp.to != span.from) &&\n          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&\n          lineIsHiddenInner(doc, line, sp)) { return true }\n    }\n  }\n\n  // Find the height above the given line.\n  function heightAtLine(lineObj) {\n    lineObj = visualLine(lineObj);\n\n    var h = 0, chunk = lineObj.parent;\n    for (var i = 0; i < chunk.lines.length; ++i) {\n      var line = chunk.lines[i];\n      if (line == lineObj) { break }\n      else { h += line.height; }\n    }\n    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {\n      for (var i$1 = 0; i$1 < p.children.length; ++i$1) {\n        var cur = p.children[i$1];\n        if (cur == chunk) { break }\n        else { h += cur.height; }\n      }\n    }\n    return h\n  }\n\n  // Compute the character length of a line, taking into account\n  // collapsed ranges (see markText) that might hide parts, and join\n  // other lines onto it.\n  function lineLength(line) {\n    if (line.height == 0) { return 0 }\n    var len = line.text.length, merged, cur = line;\n    while (merged = collapsedSpanAtStart(cur)) {\n      var found = merged.find(0, true);\n      cur = found.from.line;\n      len += found.from.ch - found.to.ch;\n    }\n    cur = line;\n    while (merged = collapsedSpanAtEnd(cur)) {\n      var found$1 = merged.find(0, true);\n      len -= cur.text.length - found$1.from.ch;\n      cur = found$1.to.line;\n      len += cur.text.length - found$1.to.ch;\n    }\n    return len\n  }\n\n  // Find the longest line in the document.\n  function findMaxLine(cm) {\n    var d = cm.display, doc = cm.doc;\n    d.maxLine = getLine(doc, doc.first);\n    d.maxLineLength = lineLength(d.maxLine);\n    d.maxLineChanged = true;\n    doc.iter(function (line) {\n      var len = lineLength(line);\n      if (len > d.maxLineLength) {\n        d.maxLineLength = len;\n        d.maxLine = line;\n      }\n    });\n  }\n\n  // BIDI HELPERS\n\n  function iterateBidiSections(order, from, to, f) {\n    if (!order) { return f(from, to, \"ltr\", 0) }\n    var found = false;\n    for (var i = 0; i < order.length; ++i) {\n      var part = order[i];\n      if (part.from < to && part.to > from || from == to && part.to == from) {\n        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? \"rtl\" : \"ltr\", i);\n        found = true;\n      }\n    }\n    if (!found) { f(from, to, \"ltr\"); }\n  }\n\n  var bidiOther = null;\n  function getBidiPartAt(order, ch, sticky) {\n    var found;\n    bidiOther = null;\n    for (var i = 0; i < order.length; ++i) {\n      var cur = order[i];\n      if (cur.from < ch && cur.to > ch) { return i }\n      if (cur.to == ch) {\n        if (cur.from != cur.to && sticky == \"before\") { found = i; }\n        else { bidiOther = i; }\n      }\n      if (cur.from == ch) {\n        if (cur.from != cur.to && sticky != \"before\") { found = i; }\n        else { bidiOther = i; }\n      }\n    }\n    return found != null ? found : bidiOther\n  }\n\n  // Bidirectional ordering algorithm\n  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm\n  // that this (partially) implements.\n\n  // One-char codes used for character types:\n  // L (L):   Left-to-Right\n  // R (R):   Right-to-Left\n  // r (AL):  Right-to-Left Arabic\n  // 1 (EN):  European Number\n  // + (ES):  European Number Separator\n  // % (ET):  European Number Terminator\n  // n (AN):  Arabic Number\n  // , (CS):  Common Number Separator\n  // m (NSM): Non-Spacing Mark\n  // b (BN):  Boundary Neutral\n  // s (B):   Paragraph Separator\n  // t (S):   Segment Separator\n  // w (WS):  Whitespace\n  // N (ON):  Other Neutrals\n\n  // Returns null if characters are ordered as they appear\n  // (left-to-right), or an array of sections ({from, to, level}\n  // objects) in the order in which they occur visually.\n  var bidiOrdering = (function() {\n    // Character types for codepoints 0 to 0xff\n    var lowTypes = \"bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN\";\n    // Character types for codepoints 0x600 to 0x6f9\n    var arabicTypes = \"nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111\";\n    function charType(code) {\n      if (code <= 0xf7) { return lowTypes.charAt(code) }\n      else if (0x590 <= code && code <= 0x5f4) { return \"R\" }\n      else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }\n      else if (0x6ee <= code && code <= 0x8ac) { return \"r\" }\n      else if (0x2000 <= code && code <= 0x200b) { return \"w\" }\n      else if (code == 0x200c) { return \"b\" }\n      else { return \"L\" }\n    }\n\n    var bidiRE = /[\\u0590-\\u05f4\\u0600-\\u06ff\\u0700-\\u08ac]/;\n    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;\n\n    function BidiSpan(level, from, to) {\n      this.level = level;\n      this.from = from; this.to = to;\n    }\n\n    return function(str, direction) {\n      var outerType = direction == \"ltr\" ? \"L\" : \"R\";\n\n      if (str.length == 0 || direction == \"ltr\" && !bidiRE.test(str)) { return false }\n      var len = str.length, types = [];\n      for (var i = 0; i < len; ++i)\n        { types.push(charType(str.charCodeAt(i))); }\n\n      // W1. Examine each non-spacing mark (NSM) in the level run, and\n      // change the type of the NSM to the type of the previous\n      // character. If the NSM is at the start of the level run, it will\n      // get the type of sor.\n      for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {\n        var type = types[i$1];\n        if (type == \"m\") { types[i$1] = prev; }\n        else { prev = type; }\n      }\n\n      // W2. Search backwards from each instance of a European number\n      // until the first strong type (R, L, AL, or sor) is found. If an\n      // AL is found, change the type of the European number to Arabic\n      // number.\n      // W3. Change all ALs to R.\n      for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {\n        var type$1 = types[i$2];\n        if (type$1 == \"1\" && cur == \"r\") { types[i$2] = \"n\"; }\n        else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == \"r\") { types[i$2] = \"R\"; } }\n      }\n\n      // W4. A single European separator between two European numbers\n      // changes to a European number. A single common separator between\n      // two numbers of the same type changes to that type.\n      for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {\n        var type$2 = types[i$3];\n        if (type$2 == \"+\" && prev$1 == \"1\" && types[i$3+1] == \"1\") { types[i$3] = \"1\"; }\n        else if (type$2 == \",\" && prev$1 == types[i$3+1] &&\n                 (prev$1 == \"1\" || prev$1 == \"n\")) { types[i$3] = prev$1; }\n        prev$1 = type$2;\n      }\n\n      // W5. A sequence of European terminators adjacent to European\n      // numbers changes to all European numbers.\n      // W6. Otherwise, separators and terminators change to Other\n      // Neutral.\n      for (var i$4 = 0; i$4 < len; ++i$4) {\n        var type$3 = types[i$4];\n        if (type$3 == \",\") { types[i$4] = \"N\"; }\n        else if (type$3 == \"%\") {\n          var end = (void 0);\n          for (end = i$4 + 1; end < len && types[end] == \"%\"; ++end) {}\n          var replace = (i$4 && types[i$4-1] == \"!\") || (end < len && types[end] == \"1\") ? \"1\" : \"N\";\n          for (var j = i$4; j < end; ++j) { types[j] = replace; }\n          i$4 = end - 1;\n        }\n      }\n\n      // W7. Search backwards from each instance of a European number\n      // until the first strong type (R, L, or sor) is found. If an L is\n      // found, then change the type of the European number to L.\n      for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {\n        var type$4 = types[i$5];\n        if (cur$1 == \"L\" && type$4 == \"1\") { types[i$5] = \"L\"; }\n        else if (isStrong.test(type$4)) { cur$1 = type$4; }\n      }\n\n      // N1. A sequence of neutrals takes the direction of the\n      // surrounding strong text if the text on both sides has the same\n      // direction. European and Arabic numbers act as if they were R in\n      // terms of their influence on neutrals. Start-of-level-run (sor)\n      // and end-of-level-run (eor) are used at level run boundaries.\n      // N2. Any remaining neutrals take the embedding direction.\n      for (var i$6 = 0; i$6 < len; ++i$6) {\n        if (isNeutral.test(types[i$6])) {\n          var end$1 = (void 0);\n          for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}\n          var before = (i$6 ? types[i$6-1] : outerType) == \"L\";\n          var after = (end$1 < len ? types[end$1] : outerType) == \"L\";\n          var replace$1 = before == after ? (before ? \"L\" : \"R\") : outerType;\n          for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }\n          i$6 = end$1 - 1;\n        }\n      }\n\n      // Here we depart from the documented algorithm, in order to avoid\n      // building up an actual levels array. Since there are only three\n      // levels (0, 1, 2) in an implementation that doesn't take\n      // explicit embedding into account, we can build up the order on\n      // the fly, without following the level-based algorithm.\n      var order = [], m;\n      for (var i$7 = 0; i$7 < len;) {\n        if (countsAsLeft.test(types[i$7])) {\n          var start = i$7;\n          for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}\n          order.push(new BidiSpan(0, start, i$7));\n        } else {\n          var pos = i$7, at = order.length;\n          for (++i$7; i$7 < len && types[i$7] != \"L\"; ++i$7) {}\n          for (var j$2 = pos; j$2 < i$7;) {\n            if (countsAsNum.test(types[j$2])) {\n              if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); }\n              var nstart = j$2;\n              for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}\n              order.splice(at, 0, new BidiSpan(2, nstart, j$2));\n              pos = j$2;\n            } else { ++j$2; }\n          }\n          if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }\n        }\n      }\n      if (direction == \"ltr\") {\n        if (order[0].level == 1 && (m = str.match(/^\\s+/))) {\n          order[0].from = m[0].length;\n          order.unshift(new BidiSpan(0, 0, m[0].length));\n        }\n        if (lst(order).level == 1 && (m = str.match(/\\s+$/))) {\n          lst(order).to -= m[0].length;\n          order.push(new BidiSpan(0, len - m[0].length, len));\n        }\n      }\n\n      return direction == \"rtl\" ? order.reverse() : order\n    }\n  })();\n\n  // Get the bidi ordering for the given line (and cache it). Returns\n  // false for lines that are fully left-to-right, and an array of\n  // BidiSpan objects otherwise.\n  function getOrder(line, direction) {\n    var order = line.order;\n    if (order == null) { order = line.order = bidiOrdering(line.text, direction); }\n    return order\n  }\n\n  // EVENT HANDLING\n\n  // Lightweight event framework. on/off also work on DOM nodes,\n  // registering native DOM handlers.\n\n  var noHandlers = [];\n\n  var on = function(emitter, type, f) {\n    if (emitter.addEventListener) {\n      emitter.addEventListener(type, f, false);\n    } else if (emitter.attachEvent) {\n      emitter.attachEvent(\"on\" + type, f);\n    } else {\n      var map$$1 = emitter._handlers || (emitter._handlers = {});\n      map$$1[type] = (map$$1[type] || noHandlers).concat(f);\n    }\n  };\n\n  function getHandlers(emitter, type) {\n    return emitter._handlers && emitter._handlers[type] || noHandlers\n  }\n\n  function off(emitter, type, f) {\n    if (emitter.removeEventListener) {\n      emitter.removeEventListener(type, f, false);\n    } else if (emitter.detachEvent) {\n      emitter.detachEvent(\"on\" + type, f);\n    } else {\n      var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type];\n      if (arr) {\n        var index = indexOf(arr, f);\n        if (index > -1)\n          { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }\n      }\n    }\n  }\n\n  function signal(emitter, type /*, values...*/) {\n    var handlers = getHandlers(emitter, type);\n    if (!handlers.length) { return }\n    var args = Array.prototype.slice.call(arguments, 2);\n    for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }\n  }\n\n  // The DOM events that CodeMirror handles can be overridden by\n  // registering a (non-DOM) handler on the editor for the event name,\n  // and preventDefault-ing the event in that handler.\n  function signalDOMEvent(cm, e, override) {\n    if (typeof e == \"string\")\n      { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }\n    signal(cm, override || e.type, cm, e);\n    return e_defaultPrevented(e) || e.codemirrorIgnore\n  }\n\n  function signalCursorActivity(cm) {\n    var arr = cm._handlers && cm._handlers.cursorActivity;\n    if (!arr) { return }\n    var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);\n    for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)\n      { set.push(arr[i]); } }\n  }\n\n  function hasHandler(emitter, type) {\n    return getHandlers(emitter, type).length > 0\n  }\n\n  // Add on and off methods to a constructor's prototype, to make\n  // registering events on such objects more convenient.\n  function eventMixin(ctor) {\n    ctor.prototype.on = function(type, f) {on(this, type, f);};\n    ctor.prototype.off = function(type, f) {off(this, type, f);};\n  }\n\n  // Due to the fact that we still support jurassic IE versions, some\n  // compatibility wrappers are needed.\n\n  function e_preventDefault(e) {\n    if (e.preventDefault) { e.preventDefault(); }\n    else { e.returnValue = false; }\n  }\n  function e_stopPropagation(e) {\n    if (e.stopPropagation) { e.stopPropagation(); }\n    else { e.cancelBubble = true; }\n  }\n  function e_defaultPrevented(e) {\n    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false\n  }\n  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}\n\n  function e_target(e) {return e.target || e.srcElement}\n  function e_button(e) {\n    var b = e.which;\n    if (b == null) {\n      if (e.button & 1) { b = 1; }\n      else if (e.button & 2) { b = 3; }\n      else if (e.button & 4) { b = 2; }\n    }\n    if (mac && e.ctrlKey && b == 1) { b = 3; }\n    return b\n  }\n\n  // Detect drag-and-drop\n  var dragAndDrop = function() {\n    // There is *some* kind of drag-and-drop support in IE6-8, but I\n    // couldn't get it to work yet.\n    if (ie && ie_version < 9) { return false }\n    var div = elt('div');\n    return \"draggable\" in div || \"dragDrop\" in div\n  }();\n\n  var zwspSupported;\n  function zeroWidthElement(measure) {\n    if (zwspSupported == null) {\n      var test = elt(\"span\", \"\\u200b\");\n      removeChildrenAndAdd(measure, elt(\"span\", [test, document.createTextNode(\"x\")]));\n      if (measure.firstChild.offsetHeight != 0)\n        { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }\n    }\n    var node = zwspSupported ? elt(\"span\", \"\\u200b\") :\n      elt(\"span\", \"\\u00a0\", null, \"display: inline-block; width: 1px; margin-right: -1px\");\n    node.setAttribute(\"cm-text\", \"\");\n    return node\n  }\n\n  // Feature-detect IE's crummy client rect reporting for bidi text\n  var badBidiRects;\n  function hasBadBidiRects(measure) {\n    if (badBidiRects != null) { return badBidiRects }\n    var txt = removeChildrenAndAdd(measure, document.createTextNode(\"A\\u062eA\"));\n    var r0 = range(txt, 0, 1).getBoundingClientRect();\n    var r1 = range(txt, 1, 2).getBoundingClientRect();\n    removeChildren(measure);\n    if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)\n    return badBidiRects = (r1.right - r0.right < 3)\n  }\n\n  // See if \"\".split is the broken IE version, if so, provide an\n  // alternative way to split lines.\n  var splitLinesAuto = \"\\n\\nb\".split(/\\n/).length != 3 ? function (string) {\n    var pos = 0, result = [], l = string.length;\n    while (pos <= l) {\n      var nl = string.indexOf(\"\\n\", pos);\n      if (nl == -1) { nl = string.length; }\n      var line = string.slice(pos, string.charAt(nl - 1) == \"\\r\" ? nl - 1 : nl);\n      var rt = line.indexOf(\"\\r\");\n      if (rt != -1) {\n        result.push(line.slice(0, rt));\n        pos += rt + 1;\n      } else {\n        result.push(line);\n        pos = nl + 1;\n      }\n    }\n    return result\n  } : function (string) { return string.split(/\\r\\n?|\\n/); };\n\n  var hasSelection = window.getSelection ? function (te) {\n    try { return te.selectionStart != te.selectionEnd }\n    catch(e) { return false }\n  } : function (te) {\n    var range$$1;\n    try {range$$1 = te.ownerDocument.selection.createRange();}\n    catch(e) {}\n    if (!range$$1 || range$$1.parentElement() != te) { return false }\n    return range$$1.compareEndPoints(\"StartToEnd\", range$$1) != 0\n  };\n\n  var hasCopyEvent = (function () {\n    var e = elt(\"div\");\n    if (\"oncopy\" in e) { return true }\n    e.setAttribute(\"oncopy\", \"return;\");\n    return typeof e.oncopy == \"function\"\n  })();\n\n  var badZoomedRects = null;\n  function hasBadZoomedRects(measure) {\n    if (badZoomedRects != null) { return badZoomedRects }\n    var node = removeChildrenAndAdd(measure, elt(\"span\", \"x\"));\n    var normal = node.getBoundingClientRect();\n    var fromRange = range(node, 0, 1).getBoundingClientRect();\n    return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1\n  }\n\n  // Known modes, by name and by MIME\n  var modes = {}, mimeModes = {};\n\n  // Extra arguments are stored as the mode's dependencies, which is\n  // used by (legacy) mechanisms like loadmode.js to automatically\n  // load a mode. (Preferred mechanism is the require/define calls.)\n  function defineMode(name, mode) {\n    if (arguments.length > 2)\n      { mode.dependencies = Array.prototype.slice.call(arguments, 2); }\n    modes[name] = mode;\n  }\n\n  function defineMIME(mime, spec) {\n    mimeModes[mime] = spec;\n  }\n\n  // Given a MIME type, a {name, ...options} config object, or a name\n  // string, return a mode config object.\n  function resolveMode(spec) {\n    if (typeof spec == \"string\" && mimeModes.hasOwnProperty(spec)) {\n      spec = mimeModes[spec];\n    } else if (spec && typeof spec.name == \"string\" && mimeModes.hasOwnProperty(spec.name)) {\n      var found = mimeModes[spec.name];\n      if (typeof found == \"string\") { found = {name: found}; }\n      spec = createObj(found, spec);\n      spec.name = found.name;\n    } else if (typeof spec == \"string\" && /^[\\w\\-]+\\/[\\w\\-]+\\+xml$/.test(spec)) {\n      return resolveMode(\"application/xml\")\n    } else if (typeof spec == \"string\" && /^[\\w\\-]+\\/[\\w\\-]+\\+json$/.test(spec)) {\n      return resolveMode(\"application/json\")\n    }\n    if (typeof spec == \"string\") { return {name: spec} }\n    else { return spec || {name: \"null\"} }\n  }\n\n  // Given a mode spec (anything that resolveMode accepts), find and\n  // initialize an actual mode object.\n  function getMode(options, spec) {\n    spec = resolveMode(spec);\n    var mfactory = modes[spec.name];\n    if (!mfactory) { return getMode(options, \"text/plain\") }\n    var modeObj = mfactory(options, spec);\n    if (modeExtensions.hasOwnProperty(spec.name)) {\n      var exts = modeExtensions[spec.name];\n      for (var prop in exts) {\n        if (!exts.hasOwnProperty(prop)) { continue }\n        if (modeObj.hasOwnProperty(prop)) { modeObj[\"_\" + prop] = modeObj[prop]; }\n        modeObj[prop] = exts[prop];\n      }\n    }\n    modeObj.name = spec.name;\n    if (spec.helperType) { modeObj.helperType = spec.helperType; }\n    if (spec.modeProps) { for (var prop$1 in spec.modeProps)\n      { modeObj[prop$1] = spec.modeProps[prop$1]; } }\n\n    return modeObj\n  }\n\n  // This can be used to attach properties to mode objects from\n  // outside the actual mode definition.\n  var modeExtensions = {};\n  function extendMode(mode, properties) {\n    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});\n    copyObj(properties, exts);\n  }\n\n  function copyState(mode, state) {\n    if (state === true) { return state }\n    if (mode.copyState) { return mode.copyState(state) }\n    var nstate = {};\n    for (var n in state) {\n      var val = state[n];\n      if (val instanceof Array) { val = val.concat([]); }\n      nstate[n] = val;\n    }\n    return nstate\n  }\n\n  // Given a mode and a state (for that mode), find the inner mode and\n  // state at the position that the state refers to.\n  function innerMode(mode, state) {\n    var info;\n    while (mode.innerMode) {\n      info = mode.innerMode(state);\n      if (!info || info.mode == mode) { break }\n      state = info.state;\n      mode = info.mode;\n    }\n    return info || {mode: mode, state: state}\n  }\n\n  function startState(mode, a1, a2) {\n    return mode.startState ? mode.startState(a1, a2) : true\n  }\n\n  // STRING STREAM\n\n  // Fed to the mode parsers, provides helper functions to make\n  // parsers more succinct.\n\n  var StringStream = function(string, tabSize, lineOracle) {\n    this.pos = this.start = 0;\n    this.string = string;\n    this.tabSize = tabSize || 8;\n    this.lastColumnPos = this.lastColumnValue = 0;\n    this.lineStart = 0;\n    this.lineOracle = lineOracle;\n  };\n\n  StringStream.prototype.eol = function () {return this.pos >= this.string.length};\n  StringStream.prototype.sol = function () {return this.pos == this.lineStart};\n  StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};\n  StringStream.prototype.next = function () {\n    if (this.pos < this.string.length)\n      { return this.string.charAt(this.pos++) }\n  };\n  StringStream.prototype.eat = function (match) {\n    var ch = this.string.charAt(this.pos);\n    var ok;\n    if (typeof match == \"string\") { ok = ch == match; }\n    else { ok = ch && (match.test ? match.test(ch) : match(ch)); }\n    if (ok) {++this.pos; return ch}\n  };\n  StringStream.prototype.eatWhile = function (match) {\n    var start = this.pos;\n    while (this.eat(match)){}\n    return this.pos > start\n  };\n  StringStream.prototype.eatSpace = function () {\n      var this$1 = this;\n\n    var start = this.pos;\n    while (/[\\s\\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; }\n    return this.pos > start\n  };\n  StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};\n  StringStream.prototype.skipTo = function (ch) {\n    var found = this.string.indexOf(ch, this.pos);\n    if (found > -1) {this.pos = found; return true}\n  };\n  StringStream.prototype.backUp = function (n) {this.pos -= n;};\n  StringStream.prototype.column = function () {\n    if (this.lastColumnPos < this.start) {\n      this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);\n      this.lastColumnPos = this.start;\n    }\n    return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)\n  };\n  StringStream.prototype.indentation = function () {\n    return countColumn(this.string, null, this.tabSize) -\n      (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)\n  };\n  StringStream.prototype.match = function (pattern, consume, caseInsensitive) {\n    if (typeof pattern == \"string\") {\n      var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };\n      var substr = this.string.substr(this.pos, pattern.length);\n      if (cased(substr) == cased(pattern)) {\n        if (consume !== false) { this.pos += pattern.length; }\n        return true\n      }\n    } else {\n      var match = this.string.slice(this.pos).match(pattern);\n      if (match && match.index > 0) { return null }\n      if (match && consume !== false) { this.pos += match[0].length; }\n      return match\n    }\n  };\n  StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};\n  StringStream.prototype.hideFirstChars = function (n, inner) {\n    this.lineStart += n;\n    try { return inner() }\n    finally { this.lineStart -= n; }\n  };\n  StringStream.prototype.lookAhead = function (n) {\n    var oracle = this.lineOracle;\n    return oracle && oracle.lookAhead(n)\n  };\n  StringStream.prototype.baseToken = function () {\n    var oracle = this.lineOracle;\n    return oracle && oracle.baseToken(this.pos)\n  };\n\n  var SavedContext = function(state, lookAhead) {\n    this.state = state;\n    this.lookAhead = lookAhead;\n  };\n\n  var Context = function(doc, state, line, lookAhead) {\n    this.state = state;\n    this.doc = doc;\n    this.line = line;\n    this.maxLookAhead = lookAhead || 0;\n    this.baseTokens = null;\n    this.baseTokenPos = 1;\n  };\n\n  Context.prototype.lookAhead = function (n) {\n    var line = this.doc.getLine(this.line + n);\n    if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }\n    return line\n  };\n\n  Context.prototype.baseToken = function (n) {\n      var this$1 = this;\n\n    if (!this.baseTokens) { return null }\n    while (this.baseTokens[this.baseTokenPos] <= n)\n      { this$1.baseTokenPos += 2; }\n    var type = this.baseTokens[this.baseTokenPos + 1];\n    return {type: type && type.replace(/( |^)overlay .*/, \"\"),\n            size: this.baseTokens[this.baseTokenPos] - n}\n  };\n\n  Context.prototype.nextLine = function () {\n    this.line++;\n    if (this.maxLookAhead > 0) { this.maxLookAhead--; }\n  };\n\n  Context.fromSaved = function (doc, saved, line) {\n    if (saved instanceof SavedContext)\n      { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }\n    else\n      { return new Context(doc, copyState(doc.mode, saved), line) }\n  };\n\n  Context.prototype.save = function (copy) {\n    var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;\n    return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state\n  };\n\n\n  // Compute a style array (an array starting with a mode generation\n  // -- for invalidation -- followed by pairs of end positions and\n  // style strings), which is used to highlight the tokens on the\n  // line.\n  function highlightLine(cm, line, context, forceToEnd) {\n    // A styles array always starts with a number identifying the\n    // mode/overlays that it is based on (for easy invalidation).\n    var st = [cm.state.modeGen], lineClasses = {};\n    // Compute the base array of styles\n    runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },\n            lineClasses, forceToEnd);\n    var state = context.state;\n\n    // Run overlays, adjust style array.\n    var loop = function ( o ) {\n      context.baseTokens = st;\n      var overlay = cm.state.overlays[o], i = 1, at = 0;\n      context.state = true;\n      runMode(cm, line.text, overlay.mode, context, function (end, style) {\n        var start = i;\n        // Ensure there's a token end at the current position, and that i points at it\n        while (at < end) {\n          var i_end = st[i];\n          if (i_end > end)\n            { st.splice(i, 1, end, st[i+1], i_end); }\n          i += 2;\n          at = Math.min(end, i_end);\n        }\n        if (!style) { return }\n        if (overlay.opaque) {\n          st.splice(start, i - start, end, \"overlay \" + style);\n          i = start + 2;\n        } else {\n          for (; start < i; start += 2) {\n            var cur = st[start+1];\n            st[start+1] = (cur ? cur + \" \" : \"\") + \"overlay \" + style;\n          }\n        }\n      }, lineClasses);\n      context.state = state;\n      context.baseTokens = null;\n      context.baseTokenPos = 1;\n    };\n\n    for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );\n\n    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}\n  }\n\n  function getLineStyles(cm, line, updateFrontier) {\n    if (!line.styles || line.styles[0] != cm.state.modeGen) {\n      var context = getContextBefore(cm, lineNo(line));\n      var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);\n      var result = highlightLine(cm, line, context);\n      if (resetState) { context.state = resetState; }\n      line.stateAfter = context.save(!resetState);\n      line.styles = result.styles;\n      if (result.classes) { line.styleClasses = result.classes; }\n      else if (line.styleClasses) { line.styleClasses = null; }\n      if (updateFrontier === cm.doc.highlightFrontier)\n        { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }\n    }\n    return line.styles\n  }\n\n  function getContextBefore(cm, n, precise) {\n    var doc = cm.doc, display = cm.display;\n    if (!doc.mode.startState) { return new Context(doc, true, n) }\n    var start = findStartLine(cm, n, precise);\n    var saved = start > doc.first && getLine(doc, start - 1).stateAfter;\n    var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);\n\n    doc.iter(start, n, function (line) {\n      processLine(cm, line.text, context);\n      var pos = context.line;\n      line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;\n      context.nextLine();\n    });\n    if (precise) { doc.modeFrontier = context.line; }\n    return context\n  }\n\n  // Lightweight form of highlight -- proceed over this line and\n  // update state, but don't save a style array. Used for lines that\n  // aren't currently visible.\n  function processLine(cm, text, context, startAt) {\n    var mode = cm.doc.mode;\n    var stream = new StringStream(text, cm.options.tabSize, context);\n    stream.start = stream.pos = startAt || 0;\n    if (text == \"\") { callBlankLine(mode, context.state); }\n    while (!stream.eol()) {\n      readToken(mode, stream, context.state);\n      stream.start = stream.pos;\n    }\n  }\n\n  function callBlankLine(mode, state) {\n    if (mode.blankLine) { return mode.blankLine(state) }\n    if (!mode.innerMode) { return }\n    var inner = innerMode(mode, state);\n    if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }\n  }\n\n  function readToken(mode, stream, state, inner) {\n    for (var i = 0; i < 10; i++) {\n      if (inner) { inner[0] = innerMode(mode, state).mode; }\n      var style = mode.token(stream, state);\n      if (stream.pos > stream.start) { return style }\n    }\n    throw new Error(\"Mode \" + mode.name + \" failed to advance stream.\")\n  }\n\n  var Token = function(stream, type, state) {\n    this.start = stream.start; this.end = stream.pos;\n    this.string = stream.current();\n    this.type = type || null;\n    this.state = state;\n  };\n\n  // Utility for getTokenAt and getLineTokens\n  function takeToken(cm, pos, precise, asArray) {\n    var doc = cm.doc, mode = doc.mode, style;\n    pos = clipPos(doc, pos);\n    var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);\n    var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;\n    if (asArray) { tokens = []; }\n    while ((asArray || stream.pos < pos.ch) && !stream.eol()) {\n      stream.start = stream.pos;\n      style = readToken(mode, stream, context.state);\n      if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }\n    }\n    return asArray ? tokens : new Token(stream, style, context.state)\n  }\n\n  function extractLineClasses(type, output) {\n    if (type) { for (;;) {\n      var lineClass = type.match(/(?:^|\\s+)line-(background-)?(\\S+)/);\n      if (!lineClass) { break }\n      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);\n      var prop = lineClass[1] ? \"bgClass\" : \"textClass\";\n      if (output[prop] == null)\n        { output[prop] = lineClass[2]; }\n      else if (!(new RegExp(\"(?:^|\\s)\" + lineClass[2] + \"(?:$|\\s)\")).test(output[prop]))\n        { output[prop] += \" \" + lineClass[2]; }\n    } }\n    return type\n  }\n\n  // Run the given mode's parser over a line, calling f for each token.\n  function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {\n    var flattenSpans = mode.flattenSpans;\n    if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }\n    var curStart = 0, curStyle = null;\n    var stream = new StringStream(text, cm.options.tabSize, context), style;\n    var inner = cm.options.addModeClass && [null];\n    if (text == \"\") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }\n    while (!stream.eol()) {\n      if (stream.pos > cm.options.maxHighlightLength) {\n        flattenSpans = false;\n        if (forceToEnd) { processLine(cm, text, context, stream.pos); }\n        stream.pos = text.length;\n        style = null;\n      } else {\n        style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);\n      }\n      if (inner) {\n        var mName = inner[0].name;\n        if (mName) { style = \"m-\" + (style ? mName + \" \" + style : mName); }\n      }\n      if (!flattenSpans || curStyle != style) {\n        while (curStart < stream.start) {\n          curStart = Math.min(stream.start, curStart + 5000);\n          f(curStart, curStyle);\n        }\n        curStyle = style;\n      }\n      stream.start = stream.pos;\n    }\n    while (curStart < stream.pos) {\n      // Webkit seems to refuse to render text nodes longer than 57444\n      // characters, and returns inaccurate measurements in nodes\n      // starting around 5000 chars.\n      var pos = Math.min(stream.pos, curStart + 5000);\n      f(pos, curStyle);\n      curStart = pos;\n    }\n  }\n\n  // Finds the line to start with when starting a parse. Tries to\n  // find a line with a stateAfter, so that it can start with a\n  // valid state. If that fails, it returns the line with the\n  // smallest indentation, which tends to need the least context to\n  // parse correctly.\n  function findStartLine(cm, n, precise) {\n    var minindent, minline, doc = cm.doc;\n    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);\n    for (var search = n; search > lim; --search) {\n      if (search <= doc.first) { return doc.first }\n      var line = getLine(doc, search - 1), after = line.stateAfter;\n      if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))\n        { return search }\n      var indented = countColumn(line.text, null, cm.options.tabSize);\n      if (minline == null || minindent > indented) {\n        minline = search - 1;\n        minindent = indented;\n      }\n    }\n    return minline\n  }\n\n  function retreatFrontier(doc, n) {\n    doc.modeFrontier = Math.min(doc.modeFrontier, n);\n    if (doc.highlightFrontier < n - 10) { return }\n    var start = doc.first;\n    for (var line = n - 1; line > start; line--) {\n      var saved = getLine(doc, line).stateAfter;\n      // change is on 3\n      // state on line 1 looked ahead 2 -- so saw 3\n      // test 1 + 2 < 3 should cover this\n      if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {\n        start = line + 1;\n        break\n      }\n    }\n    doc.highlightFrontier = Math.min(doc.highlightFrontier, start);\n  }\n\n  // LINE DATA STRUCTURE\n\n  // Line objects. These hold state related to a line, including\n  // highlighting info (the styles array).\n  var Line = function(text, markedSpans, estimateHeight) {\n    this.text = text;\n    attachMarkedSpans(this, markedSpans);\n    this.height = estimateHeight ? estimateHeight(this) : 1;\n  };\n\n  Line.prototype.lineNo = function () { return lineNo(this) };\n  eventMixin(Line);\n\n  // Change the content (text, markers) of a line. Automatically\n  // invalidates cached information and tries to re-estimate the\n  // line's height.\n  function updateLine(line, text, markedSpans, estimateHeight) {\n    line.text = text;\n    if (line.stateAfter) { line.stateAfter = null; }\n    if (line.styles) { line.styles = null; }\n    if (line.order != null) { line.order = null; }\n    detachMarkedSpans(line);\n    attachMarkedSpans(line, markedSpans);\n    var estHeight = estimateHeight ? estimateHeight(line) : 1;\n    if (estHeight != line.height) { updateLineHeight(line, estHeight); }\n  }\n\n  // Detach a line from the document tree and its markers.\n  function cleanUpLine(line) {\n    line.parent = null;\n    detachMarkedSpans(line);\n  }\n\n  // Convert a style as returned by a mode (either null, or a string\n  // containing one or more styles) to a CSS style. This is cached,\n  // and also looks for line-wide styles.\n  var styleToClassCache = {}, styleToClassCacheWithMode = {};\n  function interpretTokenStyle(style, options) {\n    if (!style || /^\\s*$/.test(style)) { return null }\n    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;\n    return cache[style] ||\n      (cache[style] = style.replace(/\\S+/g, \"cm-$&\"))\n  }\n\n  // Render the DOM representation of the text of a line. Also builds\n  // up a 'line map', which points at the DOM nodes that represent\n  // specific stretches of text, and is used by the measuring code.\n  // The returned object contains the DOM node, this map, and\n  // information about line-wide styles that were set by the mode.\n  function buildLineContent(cm, lineView) {\n    // The padding-right forces the element to have a 'border', which\n    // is needed on Webkit to be able to get line-level bounding\n    // rectangles for it (in measureChar).\n    var content = eltP(\"span\", null, null, webkit ? \"padding-right: .1px\" : null);\n    var builder = {pre: eltP(\"pre\", [content], \"CodeMirror-line\"), content: content,\n                   col: 0, pos: 0, cm: cm,\n                   trailingSpace: false,\n                   splitSpaces: cm.getOption(\"lineWrapping\")};\n    lineView.measure = {};\n\n    // Iterate over the logical lines that make up this visual line.\n    for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {\n      var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);\n      builder.pos = 0;\n      builder.addToken = buildToken;\n      // Optionally wire in some hacks into the token-rendering\n      // algorithm, to deal with browser quirks.\n      if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))\n        { builder.addToken = buildTokenBadBidi(builder.addToken, order); }\n      builder.map = [];\n      var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);\n      insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));\n      if (line.styleClasses) {\n        if (line.styleClasses.bgClass)\n          { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || \"\"); }\n        if (line.styleClasses.textClass)\n          { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || \"\"); }\n      }\n\n      // Ensure at least a single node is present, for measuring.\n      if (builder.map.length == 0)\n        { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }\n\n      // Store the map and a cache object for the current logical line\n      if (i == 0) {\n        lineView.measure.map = builder.map;\n        lineView.measure.cache = {};\n      } else {\n  (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)\n        ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});\n      }\n    }\n\n    // See issue #2901\n    if (webkit) {\n      var last = builder.content.lastChild;\n      if (/\\bcm-tab\\b/.test(last.className) || (last.querySelector && last.querySelector(\".cm-tab\")))\n        { builder.content.className = \"cm-tab-wrap-hack\"; }\n    }\n\n    signal(cm, \"renderLine\", cm, lineView.line, builder.pre);\n    if (builder.pre.className)\n      { builder.textClass = joinClasses(builder.pre.className, builder.textClass || \"\"); }\n\n    return builder\n  }\n\n  function defaultSpecialCharPlaceholder(ch) {\n    var token = elt(\"span\", \"\\u2022\", \"cm-invalidchar\");\n    token.title = \"\\\\u\" + ch.charCodeAt(0).toString(16);\n    token.setAttribute(\"aria-label\", token.title);\n    return token\n  }\n\n  // Build up the DOM representation for a single token, and add it to\n  // the line map. Takes care to render special characters separately.\n  function buildToken(builder, text, style, startStyle, endStyle, title, css) {\n    if (!text) { return }\n    var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;\n    var special = builder.cm.state.specialChars, mustWrap = false;\n    var content;\n    if (!special.test(text)) {\n      builder.col += text.length;\n      content = document.createTextNode(displayText);\n      builder.map.push(builder.pos, builder.pos + text.length, content);\n      if (ie && ie_version < 9) { mustWrap = true; }\n      builder.pos += text.length;\n    } else {\n      content = document.createDocumentFragment();\n      var pos = 0;\n      while (true) {\n        special.lastIndex = pos;\n        var m = special.exec(text);\n        var skipped = m ? m.index - pos : text.length - pos;\n        if (skipped) {\n          var txt = document.createTextNode(displayText.slice(pos, pos + skipped));\n          if (ie && ie_version < 9) { content.appendChild(elt(\"span\", [txt])); }\n          else { content.appendChild(txt); }\n          builder.map.push(builder.pos, builder.pos + skipped, txt);\n          builder.col += skipped;\n          builder.pos += skipped;\n        }\n        if (!m) { break }\n        pos += skipped + 1;\n        var txt$1 = (void 0);\n        if (m[0] == \"\\t\") {\n          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;\n          txt$1 = content.appendChild(elt(\"span\", spaceStr(tabWidth), \"cm-tab\"));\n          txt$1.setAttribute(\"role\", \"presentation\");\n          txt$1.setAttribute(\"cm-text\", \"\\t\");\n          builder.col += tabWidth;\n        } else if (m[0] == \"\\r\" || m[0] == \"\\n\") {\n          txt$1 = content.appendChild(elt(\"span\", m[0] == \"\\r\" ? \"\\u240d\" : \"\\u2424\", \"cm-invalidchar\"));\n          txt$1.setAttribute(\"cm-text\", m[0]);\n          builder.col += 1;\n        } else {\n          txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);\n          txt$1.setAttribute(\"cm-text\", m[0]);\n          if (ie && ie_version < 9) { content.appendChild(elt(\"span\", [txt$1])); }\n          else { content.appendChild(txt$1); }\n          builder.col += 1;\n        }\n        builder.map.push(builder.pos, builder.pos + 1, txt$1);\n        builder.pos++;\n      }\n    }\n    builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;\n    if (style || startStyle || endStyle || mustWrap || css) {\n      var fullStyle = style || \"\";\n      if (startStyle) { fullStyle += startStyle; }\n      if (endStyle) { fullStyle += endStyle; }\n      var token = elt(\"span\", [content], fullStyle, css);\n      if (title) { token.title = title; }\n      return builder.content.appendChild(token)\n    }\n    builder.content.appendChild(content);\n  }\n\n  // Change some spaces to NBSP to prevent the browser from collapsing\n  // trailing spaces at the end of a line when rendering text (issue #1362).\n  function splitSpaces(text, trailingBefore) {\n    if (text.length > 1 && !/  /.test(text)) { return text }\n    var spaceBefore = trailingBefore, result = \"\";\n    for (var i = 0; i < text.length; i++) {\n      var ch = text.charAt(i);\n      if (ch == \" \" && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))\n        { ch = \"\\u00a0\"; }\n      result += ch;\n      spaceBefore = ch == \" \";\n    }\n    return result\n  }\n\n  // Work around nonsense dimensions being reported for stretches of\n  // right-to-left text.\n  function buildTokenBadBidi(inner, order) {\n    return function (builder, text, style, startStyle, endStyle, title, css) {\n      style = style ? style + \" cm-force-border\" : \"cm-force-border\";\n      var start = builder.pos, end = start + text.length;\n      for (;;) {\n        // Find the part that overlaps with the start of this text\n        var part = (void 0);\n        for (var i = 0; i < order.length; i++) {\n          part = order[i];\n          if (part.to > start && part.from <= start) { break }\n        }\n        if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) }\n        inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);\n        startStyle = null;\n        text = text.slice(part.to - start);\n        start = part.to;\n      }\n    }\n  }\n\n  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {\n    var widget = !ignoreWidget && marker.widgetNode;\n    if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }\n    if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {\n      if (!widget)\n        { widget = builder.content.appendChild(document.createElement(\"span\")); }\n      widget.setAttribute(\"cm-marker\", marker.id);\n    }\n    if (widget) {\n      builder.cm.display.input.setUneditable(widget);\n      builder.content.appendChild(widget);\n    }\n    builder.pos += size;\n    builder.trailingSpace = false;\n  }\n\n  // Outputs a number of spans to make up a line, taking highlighting\n  // and marked text into account.\n  function insertLineContent(line, builder, styles) {\n    var spans = line.markedSpans, allText = line.text, at = 0;\n    if (!spans) {\n      for (var i$1 = 1; i$1 < styles.length; i$1+=2)\n        { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }\n      return\n    }\n\n    var len = allText.length, pos = 0, i = 1, text = \"\", style, css;\n    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;\n    for (;;) {\n      if (nextChange == pos) { // Update current marker set\n        spanStyle = spanEndStyle = spanStartStyle = title = css = \"\";\n        collapsed = null; nextChange = Infinity;\n        var foundBookmarks = [], endStyles = (void 0);\n        for (var j = 0; j < spans.length; ++j) {\n          var sp = spans[j], m = sp.marker;\n          if (m.type == \"bookmark\" && sp.from == pos && m.widgetNode) {\n            foundBookmarks.push(m);\n          } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {\n            if (sp.to != null && sp.to != pos && nextChange > sp.to) {\n              nextChange = sp.to;\n              spanEndStyle = \"\";\n            }\n            if (m.className) { spanStyle += \" \" + m.className; }\n            if (m.css) { css = (css ? css + \";\" : \"\") + m.css; }\n            if (m.startStyle && sp.from == pos) { spanStartStyle += \" \" + m.startStyle; }\n            if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }\n            if (m.title && !title) { title = m.title; }\n            if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))\n              { collapsed = sp; }\n          } else if (sp.from > pos && nextChange > sp.from) {\n            nextChange = sp.from;\n          }\n        }\n        if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)\n          { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += \" \" + endStyles[j$1]; } } }\n\n        if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)\n          { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }\n        if (collapsed && (collapsed.from || 0) == pos) {\n          buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,\n                             collapsed.marker, collapsed.from == null);\n          if (collapsed.to == null) { return }\n          if (collapsed.to == pos) { collapsed = false; }\n        }\n      }\n      if (pos >= len) { break }\n\n      var upto = Math.min(len, nextChange);\n      while (true) {\n        if (text) {\n          var end = pos + text.length;\n          if (!collapsed) {\n            var tokenText = end > upto ? text.slice(0, upto - pos) : text;\n            builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,\n                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : \"\", title, css);\n          }\n          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}\n          pos = end;\n          spanStartStyle = \"\";\n        }\n        text = allText.slice(at, at = styles[i++]);\n        style = interpretTokenStyle(styles[i++], builder.cm.options);\n      }\n    }\n  }\n\n\n  // These objects are used to represent the visible (currently drawn)\n  // part of the document. A LineView may correspond to multiple\n  // logical lines, if those are connected by collapsed ranges.\n  function LineView(doc, line, lineN) {\n    // The starting line\n    this.line = line;\n    // Continuing lines, if any\n    this.rest = visualLineContinued(line);\n    // Number of logical lines in this visual line\n    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;\n    this.node = this.text = null;\n    this.hidden = lineIsHidden(doc, line);\n  }\n\n  // Create a range of LineView objects for the given lines.\n  function buildViewArray(cm, from, to) {\n    var array = [], nextPos;\n    for (var pos = from; pos < to; pos = nextPos) {\n      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);\n      nextPos = pos + view.size;\n      array.push(view);\n    }\n    return array\n  }\n\n  var operationGroup = null;\n\n  function pushOperation(op) {\n    if (operationGroup) {\n      operationGroup.ops.push(op);\n    } else {\n      op.ownsGroup = operationGroup = {\n        ops: [op],\n        delayedCallbacks: []\n      };\n    }\n  }\n\n  function fireCallbacksForOps(group) {\n    // Calls delayed callbacks and cursorActivity handlers until no\n    // new ones appear\n    var callbacks = group.delayedCallbacks, i = 0;\n    do {\n      for (; i < callbacks.length; i++)\n        { callbacks[i].call(null); }\n      for (var j = 0; j < group.ops.length; j++) {\n        var op = group.ops[j];\n        if (op.cursorActivityHandlers)\n          { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)\n            { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }\n      }\n    } while (i < callbacks.length)\n  }\n\n  function finishOperation(op, endCb) {\n    var group = op.ownsGroup;\n    if (!group) { return }\n\n    try { fireCallbacksForOps(group); }\n    finally {\n      operationGroup = null;\n      endCb(group);\n    }\n  }\n\n  var orphanDelayedCallbacks = null;\n\n  // Often, we want to signal events at a point where we are in the\n  // middle of some work, but don't want the handler to start calling\n  // other methods on the editor, which might be in an inconsistent\n  // state or simply not expect any other events to happen.\n  // signalLater looks whether there are any handlers, and schedules\n  // them to be executed when the last operation ends, or, if no\n  // operation is active, when a timeout fires.\n  function signalLater(emitter, type /*, values...*/) {\n    var arr = getHandlers(emitter, type);\n    if (!arr.length) { return }\n    var args = Array.prototype.slice.call(arguments, 2), list;\n    if (operationGroup) {\n      list = operationGroup.delayedCallbacks;\n    } else if (orphanDelayedCallbacks) {\n      list = orphanDelayedCallbacks;\n    } else {\n      list = orphanDelayedCallbacks = [];\n      setTimeout(fireOrphanDelayed, 0);\n    }\n    var loop = function ( i ) {\n      list.push(function () { return arr[i].apply(null, args); });\n    };\n\n    for (var i = 0; i < arr.length; ++i)\n      loop( i );\n  }\n\n  function fireOrphanDelayed() {\n    var delayed = orphanDelayedCallbacks;\n    orphanDelayedCallbacks = null;\n    for (var i = 0; i < delayed.length; ++i) { delayed[i](); }\n  }\n\n  // When an aspect of a line changes, a string is added to\n  // lineView.changes. This updates the relevant part of the line's\n  // DOM structure.\n  function updateLineForChanges(cm, lineView, lineN, dims) {\n    for (var j = 0; j < lineView.changes.length; j++) {\n      var type = lineView.changes[j];\n      if (type == \"text\") { updateLineText(cm, lineView); }\n      else if (type == \"gutter\") { updateLineGutter(cm, lineView, lineN, dims); }\n      else if (type == \"class\") { updateLineClasses(cm, lineView); }\n      else if (type == \"widget\") { updateLineWidgets(cm, lineView, dims); }\n    }\n    lineView.changes = null;\n  }\n\n  // Lines with gutter elements, widgets or a background class need to\n  // be wrapped, and have the extra elements added to the wrapper div\n  function ensureLineWrapped(lineView) {\n    if (lineView.node == lineView.text) {\n      lineView.node = elt(\"div\", null, null, \"position: relative\");\n      if (lineView.text.parentNode)\n        { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }\n      lineView.node.appendChild(lineView.text);\n      if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }\n    }\n    return lineView.node\n  }\n\n  function updateLineBackground(cm, lineView) {\n    var cls = lineView.bgClass ? lineView.bgClass + \" \" + (lineView.line.bgClass || \"\") : lineView.line.bgClass;\n    if (cls) { cls += \" CodeMirror-linebackground\"; }\n    if (lineView.background) {\n      if (cls) { lineView.background.className = cls; }\n      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }\n    } else if (cls) {\n      var wrap = ensureLineWrapped(lineView);\n      lineView.background = wrap.insertBefore(elt(\"div\", null, cls), wrap.firstChild);\n      cm.display.input.setUneditable(lineView.background);\n    }\n  }\n\n  // Wrapper around buildLineContent which will reuse the structure\n  // in display.externalMeasured when possible.\n  function getLineContent(cm, lineView) {\n    var ext = cm.display.externalMeasured;\n    if (ext && ext.line == lineView.line) {\n      cm.display.externalMeasured = null;\n      lineView.measure = ext.measure;\n      return ext.built\n    }\n    return buildLineContent(cm, lineView)\n  }\n\n  // Redraw the line's text. Interacts with the background and text\n  // classes because the mode may output tokens that influence these\n  // classes.\n  function updateLineText(cm, lineView) {\n    var cls = lineView.text.className;\n    var built = getLineContent(cm, lineView);\n    if (lineView.text == lineView.node) { lineView.node = built.pre; }\n    lineView.text.parentNode.replaceChild(built.pre, lineView.text);\n    lineView.text = built.pre;\n    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {\n      lineView.bgClass = built.bgClass;\n      lineView.textClass = built.textClass;\n      updateLineClasses(cm, lineView);\n    } else if (cls) {\n      lineView.text.className = cls;\n    }\n  }\n\n  function updateLineClasses(cm, lineView) {\n    updateLineBackground(cm, lineView);\n    if (lineView.line.wrapClass)\n      { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }\n    else if (lineView.node != lineView.text)\n      { lineView.node.className = \"\"; }\n    var textClass = lineView.textClass ? lineView.textClass + \" \" + (lineView.line.textClass || \"\") : lineView.line.textClass;\n    lineView.text.className = textClass || \"\";\n  }\n\n  function updateLineGutter(cm, lineView, lineN, dims) {\n    if (lineView.gutter) {\n      lineView.node.removeChild(lineView.gutter);\n      lineView.gutter = null;\n    }\n    if (lineView.gutterBackground) {\n      lineView.node.removeChild(lineView.gutterBackground);\n      lineView.gutterBackground = null;\n    }\n    if (lineView.line.gutterClass) {\n      var wrap = ensureLineWrapped(lineView);\n      lineView.gutterBackground = elt(\"div\", null, \"CodeMirror-gutter-background \" + lineView.line.gutterClass,\n                                      (\"left: \" + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + \"px; width: \" + (dims.gutterTotalWidth) + \"px\"));\n      cm.display.input.setUneditable(lineView.gutterBackground);\n      wrap.insertBefore(lineView.gutterBackground, lineView.text);\n    }\n    var markers = lineView.line.gutterMarkers;\n    if (cm.options.lineNumbers || markers) {\n      var wrap$1 = ensureLineWrapped(lineView);\n      var gutterWrap = lineView.gutter = elt(\"div\", null, \"CodeMirror-gutter-wrapper\", (\"left: \" + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + \"px\"));\n      cm.display.input.setUneditable(gutterWrap);\n      wrap$1.insertBefore(gutterWrap, lineView.text);\n      if (lineView.line.gutterClass)\n        { gutterWrap.className += \" \" + lineView.line.gutterClass; }\n      if (cm.options.lineNumbers && (!markers || !markers[\"CodeMirror-linenumbers\"]))\n        { lineView.lineNumber = gutterWrap.appendChild(\n          elt(\"div\", lineNumberFor(cm.options, lineN),\n              \"CodeMirror-linenumber CodeMirror-gutter-elt\",\n              (\"left: \" + (dims.gutterLeft[\"CodeMirror-linenumbers\"]) + \"px; width: \" + (cm.display.lineNumInnerWidth) + \"px\"))); }\n      if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {\n        var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];\n        if (found)\n          { gutterWrap.appendChild(elt(\"div\", [found], \"CodeMirror-gutter-elt\",\n                                     (\"left: \" + (dims.gutterLeft[id]) + \"px; width: \" + (dims.gutterWidth[id]) + \"px\"))); }\n      } }\n    }\n  }\n\n  function updateLineWidgets(cm, lineView, dims) {\n    if (lineView.alignable) { lineView.alignable = null; }\n    for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {\n      next = node.nextSibling;\n      if (node.className == \"CodeMirror-linewidget\")\n        { lineView.node.removeChild(node); }\n    }\n    insertLineWidgets(cm, lineView, dims);\n  }\n\n  // Build a line's DOM representation from scratch\n  function buildLineElement(cm, lineView, lineN, dims) {\n    var built = getLineContent(cm, lineView);\n    lineView.text = lineView.node = built.pre;\n    if (built.bgClass) { lineView.bgClass = built.bgClass; }\n    if (built.textClass) { lineView.textClass = built.textClass; }\n\n    updateLineClasses(cm, lineView);\n    updateLineGutter(cm, lineView, lineN, dims);\n    insertLineWidgets(cm, lineView, dims);\n    return lineView.node\n  }\n\n  // A lineView may contain multiple logical lines (when merged by\n  // collapsed spans). The widgets for all of them need to be drawn.\n  function insertLineWidgets(cm, lineView, dims) {\n    insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);\n    if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)\n      { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }\n  }\n\n  function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {\n    if (!line.widgets) { return }\n    var wrap = ensureLineWrapped(lineView);\n    for (var i = 0, ws = line.widgets; i < ws.length; ++i) {\n      var widget = ws[i], node = elt(\"div\", [widget.node], \"CodeMirror-linewidget\");\n      if (!widget.handleMouseEvents) { node.setAttribute(\"cm-ignore-events\", \"true\"); }\n      positionLineWidget(widget, node, lineView, dims);\n      cm.display.input.setUneditable(node);\n      if (allowAbove && widget.above)\n        { wrap.insertBefore(node, lineView.gutter || lineView.text); }\n      else\n        { wrap.appendChild(node); }\n      signalLater(widget, \"redraw\");\n    }\n  }\n\n  function positionLineWidget(widget, node, lineView, dims) {\n    if (widget.noHScroll) {\n  (lineView.alignable || (lineView.alignable = [])).push(node);\n      var width = dims.wrapperWidth;\n      node.style.left = dims.fixedPos + \"px\";\n      if (!widget.coverGutter) {\n        width -= dims.gutterTotalWidth;\n        node.style.paddingLeft = dims.gutterTotalWidth + \"px\";\n      }\n      node.style.width = width + \"px\";\n    }\n    if (widget.coverGutter) {\n      node.style.zIndex = 5;\n      node.style.position = \"relative\";\n      if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + \"px\"; }\n    }\n  }\n\n  function widgetHeight(widget) {\n    if (widget.height != null) { return widget.height }\n    var cm = widget.doc.cm;\n    if (!cm) { return 0 }\n    if (!contains(document.body, widget.node)) {\n      var parentStyle = \"position: relative;\";\n      if (widget.coverGutter)\n        { parentStyle += \"margin-left: -\" + cm.display.gutters.offsetWidth + \"px;\"; }\n      if (widget.noHScroll)\n        { parentStyle += \"width: \" + cm.display.wrapper.clientWidth + \"px;\"; }\n      removeChildrenAndAdd(cm.display.measure, elt(\"div\", [widget.node], null, parentStyle));\n    }\n    return widget.height = widget.node.parentNode.offsetHeight\n  }\n\n  // Return true when the given mouse event happened in a widget\n  function eventInWidget(display, e) {\n    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {\n      if (!n || (n.nodeType == 1 && n.getAttribute(\"cm-ignore-events\") == \"true\") ||\n          (n.parentNode == display.sizer && n != display.mover))\n        { return true }\n    }\n  }\n\n  // POSITION MEASUREMENT\n\n  function paddingTop(display) {return display.lineSpace.offsetTop}\n  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}\n  function paddingH(display) {\n    if (display.cachedPaddingH) { return display.cachedPaddingH }\n    var e = removeChildrenAndAdd(display.measure, elt(\"pre\", \"x\"));\n    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;\n    var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};\n    if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }\n    return data\n  }\n\n  function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }\n  function displayWidth(cm) {\n    return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth\n  }\n  function displayHeight(cm) {\n    return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight\n  }\n\n  // Ensure the lineView.wrapping.heights array is populated. This is\n  // an array of bottom offsets for the lines that make up a drawn\n  // line. When lineWrapping is on, there might be more than one\n  // height.\n  function ensureLineHeights(cm, lineView, rect) {\n    var wrapping = cm.options.lineWrapping;\n    var curWidth = wrapping && displayWidth(cm);\n    if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {\n      var heights = lineView.measure.heights = [];\n      if (wrapping) {\n        lineView.measure.width = curWidth;\n        var rects = lineView.text.firstChild.getClientRects();\n        for (var i = 0; i < rects.length - 1; i++) {\n          var cur = rects[i], next = rects[i + 1];\n          if (Math.abs(cur.bottom - next.bottom) > 2)\n            { heights.push((cur.bottom + next.top) / 2 - rect.top); }\n        }\n      }\n      heights.push(rect.bottom - rect.top);\n    }\n  }\n\n  // Find a line map (mapping character offsets to text nodes) and a\n  // measurement cache for the given line number. (A line view might\n  // contain multiple lines when collapsed ranges are present.)\n  function mapFromLineView(lineView, line, lineN) {\n    if (lineView.line == line)\n      { return {map: lineView.measure.map, cache: lineView.measure.cache} }\n    for (var i = 0; i < lineView.rest.length; i++)\n      { if (lineView.rest[i] == line)\n        { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }\n    for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)\n      { if (lineNo(lineView.rest[i$1]) > lineN)\n        { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }\n  }\n\n  // Render a line into the hidden node display.externalMeasured. Used\n  // when measurement is needed for a line that's not in the viewport.\n  function updateExternalMeasurement(cm, line) {\n    line = visualLine(line);\n    var lineN = lineNo(line);\n    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);\n    view.lineN = lineN;\n    var built = view.built = buildLineContent(cm, view);\n    view.text = built.pre;\n    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);\n    return view\n  }\n\n  // Get a {top, bottom, left, right} box (in line-local coordinates)\n  // for a given character.\n  function measureChar(cm, line, ch, bias) {\n    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)\n  }\n\n  // Find a line view that corresponds to the given line number.\n  function findViewForLine(cm, lineN) {\n    if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)\n      { return cm.display.view[findViewIndex(cm, lineN)] }\n    var ext = cm.display.externalMeasured;\n    if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)\n      { return ext }\n  }\n\n  // Measurement can be split in two steps, the set-up work that\n  // applies to the whole line, and the measurement of the actual\n  // character. Functions like coordsChar, that need to do a lot of\n  // measurements in a row, can thus ensure that the set-up work is\n  // only done once.\n  function prepareMeasureForLine(cm, line) {\n    var lineN = lineNo(line);\n    var view = findViewForLine(cm, lineN);\n    if (view && !view.text) {\n      view = null;\n    } else if (view && view.changes) {\n      updateLineForChanges(cm, view, lineN, getDimensions(cm));\n      cm.curOp.forceUpdate = true;\n    }\n    if (!view)\n      { view = updateExternalMeasurement(cm, line); }\n\n    var info = mapFromLineView(view, line, lineN);\n    return {\n      line: line, view: view, rect: null,\n      map: info.map, cache: info.cache, before: info.before,\n      hasHeights: false\n    }\n  }\n\n  // Given a prepared measurement object, measures the position of an\n  // actual character (or fetches it from the cache).\n  function measureCharPrepared(cm, prepared, ch, bias, varHeight) {\n    if (prepared.before) { ch = -1; }\n    var key = ch + (bias || \"\"), found;\n    if (prepared.cache.hasOwnProperty(key)) {\n      found = prepared.cache[key];\n    } else {\n      if (!prepared.rect)\n        { prepared.rect = prepared.view.text.getBoundingClientRect(); }\n      if (!prepared.hasHeights) {\n        ensureLineHeights(cm, prepared.view, prepared.rect);\n        prepared.hasHeights = true;\n      }\n      found = measureCharInner(cm, prepared, ch, bias);\n      if (!found.bogus) { prepared.cache[key] = found; }\n    }\n    return {left: found.left, right: found.right,\n            top: varHeight ? found.rtop : found.top,\n            bottom: varHeight ? found.rbottom : found.bottom}\n  }\n\n  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};\n\n  function nodeAndOffsetInLineMap(map$$1, ch, bias) {\n    var node, start, end, collapse, mStart, mEnd;\n    // First, search the line map for the text node corresponding to,\n    // or closest to, the target character.\n    for (var i = 0; i < map$$1.length; i += 3) {\n      mStart = map$$1[i];\n      mEnd = map$$1[i + 1];\n      if (ch < mStart) {\n        start = 0; end = 1;\n        collapse = \"left\";\n      } else if (ch < mEnd) {\n        start = ch - mStart;\n        end = start + 1;\n      } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {\n        end = mEnd - mStart;\n        start = end - 1;\n        if (ch >= mEnd) { collapse = \"right\"; }\n      }\n      if (start != null) {\n        node = map$$1[i + 2];\n        if (mStart == mEnd && bias == (node.insertLeft ? \"left\" : \"right\"))\n          { collapse = bias; }\n        if (bias == \"left\" && start == 0)\n          { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {\n            node = map$$1[(i -= 3) + 2];\n            collapse = \"left\";\n          } }\n        if (bias == \"right\" && start == mEnd - mStart)\n          { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {\n            node = map$$1[(i += 3) + 2];\n            collapse = \"right\";\n          } }\n        break\n      }\n    }\n    return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}\n  }\n\n  function getUsefulRect(rects, bias) {\n    var rect = nullRect;\n    if (bias == \"left\") { for (var i = 0; i < rects.length; i++) {\n      if ((rect = rects[i]).left != rect.right) { break }\n    } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {\n      if ((rect = rects[i$1]).left != rect.right) { break }\n    } }\n    return rect\n  }\n\n  function measureCharInner(cm, prepared, ch, bias) {\n    var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);\n    var node = place.node, start = place.start, end = place.end, collapse = place.collapse;\n\n    var rect;\n    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.\n      for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned\n        while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }\n        while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }\n        if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)\n          { rect = node.parentNode.getBoundingClientRect(); }\n        else\n          { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }\n        if (rect.left || rect.right || start == 0) { break }\n        end = start;\n        start = start - 1;\n        collapse = \"right\";\n      }\n      if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }\n    } else { // If it is a widget, simply get the box for the whole widget.\n      if (start > 0) { collapse = bias = \"right\"; }\n      var rects;\n      if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)\n        { rect = rects[bias == \"right\" ? rects.length - 1 : 0]; }\n      else\n        { rect = node.getBoundingClientRect(); }\n    }\n    if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {\n      var rSpan = node.parentNode.getClientRects()[0];\n      if (rSpan)\n        { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }\n      else\n        { rect = nullRect; }\n    }\n\n    var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;\n    var mid = (rtop + rbot) / 2;\n    var heights = prepared.view.measure.heights;\n    var i = 0;\n    for (; i < heights.length - 1; i++)\n      { if (mid < heights[i]) { break } }\n    var top = i ? heights[i - 1] : 0, bot = heights[i];\n    var result = {left: (collapse == \"right\" ? rect.right : rect.left) - prepared.rect.left,\n                  right: (collapse == \"left\" ? rect.left : rect.right) - prepared.rect.left,\n                  top: top, bottom: bot};\n    if (!rect.left && !rect.right) { result.bogus = true; }\n    if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }\n\n    return result\n  }\n\n  // Work around problem with bounding client rects on ranges being\n  // returned incorrectly when zoomed on IE10 and below.\n  function maybeUpdateRectForZooming(measure, rect) {\n    if (!window.screen || screen.logicalXDPI == null ||\n        screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))\n      { return rect }\n    var scaleX = screen.logicalXDPI / screen.deviceXDPI;\n    var scaleY = screen.logicalYDPI / screen.deviceYDPI;\n    return {left: rect.left * scaleX, right: rect.right * scaleX,\n            top: rect.top * scaleY, bottom: rect.bottom * scaleY}\n  }\n\n  function clearLineMeasurementCacheFor(lineView) {\n    if (lineView.measure) {\n      lineView.measure.cache = {};\n      lineView.measure.heights = null;\n      if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)\n        { lineView.measure.caches[i] = {}; } }\n    }\n  }\n\n  function clearLineMeasurementCache(cm) {\n    cm.display.externalMeasure = null;\n    removeChildren(cm.display.lineMeasure);\n    for (var i = 0; i < cm.display.view.length; i++)\n      { clearLineMeasurementCacheFor(cm.display.view[i]); }\n  }\n\n  function clearCaches(cm) {\n    clearLineMeasurementCache(cm);\n    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;\n    if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }\n    cm.display.lineNumChars = null;\n  }\n\n  function pageScrollX() {\n    // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206\n    // which causes page_Offset and bounding client rects to use\n    // different reference viewports and invalidate our calculations.\n    if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }\n    return window.pageXOffset || (document.documentElement || document.body).scrollLeft\n  }\n  function pageScrollY() {\n    if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }\n    return window.pageYOffset || (document.documentElement || document.body).scrollTop\n  }\n\n  function widgetTopHeight(lineObj) {\n    var height = 0;\n    if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)\n      { height += widgetHeight(lineObj.widgets[i]); } } }\n    return height\n  }\n\n  // Converts a {top, bottom, left, right} box from line-local\n  // coordinates into another coordinate system. Context may be one of\n  // \"line\", \"div\" (display.lineDiv), \"local\"./null (editor), \"window\",\n  // or \"page\".\n  function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {\n    if (!includeWidgets) {\n      var height = widgetTopHeight(lineObj);\n      rect.top += height; rect.bottom += height;\n    }\n    if (context == \"line\") { return rect }\n    if (!context) { context = \"local\"; }\n    var yOff = heightAtLine(lineObj);\n    if (context == \"local\") { yOff += paddingTop(cm.display); }\n    else { yOff -= cm.display.viewOffset; }\n    if (context == \"page\" || context == \"window\") {\n      var lOff = cm.display.lineSpace.getBoundingClientRect();\n      yOff += lOff.top + (context == \"window\" ? 0 : pageScrollY());\n      var xOff = lOff.left + (context == \"window\" ? 0 : pageScrollX());\n      rect.left += xOff; rect.right += xOff;\n    }\n    rect.top += yOff; rect.bottom += yOff;\n    return rect\n  }\n\n  // Coverts a box from \"div\" coords to another coordinate system.\n  // Context may be \"window\", \"page\", \"div\", or \"local\"./null.\n  function fromCoordSystem(cm, coords, context) {\n    if (context == \"div\") { return coords }\n    var left = coords.left, top = coords.top;\n    // First move into \"page\" coordinate system\n    if (context == \"page\") {\n      left -= pageScrollX();\n      top -= pageScrollY();\n    } else if (context == \"local\" || !context) {\n      var localBox = cm.display.sizer.getBoundingClientRect();\n      left += localBox.left;\n      top += localBox.top;\n    }\n\n    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();\n    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}\n  }\n\n  function charCoords(cm, pos, context, lineObj, bias) {\n    if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }\n    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)\n  }\n\n  // Returns a box for a given cursor position, which may have an\n  // 'other' property containing the position of the secondary cursor\n  // on a bidi boundary.\n  // A cursor Pos(line, char, \"before\") is on the same visual line as `char - 1`\n  // and after `char - 1` in writing order of `char - 1`\n  // A cursor Pos(line, char, \"after\") is on the same visual line as `char`\n  // and before `char` in writing order of `char`\n  // Examples (upper-case letters are RTL, lower-case are LTR):\n  //     Pos(0, 1, ...)\n  //     before   after\n  // ab     a|b     a|b\n  // aB     a|B     aB|\n  // Ab     |Ab     A|b\n  // AB     B|A     B|A\n  // Every position after the last character on a line is considered to stick\n  // to the last character on the line.\n  function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {\n    lineObj = lineObj || getLine(cm.doc, pos.line);\n    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }\n    function get(ch, right) {\n      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? \"right\" : \"left\", varHeight);\n      if (right) { m.left = m.right; } else { m.right = m.left; }\n      return intoCoordSystem(cm, lineObj, m, context)\n    }\n    var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;\n    if (ch >= lineObj.text.length) {\n      ch = lineObj.text.length;\n      sticky = \"before\";\n    } else if (ch <= 0) {\n      ch = 0;\n      sticky = \"after\";\n    }\n    if (!order) { return get(sticky == \"before\" ? ch - 1 : ch, sticky == \"before\") }\n\n    function getBidi(ch, partPos, invert) {\n      var part = order[partPos], right = part.level == 1;\n      return get(invert ? ch - 1 : ch, right != invert)\n    }\n    var partPos = getBidiPartAt(order, ch, sticky);\n    var other = bidiOther;\n    var val = getBidi(ch, partPos, sticky == \"before\");\n    if (other != null) { val.other = getBidi(ch, other, sticky != \"before\"); }\n    return val\n  }\n\n  // Used to cheaply estimate the coordinates for a position. Used for\n  // intermediate scroll updates.\n  function estimateCoords(cm, pos) {\n    var left = 0;\n    pos = clipPos(cm.doc, pos);\n    if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }\n    var lineObj = getLine(cm.doc, pos.line);\n    var top = heightAtLine(lineObj) + paddingTop(cm.display);\n    return {left: left, right: left, top: top, bottom: top + lineObj.height}\n  }\n\n  // Positions returned by coordsChar contain some extra information.\n  // xRel is the relative x position of the input coordinates compared\n  // to the found position (so xRel > 0 means the coordinates are to\n  // the right of the character position, for example). When outside\n  // is true, that means the coordinates lie outside the line's\n  // vertical range.\n  function PosWithInfo(line, ch, sticky, outside, xRel) {\n    var pos = Pos(line, ch, sticky);\n    pos.xRel = xRel;\n    if (outside) { pos.outside = true; }\n    return pos\n  }\n\n  // Compute the character position closest to the given coordinates.\n  // Input must be lineSpace-local (\"div\" coordinate system).\n  function coordsChar(cm, x, y) {\n    var doc = cm.doc;\n    y += cm.display.viewOffset;\n    if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }\n    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;\n    if (lineN > last)\n      { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) }\n    if (x < 0) { x = 0; }\n\n    var lineObj = getLine(doc, lineN);\n    for (;;) {\n      var found = coordsCharInner(cm, lineObj, lineN, x, y);\n      var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 ? 1 : 0));\n      if (!collapsed) { return found }\n      var rangeEnd = collapsed.find(1);\n      if (rangeEnd.line == lineN) { return rangeEnd }\n      lineObj = getLine(doc, lineN = rangeEnd.line);\n    }\n  }\n\n  function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {\n    y -= widgetTopHeight(lineObj);\n    var end = lineObj.text.length;\n    var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);\n    end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);\n    return {begin: begin, end: end}\n  }\n\n  function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {\n    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }\n    var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), \"line\").top;\n    return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)\n  }\n\n  // Returns true if the given side of a box is after the given\n  // coordinates, in top-to-bottom, left-to-right order.\n  function boxIsAfter(box, x, y, left) {\n    return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x\n  }\n\n  function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {\n    // Move y into line-local coordinate space\n    y -= heightAtLine(lineObj);\n    var preparedMeasure = prepareMeasureForLine(cm, lineObj);\n    // When directly calling `measureCharPrepared`, we have to adjust\n    // for the widgets at this line.\n    var widgetHeight$$1 = widgetTopHeight(lineObj);\n    var begin = 0, end = lineObj.text.length, ltr = true;\n\n    var order = getOrder(lineObj, cm.doc.direction);\n    // If the line isn't plain left-to-right text, first figure out\n    // which bidi section the coordinates fall into.\n    if (order) {\n      var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)\n                   (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y);\n      ltr = part.level != 1;\n      // The awkward -1 offsets are needed because findFirst (called\n      // on these below) will treat its first bound as inclusive,\n      // second as exclusive, but we want to actually address the\n      // characters in the part's range\n      begin = ltr ? part.from : part.to - 1;\n      end = ltr ? part.to : part.from - 1;\n    }\n\n    // A binary search to find the first character whose bounding box\n    // starts after the coordinates. If we run across any whose box wrap\n    // the coordinates, store that.\n    var chAround = null, boxAround = null;\n    var ch = findFirst(function (ch) {\n      var box = measureCharPrepared(cm, preparedMeasure, ch);\n      box.top += widgetHeight$$1; box.bottom += widgetHeight$$1;\n      if (!boxIsAfter(box, x, y, false)) { return false }\n      if (box.top <= y && box.left <= x) {\n        chAround = ch;\n        boxAround = box;\n      }\n      return true\n    }, begin, end);\n\n    var baseX, sticky, outside = false;\n    // If a box around the coordinates was found, use that\n    if (boxAround) {\n      // Distinguish coordinates nearer to the left or right side of the box\n      var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;\n      ch = chAround + (atStart ? 0 : 1);\n      sticky = atStart ? \"after\" : \"before\";\n      baseX = atLeft ? boxAround.left : boxAround.right;\n    } else {\n      // (Adjust for extended bound, if necessary.)\n      if (!ltr && (ch == end || ch == begin)) { ch++; }\n      // To determine which side to associate with, get the box to the\n      // left of the character and compare it's vertical position to the\n      // coordinates\n      sticky = ch == 0 ? \"after\" : ch == lineObj.text.length ? \"before\" :\n        (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ?\n        \"after\" : \"before\";\n      // Now get accurate coordinates for this place, in order to get a\n      // base X position\n      var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), \"line\", lineObj, preparedMeasure);\n      baseX = coords.left;\n      outside = y < coords.top || y >= coords.bottom;\n    }\n\n    ch = skipExtendingChars(lineObj.text, ch, 1);\n    return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX)\n  }\n\n  function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) {\n    // Bidi parts are sorted left-to-right, and in a non-line-wrapping\n    // situation, we can take this ordering to correspond to the visual\n    // ordering. This finds the first part whose end is after the given\n    // coordinates.\n    var index = findFirst(function (i) {\n      var part = order[i], ltr = part.level != 1;\n      return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? \"before\" : \"after\"),\n                                     \"line\", lineObj, preparedMeasure), x, y, true)\n    }, 0, order.length - 1);\n    var part = order[index];\n    // If this isn't the first part, the part's start is also after\n    // the coordinates, and the coordinates aren't on the same line as\n    // that start, move one part back.\n    if (index > 0) {\n      var ltr = part.level != 1;\n      var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? \"after\" : \"before\"),\n                               \"line\", lineObj, preparedMeasure);\n      if (boxIsAfter(start, x, y, true) && start.top > y)\n        { part = order[index - 1]; }\n    }\n    return part\n  }\n\n  function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {\n    // In a wrapped line, rtl text on wrapping boundaries can do things\n    // that don't correspond to the ordering in our `order` array at\n    // all, so a binary search doesn't work, and we want to return a\n    // part that only spans one line so that the binary search in\n    // coordsCharInner is safe. As such, we first find the extent of the\n    // wrapped line, and then do a flat search in which we discard any\n    // spans that aren't on the line.\n    var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);\n    var begin = ref.begin;\n    var end = ref.end;\n    if (/\\s/.test(lineObj.text.charAt(end - 1))) { end--; }\n    var part = null, closestDist = null;\n    for (var i = 0; i < order.length; i++) {\n      var p = order[i];\n      if (p.from >= end || p.to <= begin) { continue }\n      var ltr = p.level != 1;\n      var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;\n      // Weigh against spans ending before this, so that they are only\n      // picked if nothing ends after\n      var dist = endX < x ? x - endX + 1e9 : endX - x;\n      if (!part || closestDist > dist) {\n        part = p;\n        closestDist = dist;\n      }\n    }\n    if (!part) { part = order[order.length - 1]; }\n    // Clip the part to the wrapped line.\n    if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }\n    if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }\n    return part\n  }\n\n  var measureText;\n  // Compute the default text height.\n  function textHeight(display) {\n    if (display.cachedTextHeight != null) { return display.cachedTextHeight }\n    if (measureText == null) {\n      measureText = elt(\"pre\");\n      // Measure a bunch of lines, for browsers that compute\n      // fractional heights.\n      for (var i = 0; i < 49; ++i) {\n        measureText.appendChild(document.createTextNode(\"x\"));\n        measureText.appendChild(elt(\"br\"));\n      }\n      measureText.appendChild(document.createTextNode(\"x\"));\n    }\n    removeChildrenAndAdd(display.measure, measureText);\n    var height = measureText.offsetHeight / 50;\n    if (height > 3) { display.cachedTextHeight = height; }\n    removeChildren(display.measure);\n    return height || 1\n  }\n\n  // Compute the default character width.\n  function charWidth(display) {\n    if (display.cachedCharWidth != null) { return display.cachedCharWidth }\n    var anchor = elt(\"span\", \"xxxxxxxxxx\");\n    var pre = elt(\"pre\", [anchor]);\n    removeChildrenAndAdd(display.measure, pre);\n    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;\n    if (width > 2) { display.cachedCharWidth = width; }\n    return width || 10\n  }\n\n  // Do a bulk-read of the DOM positions and sizes needed to draw the\n  // view, so that we don't interleave reading and writing to the DOM.\n  function getDimensions(cm) {\n    var d = cm.display, left = {}, width = {};\n    var gutterLeft = d.gutters.clientLeft;\n    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {\n      left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;\n      width[cm.options.gutters[i]] = n.clientWidth;\n    }\n    return {fixedPos: compensateForHScroll(d),\n            gutterTotalWidth: d.gutters.offsetWidth,\n            gutterLeft: left,\n            gutterWidth: width,\n            wrapperWidth: d.wrapper.clientWidth}\n  }\n\n  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,\n  // but using getBoundingClientRect to get a sub-pixel-accurate\n  // result.\n  function compensateForHScroll(display) {\n    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left\n  }\n\n  // Returns a function that estimates the height of a line, to use as\n  // first approximation until the line becomes visible (and is thus\n  // properly measurable).\n  function estimateHeight(cm) {\n    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;\n    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);\n    return function (line) {\n      if (lineIsHidden(cm.doc, line)) { return 0 }\n\n      var widgetsHeight = 0;\n      if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {\n        if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }\n      } }\n\n      if (wrapping)\n        { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }\n      else\n        { return widgetsHeight + th }\n    }\n  }\n\n  function estimateLineHeights(cm) {\n    var doc = cm.doc, est = estimateHeight(cm);\n    doc.iter(function (line) {\n      var estHeight = est(line);\n      if (estHeight != line.height) { updateLineHeight(line, estHeight); }\n    });\n  }\n\n  // Given a mouse event, find the corresponding position. If liberal\n  // is false, it checks whether a gutter or scrollbar was clicked,\n  // and returns null if it was. forRect is used by rectangular\n  // selections, and tries to estimate a character position even for\n  // coordinates beyond the right of the text.\n  function posFromMouse(cm, e, liberal, forRect) {\n    var display = cm.display;\n    if (!liberal && e_target(e).getAttribute(\"cm-not-content\") == \"true\") { return null }\n\n    var x, y, space = display.lineSpace.getBoundingClientRect();\n    // Fails unpredictably on IE[67] when mouse is dragged around quickly.\n    try { x = e.clientX - space.left; y = e.clientY - space.top; }\n    catch (e) { return null }\n    var coords = coordsChar(cm, x, y), line;\n    if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {\n      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;\n      coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));\n    }\n    return coords\n  }\n\n  // Find the view element corresponding to a given line. Return null\n  // when the line isn't visible.\n  function findViewIndex(cm, n) {\n    if (n >= cm.display.viewTo) { return null }\n    n -= cm.display.viewFrom;\n    if (n < 0) { return null }\n    var view = cm.display.view;\n    for (var i = 0; i < view.length; i++) {\n      n -= view[i].size;\n      if (n < 0) { return i }\n    }\n  }\n\n  function updateSelection(cm) {\n    cm.display.input.showSelection(cm.display.input.prepareSelection());\n  }\n\n  function prepareSelection(cm, primary) {\n    if ( primary === void 0 ) primary = true;\n\n    var doc = cm.doc, result = {};\n    var curFragment = result.cursors = document.createDocumentFragment();\n    var selFragment = result.selection = document.createDocumentFragment();\n\n    for (var i = 0; i < doc.sel.ranges.length; i++) {\n      if (!primary && i == doc.sel.primIndex) { continue }\n      var range$$1 = doc.sel.ranges[i];\n      if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }\n      var collapsed = range$$1.empty();\n      if (collapsed || cm.options.showCursorWhenSelecting)\n        { drawSelectionCursor(cm, range$$1.head, curFragment); }\n      if (!collapsed)\n        { drawSelectionRange(cm, range$$1, selFragment); }\n    }\n    return result\n  }\n\n  // Draws a cursor for the given range\n  function drawSelectionCursor(cm, head, output) {\n    var pos = cursorCoords(cm, head, \"div\", null, null, !cm.options.singleCursorHeightPerLine);\n\n    var cursor = output.appendChild(elt(\"div\", \"\\u00a0\", \"CodeMirror-cursor\"));\n    cursor.style.left = pos.left + \"px\";\n    cursor.style.top = pos.top + \"px\";\n    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + \"px\";\n\n    if (pos.other) {\n      // Secondary cursor, shown when on a 'jump' in bi-directional text\n      var otherCursor = output.appendChild(elt(\"div\", \"\\u00a0\", \"CodeMirror-cursor CodeMirror-secondarycursor\"));\n      otherCursor.style.display = \"\";\n      otherCursor.style.left = pos.other.left + \"px\";\n      otherCursor.style.top = pos.other.top + \"px\";\n      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + \"px\";\n    }\n  }\n\n  function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }\n\n  // Draws the given range as a highlighted selection\n  function drawSelectionRange(cm, range$$1, output) {\n    var display = cm.display, doc = cm.doc;\n    var fragment = document.createDocumentFragment();\n    var padding = paddingH(cm.display), leftSide = padding.left;\n    var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;\n    var docLTR = doc.direction == \"ltr\";\n\n    function add(left, top, width, bottom) {\n      if (top < 0) { top = 0; }\n      top = Math.round(top);\n      bottom = Math.round(bottom);\n      fragment.appendChild(elt(\"div\", null, \"CodeMirror-selected\", (\"position: absolute; left: \" + left + \"px;\\n                             top: \" + top + \"px; width: \" + (width == null ? rightSide - left : width) + \"px;\\n                             height: \" + (bottom - top) + \"px\")));\n    }\n\n    function drawForLine(line, fromArg, toArg) {\n      var lineObj = getLine(doc, line);\n      var lineLen = lineObj.text.length;\n      var start, end;\n      function coords(ch, bias) {\n        return charCoords(cm, Pos(line, ch), \"div\", lineObj, bias)\n      }\n\n      function wrapX(pos, dir, side) {\n        var extent = wrappedLineExtentChar(cm, lineObj, null, pos);\n        var prop = (dir == \"ltr\") == (side == \"after\") ? \"left\" : \"right\";\n        var ch = side == \"after\" ? extent.begin : extent.end - (/\\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);\n        return coords(ch, prop)[prop]\n      }\n\n      var order = getOrder(lineObj, doc.direction);\n      iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {\n        var ltr = dir == \"ltr\";\n        var fromPos = coords(from, ltr ? \"left\" : \"right\");\n        var toPos = coords(to - 1, ltr ? \"right\" : \"left\");\n\n        var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;\n        var first = i == 0, last = !order || i == order.length - 1;\n        if (toPos.top - fromPos.top <= 3) { // Single line\n          var openLeft = (docLTR ? openStart : openEnd) && first;\n          var openRight = (docLTR ? openEnd : openStart) && last;\n          var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;\n          var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;\n          add(left, fromPos.top, right - left, fromPos.bottom);\n        } else { // Multiple lines\n          var topLeft, topRight, botLeft, botRight;\n          if (ltr) {\n            topLeft = docLTR && openStart && first ? leftSide : fromPos.left;\n            topRight = docLTR ? rightSide : wrapX(from, dir, \"before\");\n            botLeft = docLTR ? leftSide : wrapX(to, dir, \"after\");\n            botRight = docLTR && openEnd && last ? rightSide : toPos.right;\n          } else {\n            topLeft = !docLTR ? leftSide : wrapX(from, dir, \"before\");\n            topRight = !docLTR && openStart && first ? rightSide : fromPos.right;\n            botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;\n            botRight = !docLTR ? rightSide : wrapX(to, dir, \"after\");\n          }\n          add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);\n          if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }\n          add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);\n        }\n\n        if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }\n        if (cmpCoords(toPos, start) < 0) { start = toPos; }\n        if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }\n        if (cmpCoords(toPos, end) < 0) { end = toPos; }\n      });\n      return {start: start, end: end}\n    }\n\n    var sFrom = range$$1.from(), sTo = range$$1.to();\n    if (sFrom.line == sTo.line) {\n      drawForLine(sFrom.line, sFrom.ch, sTo.ch);\n    } else {\n      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);\n      var singleVLine = visualLine(fromLine) == visualLine(toLine);\n      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;\n      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;\n      if (singleVLine) {\n        if (leftEnd.top < rightStart.top - 2) {\n          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);\n          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);\n        } else {\n          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);\n        }\n      }\n      if (leftEnd.bottom < rightStart.top)\n        { add(leftSide, leftEnd.bottom, null, rightStart.top); }\n    }\n\n    output.appendChild(fragment);\n  }\n\n  // Cursor-blinking\n  function restartBlink(cm) {\n    if (!cm.state.focused) { return }\n    var display = cm.display;\n    clearInterval(display.blinker);\n    var on = true;\n    display.cursorDiv.style.visibility = \"\";\n    if (cm.options.cursorBlinkRate > 0)\n      { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? \"\" : \"hidden\"; },\n        cm.options.cursorBlinkRate); }\n    else if (cm.options.cursorBlinkRate < 0)\n      { display.cursorDiv.style.visibility = \"hidden\"; }\n  }\n\n  function ensureFocus(cm) {\n    if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }\n  }\n\n  function delayBlurEvent(cm) {\n    cm.state.delayingBlurEvent = true;\n    setTimeout(function () { if (cm.state.delayingBlurEvent) {\n      cm.state.delayingBlurEvent = false;\n      onBlur(cm);\n    } }, 100);\n  }\n\n  function onFocus(cm, e) {\n    if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }\n\n    if (cm.options.readOnly == \"nocursor\") { return }\n    if (!cm.state.focused) {\n      signal(cm, \"focus\", cm, e);\n      cm.state.focused = true;\n      addClass(cm.display.wrapper, \"CodeMirror-focused\");\n      // This test prevents this from firing when a context\n      // menu is closed (since the input reset would kill the\n      // select-all detection hack)\n      if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {\n        cm.display.input.reset();\n        if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730\n      }\n      cm.display.input.receivedFocus();\n    }\n    restartBlink(cm);\n  }\n  function onBlur(cm, e) {\n    if (cm.state.delayingBlurEvent) { return }\n\n    if (cm.state.focused) {\n      signal(cm, \"blur\", cm, e);\n      cm.state.focused = false;\n      rmClass(cm.display.wrapper, \"CodeMirror-focused\");\n    }\n    clearInterval(cm.display.blinker);\n    setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);\n  }\n\n  // Read the actual heights of the rendered lines, and update their\n  // stored heights to match.\n  function updateHeightsInViewport(cm) {\n    var display = cm.display;\n    var prevBottom = display.lineDiv.offsetTop;\n    for (var i = 0; i < display.view.length; i++) {\n      var cur = display.view[i], height = (void 0);\n      if (cur.hidden) { continue }\n      if (ie && ie_version < 8) {\n        var bot = cur.node.offsetTop + cur.node.offsetHeight;\n        height = bot - prevBottom;\n        prevBottom = bot;\n      } else {\n        var box = cur.node.getBoundingClientRect();\n        height = box.bottom - box.top;\n      }\n      var diff = cur.line.height - height;\n      if (height < 2) { height = textHeight(display); }\n      if (diff > .005 || diff < -.005) {\n        updateLineHeight(cur.line, height);\n        updateWidgetHeight(cur.line);\n        if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)\n          { updateWidgetHeight(cur.rest[j]); } }\n      }\n    }\n  }\n\n  // Read and store the height of line widgets associated with the\n  // given line.\n  function updateWidgetHeight(line) {\n    if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {\n      var w = line.widgets[i], parent = w.node.parentNode;\n      if (parent) { w.height = parent.offsetHeight; }\n    } }\n  }\n\n  // Compute the lines that are visible in a given viewport (defaults\n  // the the current scroll position). viewport may contain top,\n  // height, and ensure (see op.scrollToPos) properties.\n  function visibleLines(display, doc, viewport) {\n    var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;\n    top = Math.floor(top - paddingTop(display));\n    var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;\n\n    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);\n    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and\n    // forces those lines into the viewport (if possible).\n    if (viewport && viewport.ensure) {\n      var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;\n      if (ensureFrom < from) {\n        from = ensureFrom;\n        to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);\n      } else if (Math.min(ensureTo, doc.lastLine()) >= to) {\n        from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);\n        to = ensureTo;\n      }\n    }\n    return {from: from, to: Math.max(to, from + 1)}\n  }\n\n  // Re-align line numbers and gutter marks to compensate for\n  // horizontal scrolling.\n  function alignHorizontally(cm) {\n    var display = cm.display, view = display.view;\n    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }\n    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;\n    var gutterW = display.gutters.offsetWidth, left = comp + \"px\";\n    for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {\n      if (cm.options.fixedGutter) {\n        if (view[i].gutter)\n          { view[i].gutter.style.left = left; }\n        if (view[i].gutterBackground)\n          { view[i].gutterBackground.style.left = left; }\n      }\n      var align = view[i].alignable;\n      if (align) { for (var j = 0; j < align.length; j++)\n        { align[j].style.left = left; } }\n    } }\n    if (cm.options.fixedGutter)\n      { display.gutters.style.left = (comp + gutterW) + \"px\"; }\n  }\n\n  // Used to ensure that the line number gutter is still the right\n  // size for the current document size. Returns true when an update\n  // is needed.\n  function maybeUpdateLineNumberWidth(cm) {\n    if (!cm.options.lineNumbers) { return false }\n    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;\n    if (last.length != display.lineNumChars) {\n      var test = display.measure.appendChild(elt(\"div\", [elt(\"div\", last)],\n                                                 \"CodeMirror-linenumber CodeMirror-gutter-elt\"));\n      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;\n      display.lineGutter.style.width = \"\";\n      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;\n      display.lineNumWidth = display.lineNumInnerWidth + padding;\n      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;\n      display.lineGutter.style.width = display.lineNumWidth + \"px\";\n      updateGutterSpace(cm);\n      return true\n    }\n    return false\n  }\n\n  // SCROLLING THINGS INTO VIEW\n\n  // If an editor sits on the top or bottom of the window, partially\n  // scrolled out of view, this ensures that the cursor is visible.\n  function maybeScrollWindow(cm, rect) {\n    if (signalDOMEvent(cm, \"scrollCursorIntoView\")) { return }\n\n    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;\n    if (rect.top + box.top < 0) { doScroll = true; }\n    else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }\n    if (doScroll != null && !phantom) {\n      var scrollNode = elt(\"div\", \"\\u200b\", null, (\"position: absolute;\\n                         top: \" + (rect.top - display.viewOffset - paddingTop(cm.display)) + \"px;\\n                         height: \" + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + \"px;\\n                         left: \" + (rect.left) + \"px; width: \" + (Math.max(2, rect.right - rect.left)) + \"px;\"));\n      cm.display.lineSpace.appendChild(scrollNode);\n      scrollNode.scrollIntoView(doScroll);\n      cm.display.lineSpace.removeChild(scrollNode);\n    }\n  }\n\n  // Scroll a given position into view (immediately), verifying that\n  // it actually became visible (as line heights are accurately\n  // measured, the position of something may 'drift' during drawing).\n  function scrollPosIntoView(cm, pos, end, margin) {\n    if (margin == null) { margin = 0; }\n    var rect;\n    if (!cm.options.lineWrapping && pos == end) {\n      // Set pos and end to the cursor positions around the character pos sticks to\n      // If pos.sticky == \"before\", that is around pos.ch - 1, otherwise around pos.ch\n      // If pos == Pos(_, 0, \"before\"), pos and end are unchanged\n      pos = pos.ch ? Pos(pos.line, pos.sticky == \"before\" ? pos.ch - 1 : pos.ch, \"after\") : pos;\n      end = pos.sticky == \"before\" ? Pos(pos.line, pos.ch + 1, \"before\") : pos;\n    }\n    for (var limit = 0; limit < 5; limit++) {\n      var changed = false;\n      var coords = cursorCoords(cm, pos);\n      var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);\n      rect = {left: Math.min(coords.left, endCoords.left),\n              top: Math.min(coords.top, endCoords.top) - margin,\n              right: Math.max(coords.left, endCoords.left),\n              bottom: Math.max(coords.bottom, endCoords.bottom) + margin};\n      var scrollPos = calculateScrollPos(cm, rect);\n      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;\n      if (scrollPos.scrollTop != null) {\n        updateScrollTop(cm, scrollPos.scrollTop);\n        if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }\n      }\n      if (scrollPos.scrollLeft != null) {\n        setScrollLeft(cm, scrollPos.scrollLeft);\n        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }\n      }\n      if (!changed) { break }\n    }\n    return rect\n  }\n\n  // Scroll a given set of coordinates into view (immediately).\n  function scrollIntoView(cm, rect) {\n    var scrollPos = calculateScrollPos(cm, rect);\n    if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }\n    if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }\n  }\n\n  // Calculate a new scroll position needed to scroll the given\n  // rectangle into view. Returns an object with scrollTop and\n  // scrollLeft properties. When these are undefined, the\n  // vertical/horizontal position does not need to be adjusted.\n  function calculateScrollPos(cm, rect) {\n    var display = cm.display, snapMargin = textHeight(cm.display);\n    if (rect.top < 0) { rect.top = 0; }\n    var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;\n    var screen = displayHeight(cm), result = {};\n    if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }\n    var docBottom = cm.doc.height + paddingVert(display);\n    var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;\n    if (rect.top < screentop) {\n      result.scrollTop = atTop ? 0 : rect.top;\n    } else if (rect.bottom > screentop + screen) {\n      var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);\n      if (newTop != screentop) { result.scrollTop = newTop; }\n    }\n\n    var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;\n    var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);\n    var tooWide = rect.right - rect.left > screenw;\n    if (tooWide) { rect.right = rect.left + screenw; }\n    if (rect.left < 10)\n      { result.scrollLeft = 0; }\n    else if (rect.left < screenleft)\n      { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }\n    else if (rect.right > screenw + screenleft - 3)\n      { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }\n    return result\n  }\n\n  // Store a relative adjustment to the scroll position in the current\n  // operation (to be applied when the operation finishes).\n  function addToScrollTop(cm, top) {\n    if (top == null) { return }\n    resolveScrollToPos(cm);\n    cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;\n  }\n\n  // Make sure that at the end of the operation the current cursor is\n  // shown.\n  function ensureCursorVisible(cm) {\n    resolveScrollToPos(cm);\n    var cur = cm.getCursor();\n    cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};\n  }\n\n  function scrollToCoords(cm, x, y) {\n    if (x != null || y != null) { resolveScrollToPos(cm); }\n    if (x != null) { cm.curOp.scrollLeft = x; }\n    if (y != null) { cm.curOp.scrollTop = y; }\n  }\n\n  function scrollToRange(cm, range$$1) {\n    resolveScrollToPos(cm);\n    cm.curOp.scrollToPos = range$$1;\n  }\n\n  // When an operation has its scrollToPos property set, and another\n  // scroll action is applied before the end of the operation, this\n  // 'simulates' scrolling that position into view in a cheap way, so\n  // that the effect of intermediate scroll commands is not ignored.\n  function resolveScrollToPos(cm) {\n    var range$$1 = cm.curOp.scrollToPos;\n    if (range$$1) {\n      cm.curOp.scrollToPos = null;\n      var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to);\n      scrollToCoordsRange(cm, from, to, range$$1.margin);\n    }\n  }\n\n  function scrollToCoordsRange(cm, from, to, margin) {\n    var sPos = calculateScrollPos(cm, {\n      left: Math.min(from.left, to.left),\n      top: Math.min(from.top, to.top) - margin,\n      right: Math.max(from.right, to.right),\n      bottom: Math.max(from.bottom, to.bottom) + margin\n    });\n    scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);\n  }\n\n  // Sync the scrollable area and scrollbars, ensure the viewport\n  // covers the visible area.\n  function updateScrollTop(cm, val) {\n    if (Math.abs(cm.doc.scrollTop - val) < 2) { return }\n    if (!gecko) { updateDisplaySimple(cm, {top: val}); }\n    setScrollTop(cm, val, true);\n    if (gecko) { updateDisplaySimple(cm); }\n    startWorker(cm, 100);\n  }\n\n  function setScrollTop(cm, val, forceScroll) {\n    val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val);\n    if (cm.display.scroller.scrollTop == val && !forceScroll) { return }\n    cm.doc.scrollTop = val;\n    cm.display.scrollbars.setScrollTop(val);\n    if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }\n  }\n\n  // Sync scroller and scrollbar, ensure the gutter elements are\n  // aligned.\n  function setScrollLeft(cm, val, isScroller, forceScroll) {\n    val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);\n    if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }\n    cm.doc.scrollLeft = val;\n    alignHorizontally(cm);\n    if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }\n    cm.display.scrollbars.setScrollLeft(val);\n  }\n\n  // SCROLLBARS\n\n  // Prepare DOM reads needed to update the scrollbars. Done in one\n  // shot to minimize update/measure roundtrips.\n  function measureForScrollbars(cm) {\n    var d = cm.display, gutterW = d.gutters.offsetWidth;\n    var docH = Math.round(cm.doc.height + paddingVert(cm.display));\n    return {\n      clientHeight: d.scroller.clientHeight,\n      viewHeight: d.wrapper.clientHeight,\n      scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,\n      viewWidth: d.wrapper.clientWidth,\n      barLeft: cm.options.fixedGutter ? gutterW : 0,\n      docHeight: docH,\n      scrollHeight: docH + scrollGap(cm) + d.barHeight,\n      nativeBarWidth: d.nativeBarWidth,\n      gutterWidth: gutterW\n    }\n  }\n\n  var NativeScrollbars = function(place, scroll, cm) {\n    this.cm = cm;\n    var vert = this.vert = elt(\"div\", [elt(\"div\", null, null, \"min-width: 1px\")], \"CodeMirror-vscrollbar\");\n    var horiz = this.horiz = elt(\"div\", [elt(\"div\", null, null, \"height: 100%; min-height: 1px\")], \"CodeMirror-hscrollbar\");\n    vert.tabIndex = horiz.tabIndex = -1;\n    place(vert); place(horiz);\n\n    on(vert, \"scroll\", function () {\n      if (vert.clientHeight) { scroll(vert.scrollTop, \"vertical\"); }\n    });\n    on(horiz, \"scroll\", function () {\n      if (horiz.clientWidth) { scroll(horiz.scrollLeft, \"horizontal\"); }\n    });\n\n    this.checkedZeroWidth = false;\n    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).\n    if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = \"18px\"; }\n  };\n\n  NativeScrollbars.prototype.update = function (measure) {\n    var needsH = measure.scrollWidth > measure.clientWidth + 1;\n    var needsV = measure.scrollHeight > measure.clientHeight + 1;\n    var sWidth = measure.nativeBarWidth;\n\n    if (needsV) {\n      this.vert.style.display = \"block\";\n      this.vert.style.bottom = needsH ? sWidth + \"px\" : \"0\";\n      var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);\n      // A bug in IE8 can cause this value to be negative, so guard it.\n      this.vert.firstChild.style.height =\n        Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + \"px\";\n    } else {\n      this.vert.style.display = \"\";\n      this.vert.firstChild.style.height = \"0\";\n    }\n\n    if (needsH) {\n      this.horiz.style.display = \"block\";\n      this.horiz.style.right = needsV ? sWidth + \"px\" : \"0\";\n      this.horiz.style.left = measure.barLeft + \"px\";\n      var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);\n      this.horiz.firstChild.style.width =\n        Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + \"px\";\n    } else {\n      this.horiz.style.display = \"\";\n      this.horiz.firstChild.style.width = \"0\";\n    }\n\n    if (!this.checkedZeroWidth && measure.clientHeight > 0) {\n      if (sWidth == 0) { this.zeroWidthHack(); }\n      this.checkedZeroWidth = true;\n    }\n\n    return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}\n  };\n\n  NativeScrollbars.prototype.setScrollLeft = function (pos) {\n    if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }\n    if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, \"horiz\"); }\n  };\n\n  NativeScrollbars.prototype.setScrollTop = function (pos) {\n    if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }\n    if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, \"vert\"); }\n  };\n\n  NativeScrollbars.prototype.zeroWidthHack = function () {\n    var w = mac && !mac_geMountainLion ? \"12px\" : \"18px\";\n    this.horiz.style.height = this.vert.style.width = w;\n    this.horiz.style.pointerEvents = this.vert.style.pointerEvents = \"none\";\n    this.disableHoriz = new Delayed;\n    this.disableVert = new Delayed;\n  };\n\n  NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {\n    bar.style.pointerEvents = \"auto\";\n    function maybeDisable() {\n      // To find out whether the scrollbar is still visible, we\n      // check whether the element under the pixel in the bottom\n      // right corner of the scrollbar box is the scrollbar box\n      // itself (when the bar is still visible) or its filler child\n      // (when the bar is hidden). If it is still visible, we keep\n      // it enabled, if it's hidden, we disable pointer events.\n      var box = bar.getBoundingClientRect();\n      var elt$$1 = type == \"vert\" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)\n          : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);\n      if (elt$$1 != bar) { bar.style.pointerEvents = \"none\"; }\n      else { delay.set(1000, maybeDisable); }\n    }\n    delay.set(1000, maybeDisable);\n  };\n\n  NativeScrollbars.prototype.clear = function () {\n    var parent = this.horiz.parentNode;\n    parent.removeChild(this.horiz);\n    parent.removeChild(this.vert);\n  };\n\n  var NullScrollbars = function () {};\n\n  NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };\n  NullScrollbars.prototype.setScrollLeft = function () {};\n  NullScrollbars.prototype.setScrollTop = function () {};\n  NullScrollbars.prototype.clear = function () {};\n\n  function updateScrollbars(cm, measure) {\n    if (!measure) { measure = measureForScrollbars(cm); }\n    var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;\n    updateScrollbarsInner(cm, measure);\n    for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {\n      if (startWidth != cm.display.barWidth && cm.options.lineWrapping)\n        { updateHeightsInViewport(cm); }\n      updateScrollbarsInner(cm, measureForScrollbars(cm));\n      startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;\n    }\n  }\n\n  // Re-synchronize the fake scrollbars with the actual size of the\n  // content.\n  function updateScrollbarsInner(cm, measure) {\n    var d = cm.display;\n    var sizes = d.scrollbars.update(measure);\n\n    d.sizer.style.paddingRight = (d.barWidth = sizes.right) + \"px\";\n    d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + \"px\";\n    d.heightForcer.style.borderBottom = sizes.bottom + \"px solid transparent\";\n\n    if (sizes.right && sizes.bottom) {\n      d.scrollbarFiller.style.display = \"block\";\n      d.scrollbarFiller.style.height = sizes.bottom + \"px\";\n      d.scrollbarFiller.style.width = sizes.right + \"px\";\n    } else { d.scrollbarFiller.style.display = \"\"; }\n    if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {\n      d.gutterFiller.style.display = \"block\";\n      d.gutterFiller.style.height = sizes.bottom + \"px\";\n      d.gutterFiller.style.width = measure.gutterWidth + \"px\";\n    } else { d.gutterFiller.style.display = \"\"; }\n  }\n\n  var scrollbarModel = {\"native\": NativeScrollbars, \"null\": NullScrollbars};\n\n  function initScrollbars(cm) {\n    if (cm.display.scrollbars) {\n      cm.display.scrollbars.clear();\n      if (cm.display.scrollbars.addClass)\n        { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }\n    }\n\n    cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {\n      cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);\n      // Prevent clicks in the scrollbars from killing focus\n      on(node, \"mousedown\", function () {\n        if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }\n      });\n      node.setAttribute(\"cm-not-content\", \"true\");\n    }, function (pos, axis) {\n      if (axis == \"horizontal\") { setScrollLeft(cm, pos); }\n      else { updateScrollTop(cm, pos); }\n    }, cm);\n    if (cm.display.scrollbars.addClass)\n      { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }\n  }\n\n  // Operations are used to wrap a series of changes to the editor\n  // state in such a way that each change won't have to update the\n  // cursor and display (which would be awkward, slow, and\n  // error-prone). Instead, display updates are batched and then all\n  // combined and executed at once.\n\n  var nextOpId = 0;\n  // Start a new operation.\n  function startOperation(cm) {\n    cm.curOp = {\n      cm: cm,\n      viewChanged: false,      // Flag that indicates that lines might need to be redrawn\n      startHeight: cm.doc.height, // Used to detect need to update scrollbar\n      forceUpdate: false,      // Used to force a redraw\n      updateInput: null,       // Whether to reset the input textarea\n      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)\n      changeObjs: null,        // Accumulated changes, for firing change events\n      cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on\n      cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already\n      selectionChanged: false, // Whether the selection needs to be redrawn\n      updateMaxLine: false,    // Set when the widest line needs to be determined anew\n      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet\n      scrollToPos: null,       // Used to scroll to a specific position\n      focus: false,\n      id: ++nextOpId           // Unique ID\n    };\n    pushOperation(cm.curOp);\n  }\n\n  // Finish an operation, updating the display and signalling delayed events\n  function endOperation(cm) {\n    var op = cm.curOp;\n    if (op) { finishOperation(op, function (group) {\n      for (var i = 0; i < group.ops.length; i++)\n        { group.ops[i].cm.curOp = null; }\n      endOperations(group);\n    }); }\n  }\n\n  // The DOM updates done when an operation finishes are batched so\n  // that the minimum number of relayouts are required.\n  function endOperations(group) {\n    var ops = group.ops;\n    for (var i = 0; i < ops.length; i++) // Read DOM\n      { endOperation_R1(ops[i]); }\n    for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)\n      { endOperation_W1(ops[i$1]); }\n    for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM\n      { endOperation_R2(ops[i$2]); }\n    for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)\n      { endOperation_W2(ops[i$3]); }\n    for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM\n      { endOperation_finish(ops[i$4]); }\n  }\n\n  function endOperation_R1(op) {\n    var cm = op.cm, display = cm.display;\n    maybeClipScrollbars(cm);\n    if (op.updateMaxLine) { findMaxLine(cm); }\n\n    op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||\n      op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||\n                         op.scrollToPos.to.line >= display.viewTo) ||\n      display.maxLineChanged && cm.options.lineWrapping;\n    op.update = op.mustUpdate &&\n      new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);\n  }\n\n  function endOperation_W1(op) {\n    op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);\n  }\n\n  function endOperation_R2(op) {\n    var cm = op.cm, display = cm.display;\n    if (op.updatedDisplay) { updateHeightsInViewport(cm); }\n\n    op.barMeasure = measureForScrollbars(cm);\n\n    // If the max line changed since it was last measured, measure it,\n    // and ensure the document's width matches it.\n    // updateDisplay_W2 will use these properties to do the actual resizing\n    if (display.maxLineChanged && !cm.options.lineWrapping) {\n      op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;\n      cm.display.sizerWidth = op.adjustWidthTo;\n      op.barMeasure.scrollWidth =\n        Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);\n      op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));\n    }\n\n    if (op.updatedDisplay || op.selectionChanged)\n      { op.preparedSelection = display.input.prepareSelection(); }\n  }\n\n  function endOperation_W2(op) {\n    var cm = op.cm;\n\n    if (op.adjustWidthTo != null) {\n      cm.display.sizer.style.minWidth = op.adjustWidthTo + \"px\";\n      if (op.maxScrollLeft < cm.doc.scrollLeft)\n        { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }\n      cm.display.maxLineChanged = false;\n    }\n\n    var takeFocus = op.focus && op.focus == activeElt();\n    if (op.preparedSelection)\n      { cm.display.input.showSelection(op.preparedSelection, takeFocus); }\n    if (op.updatedDisplay || op.startHeight != cm.doc.height)\n      { updateScrollbars(cm, op.barMeasure); }\n    if (op.updatedDisplay)\n      { setDocumentHeight(cm, op.barMeasure); }\n\n    if (op.selectionChanged) { restartBlink(cm); }\n\n    if (cm.state.focused && op.updateInput)\n      { cm.display.input.reset(op.typing); }\n    if (takeFocus) { ensureFocus(op.cm); }\n  }\n\n  function endOperation_finish(op) {\n    var cm = op.cm, display = cm.display, doc = cm.doc;\n\n    if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }\n\n    // Abort mouse wheel delta measurement, when scrolling explicitly\n    if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))\n      { display.wheelStartX = display.wheelStartY = null; }\n\n    // Propagate the scroll position to the actual DOM scroller\n    if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }\n\n    if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }\n    // If we need to scroll a specific position into view, do so.\n    if (op.scrollToPos) {\n      var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),\n                                   clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);\n      maybeScrollWindow(cm, rect);\n    }\n\n    // Fire events for markers that are hidden/unidden by editing or\n    // undoing\n    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;\n    if (hidden) { for (var i = 0; i < hidden.length; ++i)\n      { if (!hidden[i].lines.length) { signal(hidden[i], \"hide\"); } } }\n    if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)\n      { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], \"unhide\"); } } }\n\n    if (display.wrapper.offsetHeight)\n      { doc.scrollTop = cm.display.scroller.scrollTop; }\n\n    // Fire change events, and delayed event handlers\n    if (op.changeObjs)\n      { signal(cm, \"changes\", cm, op.changeObjs); }\n    if (op.update)\n      { op.update.finish(); }\n  }\n\n  // Run the given function in an operation\n  function runInOp(cm, f) {\n    if (cm.curOp) { return f() }\n    startOperation(cm);\n    try { return f() }\n    finally { endOperation(cm); }\n  }\n  // Wraps a function in an operation. Returns the wrapped function.\n  function operation(cm, f) {\n    return function() {\n      if (cm.curOp) { return f.apply(cm, arguments) }\n      startOperation(cm);\n      try { return f.apply(cm, arguments) }\n      finally { endOperation(cm); }\n    }\n  }\n  // Used to add methods to editor and doc instances, wrapping them in\n  // operations.\n  function methodOp(f) {\n    return function() {\n      if (this.curOp) { return f.apply(this, arguments) }\n      startOperation(this);\n      try { return f.apply(this, arguments) }\n      finally { endOperation(this); }\n    }\n  }\n  function docMethodOp(f) {\n    return function() {\n      var cm = this.cm;\n      if (!cm || cm.curOp) { return f.apply(this, arguments) }\n      startOperation(cm);\n      try { return f.apply(this, arguments) }\n      finally { endOperation(cm); }\n    }\n  }\n\n  // Updates the display.view data structure for a given change to the\n  // document. From and to are in pre-change coordinates. Lendiff is\n  // the amount of lines added or subtracted by the change. This is\n  // used for changes that span multiple lines, or change the way\n  // lines are divided into visual lines. regLineChange (below)\n  // registers single-line changes.\n  function regChange(cm, from, to, lendiff) {\n    if (from == null) { from = cm.doc.first; }\n    if (to == null) { to = cm.doc.first + cm.doc.size; }\n    if (!lendiff) { lendiff = 0; }\n\n    var display = cm.display;\n    if (lendiff && to < display.viewTo &&\n        (display.updateLineNumbers == null || display.updateLineNumbers > from))\n      { display.updateLineNumbers = from; }\n\n    cm.curOp.viewChanged = true;\n\n    if (from >= display.viewTo) { // Change after\n      if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)\n        { resetView(cm); }\n    } else if (to <= display.viewFrom) { // Change before\n      if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {\n        resetView(cm);\n      } else {\n        display.viewFrom += lendiff;\n        display.viewTo += lendiff;\n      }\n    } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap\n      resetView(cm);\n    } else if (from <= display.viewFrom) { // Top overlap\n      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);\n      if (cut) {\n        display.view = display.view.slice(cut.index);\n        display.viewFrom = cut.lineN;\n        display.viewTo += lendiff;\n      } else {\n        resetView(cm);\n      }\n    } else if (to >= display.viewTo) { // Bottom overlap\n      var cut$1 = viewCuttingPoint(cm, from, from, -1);\n      if (cut$1) {\n        display.view = display.view.slice(0, cut$1.index);\n        display.viewTo = cut$1.lineN;\n      } else {\n        resetView(cm);\n      }\n    } else { // Gap in the middle\n      var cutTop = viewCuttingPoint(cm, from, from, -1);\n      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);\n      if (cutTop && cutBot) {\n        display.view = display.view.slice(0, cutTop.index)\n          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))\n          .concat(display.view.slice(cutBot.index));\n        display.viewTo += lendiff;\n      } else {\n        resetView(cm);\n      }\n    }\n\n    var ext = display.externalMeasured;\n    if (ext) {\n      if (to < ext.lineN)\n        { ext.lineN += lendiff; }\n      else if (from < ext.lineN + ext.size)\n        { display.externalMeasured = null; }\n    }\n  }\n\n  // Register a change to a single line. Type must be one of \"text\",\n  // \"gutter\", \"class\", \"widget\"\n  function regLineChange(cm, line, type) {\n    cm.curOp.viewChanged = true;\n    var display = cm.display, ext = cm.display.externalMeasured;\n    if (ext && line >= ext.lineN && line < ext.lineN + ext.size)\n      { display.externalMeasured = null; }\n\n    if (line < display.viewFrom || line >= display.viewTo) { return }\n    var lineView = display.view[findViewIndex(cm, line)];\n    if (lineView.node == null) { return }\n    var arr = lineView.changes || (lineView.changes = []);\n    if (indexOf(arr, type) == -1) { arr.push(type); }\n  }\n\n  // Clear the view.\n  function resetView(cm) {\n    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;\n    cm.display.view = [];\n    cm.display.viewOffset = 0;\n  }\n\n  function viewCuttingPoint(cm, oldN, newN, dir) {\n    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;\n    if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)\n      { return {index: index, lineN: newN} }\n    var n = cm.display.viewFrom;\n    for (var i = 0; i < index; i++)\n      { n += view[i].size; }\n    if (n != oldN) {\n      if (dir > 0) {\n        if (index == view.length - 1) { return null }\n        diff = (n + view[index].size) - oldN;\n        index++;\n      } else {\n        diff = n - oldN;\n      }\n      oldN += diff; newN += diff;\n    }\n    while (visualLineNo(cm.doc, newN) != newN) {\n      if (index == (dir < 0 ? 0 : view.length - 1)) { return null }\n      newN += dir * view[index - (dir < 0 ? 1 : 0)].size;\n      index += dir;\n    }\n    return {index: index, lineN: newN}\n  }\n\n  // Force the view to cover a given range, adding empty view element\n  // or clipping off existing ones as needed.\n  function adjustView(cm, from, to) {\n    var display = cm.display, view = display.view;\n    if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {\n      display.view = buildViewArray(cm, from, to);\n      display.viewFrom = from;\n    } else {\n      if (display.viewFrom > from)\n        { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }\n      else if (display.viewFrom < from)\n        { display.view = display.view.slice(findViewIndex(cm, from)); }\n      display.viewFrom = from;\n      if (display.viewTo < to)\n        { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }\n      else if (display.viewTo > to)\n        { display.view = display.view.slice(0, findViewIndex(cm, to)); }\n    }\n    display.viewTo = to;\n  }\n\n  // Count the number of lines in the view whose DOM representation is\n  // out of date (or nonexistent).\n  function countDirtyView(cm) {\n    var view = cm.display.view, dirty = 0;\n    for (var i = 0; i < view.length; i++) {\n      var lineView = view[i];\n      if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }\n    }\n    return dirty\n  }\n\n  // HIGHLIGHT WORKER\n\n  function startWorker(cm, time) {\n    if (cm.doc.highlightFrontier < cm.display.viewTo)\n      { cm.state.highlight.set(time, bind(highlightWorker, cm)); }\n  }\n\n  function highlightWorker(cm) {\n    var doc = cm.doc;\n    if (doc.highlightFrontier >= cm.display.viewTo) { return }\n    var end = +new Date + cm.options.workTime;\n    var context = getContextBefore(cm, doc.highlightFrontier);\n    var changedLines = [];\n\n    doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {\n      if (context.line >= cm.display.viewFrom) { // Visible\n        var oldStyles = line.styles;\n        var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;\n        var highlighted = highlightLine(cm, line, context, true);\n        if (resetState) { context.state = resetState; }\n        line.styles = highlighted.styles;\n        var oldCls = line.styleClasses, newCls = highlighted.classes;\n        if (newCls) { line.styleClasses = newCls; }\n        else if (oldCls) { line.styleClasses = null; }\n        var ischange = !oldStyles || oldStyles.length != line.styles.length ||\n          oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);\n        for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }\n        if (ischange) { changedLines.push(context.line); }\n        line.stateAfter = context.save();\n        context.nextLine();\n      } else {\n        if (line.text.length <= cm.options.maxHighlightLength)\n          { processLine(cm, line.text, context); }\n        line.stateAfter = context.line % 5 == 0 ? context.save() : null;\n        context.nextLine();\n      }\n      if (+new Date > end) {\n        startWorker(cm, cm.options.workDelay);\n        return true\n      }\n    });\n    doc.highlightFrontier = context.line;\n    doc.modeFrontier = Math.max(doc.modeFrontier, context.line);\n    if (changedLines.length) { runInOp(cm, function () {\n      for (var i = 0; i < changedLines.length; i++)\n        { regLineChange(cm, changedLines[i], \"text\"); }\n    }); }\n  }\n\n  // DISPLAY DRAWING\n\n  var DisplayUpdate = function(cm, viewport, force) {\n    var display = cm.display;\n\n    this.viewport = viewport;\n    // Store some values that we'll need later (but don't want to force a relayout for)\n    this.visible = visibleLines(display, cm.doc, viewport);\n    this.editorIsHidden = !display.wrapper.offsetWidth;\n    this.wrapperHeight = display.wrapper.clientHeight;\n    this.wrapperWidth = display.wrapper.clientWidth;\n    this.oldDisplayWidth = displayWidth(cm);\n    this.force = force;\n    this.dims = getDimensions(cm);\n    this.events = [];\n  };\n\n  DisplayUpdate.prototype.signal = function (emitter, type) {\n    if (hasHandler(emitter, type))\n      { this.events.push(arguments); }\n  };\n  DisplayUpdate.prototype.finish = function () {\n      var this$1 = this;\n\n    for (var i = 0; i < this.events.length; i++)\n      { signal.apply(null, this$1.events[i]); }\n  };\n\n  function maybeClipScrollbars(cm) {\n    var display = cm.display;\n    if (!display.scrollbarsClipped && display.scroller.offsetWidth) {\n      display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;\n      display.heightForcer.style.height = scrollGap(cm) + \"px\";\n      display.sizer.style.marginBottom = -display.nativeBarWidth + \"px\";\n      display.sizer.style.borderRightWidth = scrollGap(cm) + \"px\";\n      display.scrollbarsClipped = true;\n    }\n  }\n\n  function selectionSnapshot(cm) {\n    if (cm.hasFocus()) { return null }\n    var active = activeElt();\n    if (!active || !contains(cm.display.lineDiv, active)) { return null }\n    var result = {activeElt: active};\n    if (window.getSelection) {\n      var sel = window.getSelection();\n      if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {\n        result.anchorNode = sel.anchorNode;\n        result.anchorOffset = sel.anchorOffset;\n        result.focusNode = sel.focusNode;\n        result.focusOffset = sel.focusOffset;\n      }\n    }\n    return result\n  }\n\n  function restoreSelection(snapshot) {\n    if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }\n    snapshot.activeElt.focus();\n    if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {\n      var sel = window.getSelection(), range$$1 = document.createRange();\n      range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset);\n      range$$1.collapse(false);\n      sel.removeAllRanges();\n      sel.addRange(range$$1);\n      sel.extend(snapshot.focusNode, snapshot.focusOffset);\n    }\n  }\n\n  // Does the actual updating of the line display. Bails out\n  // (returning false) when there is nothing to be done and forced is\n  // false.\n  function updateDisplayIfNeeded(cm, update) {\n    var display = cm.display, doc = cm.doc;\n\n    if (update.editorIsHidden) {\n      resetView(cm);\n      return false\n    }\n\n    // Bail out if the visible area is already rendered and nothing changed.\n    if (!update.force &&\n        update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&\n        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&\n        display.renderedView == display.view && countDirtyView(cm) == 0)\n      { return false }\n\n    if (maybeUpdateLineNumberWidth(cm)) {\n      resetView(cm);\n      update.dims = getDimensions(cm);\n    }\n\n    // Compute a suitable new viewport (from & to)\n    var end = doc.first + doc.size;\n    var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);\n    var to = Math.min(end, update.visible.to + cm.options.viewportMargin);\n    if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }\n    if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }\n    if (sawCollapsedSpans) {\n      from = visualLineNo(cm.doc, from);\n      to = visualLineEndNo(cm.doc, to);\n    }\n\n    var different = from != display.viewFrom || to != display.viewTo ||\n      display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;\n    adjustView(cm, from, to);\n\n    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));\n    // Position the mover div to align with the current scroll position\n    cm.display.mover.style.top = display.viewOffset + \"px\";\n\n    var toUpdate = countDirtyView(cm);\n    if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&\n        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))\n      { return false }\n\n    // For big changes, we hide the enclosing element during the\n    // update, since that speeds up the operations on most browsers.\n    var selSnapshot = selectionSnapshot(cm);\n    if (toUpdate > 4) { display.lineDiv.style.display = \"none\"; }\n    patchDisplay(cm, display.updateLineNumbers, update.dims);\n    if (toUpdate > 4) { display.lineDiv.style.display = \"\"; }\n    display.renderedView = display.view;\n    // There might have been a widget with a focused element that got\n    // hidden or updated, if so re-focus it.\n    restoreSelection(selSnapshot);\n\n    // Prevent selection and cursors from interfering with the scroll\n    // width and height.\n    removeChildren(display.cursorDiv);\n    removeChildren(display.selectionDiv);\n    display.gutters.style.height = display.sizer.style.minHeight = 0;\n\n    if (different) {\n      display.lastWrapHeight = update.wrapperHeight;\n      display.lastWrapWidth = update.wrapperWidth;\n      startWorker(cm, 400);\n    }\n\n    display.updateLineNumbers = null;\n\n    return true\n  }\n\n  function postUpdateDisplay(cm, update) {\n    var viewport = update.viewport;\n\n    for (var first = true;; first = false) {\n      if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {\n        // Clip forced viewport to actual scrollable area.\n        if (viewport && viewport.top != null)\n          { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }\n        // Updated line heights might result in the drawn area not\n        // actually covering the viewport. Keep looping until it does.\n        update.visible = visibleLines(cm.display, cm.doc, viewport);\n        if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)\n          { break }\n      }\n      if (!updateDisplayIfNeeded(cm, update)) { break }\n      updateHeightsInViewport(cm);\n      var barMeasure = measureForScrollbars(cm);\n      updateSelection(cm);\n      updateScrollbars(cm, barMeasure);\n      setDocumentHeight(cm, barMeasure);\n      update.force = false;\n    }\n\n    update.signal(cm, \"update\", cm);\n    if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {\n      update.signal(cm, \"viewportChange\", cm, cm.display.viewFrom, cm.display.viewTo);\n      cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;\n    }\n  }\n\n  function updateDisplaySimple(cm, viewport) {\n    var update = new DisplayUpdate(cm, viewport);\n    if (updateDisplayIfNeeded(cm, update)) {\n      updateHeightsInViewport(cm);\n      postUpdateDisplay(cm, update);\n      var barMeasure = measureForScrollbars(cm);\n      updateSelection(cm);\n      updateScrollbars(cm, barMeasure);\n      setDocumentHeight(cm, barMeasure);\n      update.finish();\n    }\n  }\n\n  // Sync the actual display DOM structure with display.view, removing\n  // nodes for lines that are no longer in view, and creating the ones\n  // that are not there yet, and updating the ones that are out of\n  // date.\n  function patchDisplay(cm, updateNumbersFrom, dims) {\n    var display = cm.display, lineNumbers = cm.options.lineNumbers;\n    var container = display.lineDiv, cur = container.firstChild;\n\n    function rm(node) {\n      var next = node.nextSibling;\n      // Works around a throw-scroll bug in OS X Webkit\n      if (webkit && mac && cm.display.currentWheelTarget == node)\n        { node.style.display = \"none\"; }\n      else\n        { node.parentNode.removeChild(node); }\n      return next\n    }\n\n    var view = display.view, lineN = display.viewFrom;\n    // Loop over the elements in the view, syncing cur (the DOM nodes\n    // in display.lineDiv) with the view as we go.\n    for (var i = 0; i < view.length; i++) {\n      var lineView = view[i];\n      if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet\n        var node = buildLineElement(cm, lineView, lineN, dims);\n        container.insertBefore(node, cur);\n      } else { // Already drawn\n        while (cur != lineView.node) { cur = rm(cur); }\n        var updateNumber = lineNumbers && updateNumbersFrom != null &&\n          updateNumbersFrom <= lineN && lineView.lineNumber;\n        if (lineView.changes) {\n          if (indexOf(lineView.changes, \"gutter\") > -1) { updateNumber = false; }\n          updateLineForChanges(cm, lineView, lineN, dims);\n        }\n        if (updateNumber) {\n          removeChildren(lineView.lineNumber);\n          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));\n        }\n        cur = lineView.node.nextSibling;\n      }\n      lineN += lineView.size;\n    }\n    while (cur) { cur = rm(cur); }\n  }\n\n  function updateGutterSpace(cm) {\n    var width = cm.display.gutters.offsetWidth;\n    cm.display.sizer.style.marginLeft = width + \"px\";\n  }\n\n  function setDocumentHeight(cm, measure) {\n    cm.display.sizer.style.minHeight = measure.docHeight + \"px\";\n    cm.display.heightForcer.style.top = measure.docHeight + \"px\";\n    cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + \"px\";\n  }\n\n  // Rebuild the gutter elements, ensure the margin to the left of the\n  // code matches their width.\n  function updateGutters(cm) {\n    var gutters = cm.display.gutters, specs = cm.options.gutters;\n    removeChildren(gutters);\n    var i = 0;\n    for (; i < specs.length; ++i) {\n      var gutterClass = specs[i];\n      var gElt = gutters.appendChild(elt(\"div\", null, \"CodeMirror-gutter \" + gutterClass));\n      if (gutterClass == \"CodeMirror-linenumbers\") {\n        cm.display.lineGutter = gElt;\n        gElt.style.width = (cm.display.lineNumWidth || 1) + \"px\";\n      }\n    }\n    gutters.style.display = i ? \"\" : \"none\";\n    updateGutterSpace(cm);\n  }\n\n  // Make sure the gutters options contains the element\n  // \"CodeMirror-linenumbers\" when the lineNumbers option is true.\n  function setGuttersForLineNumbers(options) {\n    var found = indexOf(options.gutters, \"CodeMirror-linenumbers\");\n    if (found == -1 && options.lineNumbers) {\n      options.gutters = options.gutters.concat([\"CodeMirror-linenumbers\"]);\n    } else if (found > -1 && !options.lineNumbers) {\n      options.gutters = options.gutters.slice(0);\n      options.gutters.splice(found, 1);\n    }\n  }\n\n  // Since the delta values reported on mouse wheel events are\n  // unstandardized between browsers and even browser versions, and\n  // generally horribly unpredictable, this code starts by measuring\n  // the scroll effect that the first few mouse wheel events have,\n  // and, from that, detects the way it can convert deltas to pixel\n  // offsets afterwards.\n  //\n  // The reason we want to know the amount a wheel event will scroll\n  // is that it gives us a chance to update the display before the\n  // actual scrolling happens, reducing flickering.\n\n  var wheelSamples = 0, wheelPixelsPerUnit = null;\n  // Fill in a browser-detected starting value on browsers where we\n  // know one. These don't have to be accurate -- the result of them\n  // being wrong would just be a slight flicker on the first wheel\n  // scroll (if it is large enough).\n  if (ie) { wheelPixelsPerUnit = -.53; }\n  else if (gecko) { wheelPixelsPerUnit = 15; }\n  else if (chrome) { wheelPixelsPerUnit = -.7; }\n  else if (safari) { wheelPixelsPerUnit = -1/3; }\n\n  function wheelEventDelta(e) {\n    var dx = e.wheelDeltaX, dy = e.wheelDeltaY;\n    if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }\n    if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }\n    else if (dy == null) { dy = e.wheelDelta; }\n    return {x: dx, y: dy}\n  }\n  function wheelEventPixels(e) {\n    var delta = wheelEventDelta(e);\n    delta.x *= wheelPixelsPerUnit;\n    delta.y *= wheelPixelsPerUnit;\n    return delta\n  }\n\n  function onScrollWheel(cm, e) {\n    var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;\n\n    var display = cm.display, scroll = display.scroller;\n    // Quit if there's nothing to scroll here\n    var canScrollX = scroll.scrollWidth > scroll.clientWidth;\n    var canScrollY = scroll.scrollHeight > scroll.clientHeight;\n    if (!(dx && canScrollX || dy && canScrollY)) { return }\n\n    // Webkit browsers on OS X abort momentum scrolls when the target\n    // of the scroll event is removed from the scrollable element.\n    // This hack (see related code in patchDisplay) makes sure the\n    // element is kept around.\n    if (dy && mac && webkit) {\n      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {\n        for (var i = 0; i < view.length; i++) {\n          if (view[i].node == cur) {\n            cm.display.currentWheelTarget = cur;\n            break outer\n          }\n        }\n      }\n    }\n\n    // On some browsers, horizontal scrolling will cause redraws to\n    // happen before the gutter has been realigned, causing it to\n    // wriggle around in a most unseemly way. When we have an\n    // estimated pixels/delta value, we just handle horizontal\n    // scrolling entirely here. It'll be slightly off from native, but\n    // better than glitching out.\n    if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {\n      if (dy && canScrollY)\n        { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }\n      setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));\n      // Only prevent default scrolling if vertical scrolling is\n      // actually possible. Otherwise, it causes vertical scroll\n      // jitter on OSX trackpads when deltaX is small and deltaY\n      // is large (issue #3579)\n      if (!dy || (dy && canScrollY))\n        { e_preventDefault(e); }\n      display.wheelStartX = null; // Abort measurement, if in progress\n      return\n    }\n\n    // 'Project' the visible viewport to cover the area that is being\n    // scrolled into view (if we know enough to estimate it).\n    if (dy && wheelPixelsPerUnit != null) {\n      var pixels = dy * wheelPixelsPerUnit;\n      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;\n      if (pixels < 0) { top = Math.max(0, top + pixels - 50); }\n      else { bot = Math.min(cm.doc.height, bot + pixels + 50); }\n      updateDisplaySimple(cm, {top: top, bottom: bot});\n    }\n\n    if (wheelSamples < 20) {\n      if (display.wheelStartX == null) {\n        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;\n        display.wheelDX = dx; display.wheelDY = dy;\n        setTimeout(function () {\n          if (display.wheelStartX == null) { return }\n          var movedX = scroll.scrollLeft - display.wheelStartX;\n          var movedY = scroll.scrollTop - display.wheelStartY;\n          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||\n            (movedX && display.wheelDX && movedX / display.wheelDX);\n          display.wheelStartX = display.wheelStartY = null;\n          if (!sample) { return }\n          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);\n          ++wheelSamples;\n        }, 200);\n      } else {\n        display.wheelDX += dx; display.wheelDY += dy;\n      }\n    }\n  }\n\n  // Selection objects are immutable. A new one is created every time\n  // the selection changes. A selection is one or more non-overlapping\n  // (and non-touching) ranges, sorted, and an integer that indicates\n  // which one is the primary selection (the one that's scrolled into\n  // view, that getCursor returns, etc).\n  var Selection = function(ranges, primIndex) {\n    this.ranges = ranges;\n    this.primIndex = primIndex;\n  };\n\n  Selection.prototype.primary = function () { return this.ranges[this.primIndex] };\n\n  Selection.prototype.equals = function (other) {\n      var this$1 = this;\n\n    if (other == this) { return true }\n    if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }\n    for (var i = 0; i < this.ranges.length; i++) {\n      var here = this$1.ranges[i], there = other.ranges[i];\n      if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }\n    }\n    return true\n  };\n\n  Selection.prototype.deepCopy = function () {\n      var this$1 = this;\n\n    var out = [];\n    for (var i = 0; i < this.ranges.length; i++)\n      { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); }\n    return new Selection(out, this.primIndex)\n  };\n\n  Selection.prototype.somethingSelected = function () {\n      var this$1 = this;\n\n    for (var i = 0; i < this.ranges.length; i++)\n      { if (!this$1.ranges[i].empty()) { return true } }\n    return false\n  };\n\n  Selection.prototype.contains = function (pos, end) {\n      var this$1 = this;\n\n    if (!end) { end = pos; }\n    for (var i = 0; i < this.ranges.length; i++) {\n      var range = this$1.ranges[i];\n      if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)\n        { return i }\n    }\n    return -1\n  };\n\n  var Range = function(anchor, head) {\n    this.anchor = anchor; this.head = head;\n  };\n\n  Range.prototype.from = function () { return minPos(this.anchor, this.head) };\n  Range.prototype.to = function () { return maxPos(this.anchor, this.head) };\n  Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };\n\n  // Take an unsorted, potentially overlapping set of ranges, and\n  // build a selection out of it. 'Consumes' ranges array (modifying\n  // it).\n  function normalizeSelection(cm, ranges, primIndex) {\n    var mayTouch = cm && cm.options.selectionsMayTouch;\n    var prim = ranges[primIndex];\n    ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });\n    primIndex = indexOf(ranges, prim);\n    for (var i = 1; i < ranges.length; i++) {\n      var cur = ranges[i], prev = ranges[i - 1];\n      var diff = cmp(prev.to(), cur.from());\n      if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {\n        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());\n        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;\n        if (i <= primIndex) { --primIndex; }\n        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));\n      }\n    }\n    return new Selection(ranges, primIndex)\n  }\n\n  function simpleSelection(anchor, head) {\n    return new Selection([new Range(anchor, head || anchor)], 0)\n  }\n\n  // Compute the position of the end of a change (its 'to' property\n  // refers to the pre-change end).\n  function changeEnd(change) {\n    if (!change.text) { return change.to }\n    return Pos(change.from.line + change.text.length - 1,\n               lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))\n  }\n\n  // Adjust a position to refer to the post-change position of the\n  // same text, or the end of the change if the change covers it.\n  function adjustForChange(pos, change) {\n    if (cmp(pos, change.from) < 0) { return pos }\n    if (cmp(pos, change.to) <= 0) { return changeEnd(change) }\n\n    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;\n    if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }\n    return Pos(line, ch)\n  }\n\n  function computeSelAfterChange(doc, change) {\n    var out = [];\n    for (var i = 0; i < doc.sel.ranges.length; i++) {\n      var range = doc.sel.ranges[i];\n      out.push(new Range(adjustForChange(range.anchor, change),\n                         adjustForChange(range.head, change)));\n    }\n    return normalizeSelection(doc.cm, out, doc.sel.primIndex)\n  }\n\n  function offsetPos(pos, old, nw) {\n    if (pos.line == old.line)\n      { return Pos(nw.line, pos.ch - old.ch + nw.ch) }\n    else\n      { return Pos(nw.line + (pos.line - old.line), pos.ch) }\n  }\n\n  // Used by replaceSelections to allow moving the selection to the\n  // start or around the replaced test. Hint may be \"start\" or \"around\".\n  function computeReplacedSel(doc, changes, hint) {\n    var out = [];\n    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;\n    for (var i = 0; i < changes.length; i++) {\n      var change = changes[i];\n      var from = offsetPos(change.from, oldPrev, newPrev);\n      var to = offsetPos(changeEnd(change), oldPrev, newPrev);\n      oldPrev = change.to;\n      newPrev = to;\n      if (hint == \"around\") {\n        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;\n        out[i] = new Range(inv ? to : from, inv ? from : to);\n      } else {\n        out[i] = new Range(from, from);\n      }\n    }\n    return new Selection(out, doc.sel.primIndex)\n  }\n\n  // Used to get the editor into a consistent state again when options change.\n\n  function loadMode(cm) {\n    cm.doc.mode = getMode(cm.options, cm.doc.modeOption);\n    resetModeState(cm);\n  }\n\n  function resetModeState(cm) {\n    cm.doc.iter(function (line) {\n      if (line.stateAfter) { line.stateAfter = null; }\n      if (line.styles) { line.styles = null; }\n    });\n    cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;\n    startWorker(cm, 100);\n    cm.state.modeGen++;\n    if (cm.curOp) { regChange(cm); }\n  }\n\n  // DOCUMENT DATA STRUCTURE\n\n  // By default, updates that start and end at the beginning of a line\n  // are treated specially, in order to make the association of line\n  // widgets and marker elements with the text behave more intuitive.\n  function isWholeLineUpdate(doc, change) {\n    return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == \"\" &&\n      (!doc.cm || doc.cm.options.wholeLineUpdateBefore)\n  }\n\n  // Perform a change on the document data structure.\n  function updateDoc(doc, change, markedSpans, estimateHeight$$1) {\n    function spansFor(n) {return markedSpans ? markedSpans[n] : null}\n    function update(line, text, spans) {\n      updateLine(line, text, spans, estimateHeight$$1);\n      signalLater(line, \"change\", line, change);\n    }\n    function linesFor(start, end) {\n      var result = [];\n      for (var i = start; i < end; ++i)\n        { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); }\n      return result\n    }\n\n    var from = change.from, to = change.to, text = change.text;\n    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);\n    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;\n\n    // Adjust the line structure\n    if (change.full) {\n      doc.insert(0, linesFor(0, text.length));\n      doc.remove(text.length, doc.size - text.length);\n    } else if (isWholeLineUpdate(doc, change)) {\n      // This is a whole-line replace. Treated specially to make\n      // sure line objects move the way they are supposed to.\n      var added = linesFor(0, text.length - 1);\n      update(lastLine, lastLine.text, lastSpans);\n      if (nlines) { doc.remove(from.line, nlines); }\n      if (added.length) { doc.insert(from.line, added); }\n    } else if (firstLine == lastLine) {\n      if (text.length == 1) {\n        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);\n      } else {\n        var added$1 = linesFor(1, text.length - 1);\n        added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1));\n        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));\n        doc.insert(from.line + 1, added$1);\n      }\n    } else if (text.length == 1) {\n      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));\n      doc.remove(from.line + 1, nlines);\n    } else {\n      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));\n      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);\n      var added$2 = linesFor(1, text.length - 1);\n      if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }\n      doc.insert(from.line + 1, added$2);\n    }\n\n    signalLater(doc, \"change\", doc, change);\n  }\n\n  // Call f for all linked documents.\n  function linkedDocs(doc, f, sharedHistOnly) {\n    function propagate(doc, skip, sharedHist) {\n      if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {\n        var rel = doc.linked[i];\n        if (rel.doc == skip) { continue }\n        var shared = sharedHist && rel.sharedHist;\n        if (sharedHistOnly && !shared) { continue }\n        f(rel.doc, shared);\n        propagate(rel.doc, doc, shared);\n      } }\n    }\n    propagate(doc, null, true);\n  }\n\n  // Attach a document to an editor.\n  function attachDoc(cm, doc) {\n    if (doc.cm) { throw new Error(\"This document is already in use.\") }\n    cm.doc = doc;\n    doc.cm = cm;\n    estimateLineHeights(cm);\n    loadMode(cm);\n    setDirectionClass(cm);\n    if (!cm.options.lineWrapping) { findMaxLine(cm); }\n    cm.options.mode = doc.modeOption;\n    regChange(cm);\n  }\n\n  function setDirectionClass(cm) {\n  (cm.doc.direction == \"rtl\" ? addClass : rmClass)(cm.display.lineDiv, \"CodeMirror-rtl\");\n  }\n\n  function directionChanged(cm) {\n    runInOp(cm, function () {\n      setDirectionClass(cm);\n      regChange(cm);\n    });\n  }\n\n  function History(startGen) {\n    // Arrays of change events and selections. Doing something adds an\n    // event to done and clears undo. Undoing moves events from done\n    // to undone, redoing moves them in the other direction.\n    this.done = []; this.undone = [];\n    this.undoDepth = Infinity;\n    // Used to track when changes can be merged into a single undo\n    // event\n    this.lastModTime = this.lastSelTime = 0;\n    this.lastOp = this.lastSelOp = null;\n    this.lastOrigin = this.lastSelOrigin = null;\n    // Used by the isClean() method\n    this.generation = this.maxGeneration = startGen || 1;\n  }\n\n  // Create a history change event from an updateDoc-style change\n  // object.\n  function historyChangeFromChange(doc, change) {\n    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};\n    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);\n    linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);\n    return histChange\n  }\n\n  // Pop all selection events off the end of a history array. Stop at\n  // a change event.\n  function clearSelectionEvents(array) {\n    while (array.length) {\n      var last = lst(array);\n      if (last.ranges) { array.pop(); }\n      else { break }\n    }\n  }\n\n  // Find the top change event in the history. Pop off selection\n  // events that are in the way.\n  function lastChangeEvent(hist, force) {\n    if (force) {\n      clearSelectionEvents(hist.done);\n      return lst(hist.done)\n    } else if (hist.done.length && !lst(hist.done).ranges) {\n      return lst(hist.done)\n    } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {\n      hist.done.pop();\n      return lst(hist.done)\n    }\n  }\n\n  // Register a change in the history. Merges changes that are within\n  // a single operation, or are close together with an origin that\n  // allows merging (starting with \"+\") into a single event.\n  function addChangeToHistory(doc, change, selAfter, opId) {\n    var hist = doc.history;\n    hist.undone.length = 0;\n    var time = +new Date, cur;\n    var last;\n\n    if ((hist.lastOp == opId ||\n         hist.lastOrigin == change.origin && change.origin &&\n         ((change.origin.charAt(0) == \"+\" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||\n          change.origin.charAt(0) == \"*\")) &&\n        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {\n      // Merge this change into the last event\n      last = lst(cur.changes);\n      if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {\n        // Optimized case for simple insertion -- don't want to add\n        // new changesets for every character typed\n        last.to = changeEnd(change);\n      } else {\n        // Add new sub-event\n        cur.changes.push(historyChangeFromChange(doc, change));\n      }\n    } else {\n      // Can not be merged, start a new event.\n      var before = lst(hist.done);\n      if (!before || !before.ranges)\n        { pushSelectionToHistory(doc.sel, hist.done); }\n      cur = {changes: [historyChangeFromChange(doc, change)],\n             generation: hist.generation};\n      hist.done.push(cur);\n      while (hist.done.length > hist.undoDepth) {\n        hist.done.shift();\n        if (!hist.done[0].ranges) { hist.done.shift(); }\n      }\n    }\n    hist.done.push(selAfter);\n    hist.generation = ++hist.maxGeneration;\n    hist.lastModTime = hist.lastSelTime = time;\n    hist.lastOp = hist.lastSelOp = opId;\n    hist.lastOrigin = hist.lastSelOrigin = change.origin;\n\n    if (!last) { signal(doc, \"historyAdded\"); }\n  }\n\n  function selectionEventCanBeMerged(doc, origin, prev, sel) {\n    var ch = origin.charAt(0);\n    return ch == \"*\" ||\n      ch == \"+\" &&\n      prev.ranges.length == sel.ranges.length &&\n      prev.somethingSelected() == sel.somethingSelected() &&\n      new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)\n  }\n\n  // Called whenever the selection changes, sets the new selection as\n  // the pending selection in the history, and pushes the old pending\n  // selection into the 'done' array when it was significantly\n  // different (in number of selected ranges, emptiness, or time).\n  function addSelectionToHistory(doc, sel, opId, options) {\n    var hist = doc.history, origin = options && options.origin;\n\n    // A new event is started when the previous origin does not match\n    // the current, or the origins don't allow matching. Origins\n    // starting with * are always merged, those starting with + are\n    // merged when similar and close together in time.\n    if (opId == hist.lastSelOp ||\n        (origin && hist.lastSelOrigin == origin &&\n         (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||\n          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))\n      { hist.done[hist.done.length - 1] = sel; }\n    else\n      { pushSelectionToHistory(sel, hist.done); }\n\n    hist.lastSelTime = +new Date;\n    hist.lastSelOrigin = origin;\n    hist.lastSelOp = opId;\n    if (options && options.clearRedo !== false)\n      { clearSelectionEvents(hist.undone); }\n  }\n\n  function pushSelectionToHistory(sel, dest) {\n    var top = lst(dest);\n    if (!(top && top.ranges && top.equals(sel)))\n      { dest.push(sel); }\n  }\n\n  // Used to store marked span information in the history.\n  function attachLocalSpans(doc, change, from, to) {\n    var existing = change[\"spans_\" + doc.id], n = 0;\n    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {\n      if (line.markedSpans)\n        { (existing || (existing = change[\"spans_\" + doc.id] = {}))[n] = line.markedSpans; }\n      ++n;\n    });\n  }\n\n  // When un/re-doing restores text containing marked spans, those\n  // that have been explicitly cleared should not be restored.\n  function removeClearedSpans(spans) {\n    if (!spans) { return null }\n    var out;\n    for (var i = 0; i < spans.length; ++i) {\n      if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }\n      else if (out) { out.push(spans[i]); }\n    }\n    return !out ? spans : out.length ? out : null\n  }\n\n  // Retrieve and filter the old marked spans stored in a change event.\n  function getOldSpans(doc, change) {\n    var found = change[\"spans_\" + doc.id];\n    if (!found) { return null }\n    var nw = [];\n    for (var i = 0; i < change.text.length; ++i)\n      { nw.push(removeClearedSpans(found[i])); }\n    return nw\n  }\n\n  // Used for un/re-doing changes from the history. Combines the\n  // result of computing the existing spans with the set of spans that\n  // existed in the history (so that deleting around a span and then\n  // undoing brings back the span).\n  function mergeOldSpans(doc, change) {\n    var old = getOldSpans(doc, change);\n    var stretched = stretchSpansOverChange(doc, change);\n    if (!old) { return stretched }\n    if (!stretched) { return old }\n\n    for (var i = 0; i < old.length; ++i) {\n      var oldCur = old[i], stretchCur = stretched[i];\n      if (oldCur && stretchCur) {\n        spans: for (var j = 0; j < stretchCur.length; ++j) {\n          var span = stretchCur[j];\n          for (var k = 0; k < oldCur.length; ++k)\n            { if (oldCur[k].marker == span.marker) { continue spans } }\n          oldCur.push(span);\n        }\n      } else if (stretchCur) {\n        old[i] = stretchCur;\n      }\n    }\n    return old\n  }\n\n  // Used both to provide a JSON-safe object in .getHistory, and, when\n  // detaching a document, to split the history in two\n  function copyHistoryArray(events, newGroup, instantiateSel) {\n    var copy = [];\n    for (var i = 0; i < events.length; ++i) {\n      var event = events[i];\n      if (event.ranges) {\n        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);\n        continue\n      }\n      var changes = event.changes, newChanges = [];\n      copy.push({changes: newChanges});\n      for (var j = 0; j < changes.length; ++j) {\n        var change = changes[j], m = (void 0);\n        newChanges.push({from: change.from, to: change.to, text: change.text});\n        if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\\d+)$/)) {\n          if (indexOf(newGroup, Number(m[1])) > -1) {\n            lst(newChanges)[prop] = change[prop];\n            delete change[prop];\n          }\n        } } }\n      }\n    }\n    return copy\n  }\n\n  // The 'scroll' parameter given to many of these indicated whether\n  // the new cursor position should be scrolled into view after\n  // modifying the selection.\n\n  // If shift is held or the extend flag is set, extends a range to\n  // include a given position (and optionally a second position).\n  // Otherwise, simply returns the range between the given positions.\n  // Used for cursor motion and such.\n  function extendRange(range, head, other, extend) {\n    if (extend) {\n      var anchor = range.anchor;\n      if (other) {\n        var posBefore = cmp(head, anchor) < 0;\n        if (posBefore != (cmp(other, anchor) < 0)) {\n          anchor = head;\n          head = other;\n        } else if (posBefore != (cmp(head, other) < 0)) {\n          head = other;\n        }\n      }\n      return new Range(anchor, head)\n    } else {\n      return new Range(other || head, head)\n    }\n  }\n\n  // Extend the primary selection range, discard the rest.\n  function extendSelection(doc, head, other, options, extend) {\n    if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }\n    setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);\n  }\n\n  // Extend all selections (pos is an array of selections with length\n  // equal the number of selections)\n  function extendSelections(doc, heads, options) {\n    var out = [];\n    var extend = doc.cm && (doc.cm.display.shift || doc.extend);\n    for (var i = 0; i < doc.sel.ranges.length; i++)\n      { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }\n    var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);\n    setSelection(doc, newSel, options);\n  }\n\n  // Updates a single range in the selection.\n  function replaceOneSelection(doc, i, range, options) {\n    var ranges = doc.sel.ranges.slice(0);\n    ranges[i] = range;\n    setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);\n  }\n\n  // Reset the selection to a single range.\n  function setSimpleSelection(doc, anchor, head, options) {\n    setSelection(doc, simpleSelection(anchor, head), options);\n  }\n\n  // Give beforeSelectionChange handlers a change to influence a\n  // selection update.\n  function filterSelectionChange(doc, sel, options) {\n    var obj = {\n      ranges: sel.ranges,\n      update: function(ranges) {\n        var this$1 = this;\n\n        this.ranges = [];\n        for (var i = 0; i < ranges.length; i++)\n          { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),\n                                     clipPos(doc, ranges[i].head)); }\n      },\n      origin: options && options.origin\n    };\n    signal(doc, \"beforeSelectionChange\", doc, obj);\n    if (doc.cm) { signal(doc.cm, \"beforeSelectionChange\", doc.cm, obj); }\n    if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }\n    else { return sel }\n  }\n\n  function setSelectionReplaceHistory(doc, sel, options) {\n    var done = doc.history.done, last = lst(done);\n    if (last && last.ranges) {\n      done[done.length - 1] = sel;\n      setSelectionNoUndo(doc, sel, options);\n    } else {\n      setSelection(doc, sel, options);\n    }\n  }\n\n  // Set a new selection.\n  function setSelection(doc, sel, options) {\n    setSelectionNoUndo(doc, sel, options);\n    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);\n  }\n\n  function setSelectionNoUndo(doc, sel, options) {\n    if (hasHandler(doc, \"beforeSelectionChange\") || doc.cm && hasHandler(doc.cm, \"beforeSelectionChange\"))\n      { sel = filterSelectionChange(doc, sel, options); }\n\n    var bias = options && options.bias ||\n      (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);\n    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));\n\n    if (!(options && options.scroll === false) && doc.cm)\n      { ensureCursorVisible(doc.cm); }\n  }\n\n  function setSelectionInner(doc, sel) {\n    if (sel.equals(doc.sel)) { return }\n\n    doc.sel = sel;\n\n    if (doc.cm) {\n      doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;\n      signalCursorActivity(doc.cm);\n    }\n    signalLater(doc, \"cursorActivity\", doc);\n  }\n\n  // Verify that the selection does not partially select any atomic\n  // marked ranges.\n  function reCheckSelection(doc) {\n    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));\n  }\n\n  // Return a selection that does not partially select any atomic\n  // ranges.\n  function skipAtomicInSelection(doc, sel, bias, mayClear) {\n    var out;\n    for (var i = 0; i < sel.ranges.length; i++) {\n      var range = sel.ranges[i];\n      var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];\n      var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);\n      var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);\n      if (out || newAnchor != range.anchor || newHead != range.head) {\n        if (!out) { out = sel.ranges.slice(0, i); }\n        out[i] = new Range(newAnchor, newHead);\n      }\n    }\n    return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel\n  }\n\n  function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {\n    var line = getLine(doc, pos.line);\n    if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {\n      var sp = line.markedSpans[i], m = sp.marker;\n      if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&\n          (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {\n        if (mayClear) {\n          signal(m, \"beforeCursorEnter\");\n          if (m.explicitlyCleared) {\n            if (!line.markedSpans) { break }\n            else {--i; continue}\n          }\n        }\n        if (!m.atomic) { continue }\n\n        if (oldPos) {\n          var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);\n          if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)\n            { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }\n          if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))\n            { return skipAtomicInner(doc, near, pos, dir, mayClear) }\n        }\n\n        var far = m.find(dir < 0 ? -1 : 1);\n        if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)\n          { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }\n        return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null\n      }\n    } }\n    return pos\n  }\n\n  // Ensure a given position is not inside an atomic range.\n  function skipAtomic(doc, pos, oldPos, bias, mayClear) {\n    var dir = bias || 1;\n    var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||\n        (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||\n        skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||\n        (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));\n    if (!found) {\n      doc.cantEdit = true;\n      return Pos(doc.first, 0)\n    }\n    return found\n  }\n\n  function movePos(doc, pos, dir, line) {\n    if (dir < 0 && pos.ch == 0) {\n      if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }\n      else { return null }\n    } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {\n      if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }\n      else { return null }\n    } else {\n      return new Pos(pos.line, pos.ch + dir)\n    }\n  }\n\n  function selectAll(cm) {\n    cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);\n  }\n\n  // UPDATING\n\n  // Allow \"beforeChange\" event handlers to influence a change\n  function filterChange(doc, change, update) {\n    var obj = {\n      canceled: false,\n      from: change.from,\n      to: change.to,\n      text: change.text,\n      origin: change.origin,\n      cancel: function () { return obj.canceled = true; }\n    };\n    if (update) { obj.update = function (from, to, text, origin) {\n      if (from) { obj.from = clipPos(doc, from); }\n      if (to) { obj.to = clipPos(doc, to); }\n      if (text) { obj.text = text; }\n      if (origin !== undefined) { obj.origin = origin; }\n    }; }\n    signal(doc, \"beforeChange\", doc, obj);\n    if (doc.cm) { signal(doc.cm, \"beforeChange\", doc.cm, obj); }\n\n    if (obj.canceled) { return null }\n    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}\n  }\n\n  // Apply a change to a document, and add it to the document's\n  // history, and propagating it to all linked documents.\n  function makeChange(doc, change, ignoreReadOnly) {\n    if (doc.cm) {\n      if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }\n      if (doc.cm.state.suppressEdits) { return }\n    }\n\n    if (hasHandler(doc, \"beforeChange\") || doc.cm && hasHandler(doc.cm, \"beforeChange\")) {\n      change = filterChange(doc, change, true);\n      if (!change) { return }\n    }\n\n    // Possibly split or suppress the update based on the presence\n    // of read-only spans in its range.\n    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);\n    if (split) {\n      for (var i = split.length - 1; i >= 0; --i)\n        { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [\"\"] : change.text, origin: change.origin}); }\n    } else {\n      makeChangeInner(doc, change);\n    }\n  }\n\n  function makeChangeInner(doc, change) {\n    if (change.text.length == 1 && change.text[0] == \"\" && cmp(change.from, change.to) == 0) { return }\n    var selAfter = computeSelAfterChange(doc, change);\n    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);\n\n    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));\n    var rebased = [];\n\n    linkedDocs(doc, function (doc, sharedHist) {\n      if (!sharedHist && indexOf(rebased, doc.history) == -1) {\n        rebaseHist(doc.history, change);\n        rebased.push(doc.history);\n      }\n      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));\n    });\n  }\n\n  // Revert a change stored in a document's history.\n  function makeChangeFromHistory(doc, type, allowSelectionOnly) {\n    var suppress = doc.cm && doc.cm.state.suppressEdits;\n    if (suppress && !allowSelectionOnly) { return }\n\n    var hist = doc.history, event, selAfter = doc.sel;\n    var source = type == \"undo\" ? hist.done : hist.undone, dest = type == \"undo\" ? hist.undone : hist.done;\n\n    // Verify that there is a useable event (so that ctrl-z won't\n    // needlessly clear selection events)\n    var i = 0;\n    for (; i < source.length; i++) {\n      event = source[i];\n      if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)\n        { break }\n    }\n    if (i == source.length) { return }\n    hist.lastOrigin = hist.lastSelOrigin = null;\n\n    for (;;) {\n      event = source.pop();\n      if (event.ranges) {\n        pushSelectionToHistory(event, dest);\n        if (allowSelectionOnly && !event.equals(doc.sel)) {\n          setSelection(doc, event, {clearRedo: false});\n          return\n        }\n        selAfter = event;\n      } else if (suppress) {\n        source.push(event);\n        return\n      } else { break }\n    }\n\n    // Build up a reverse change object to add to the opposite history\n    // stack (redo when undoing, and vice versa).\n    var antiChanges = [];\n    pushSelectionToHistory(selAfter, dest);\n    dest.push({changes: antiChanges, generation: hist.generation});\n    hist.generation = event.generation || ++hist.maxGeneration;\n\n    var filter = hasHandler(doc, \"beforeChange\") || doc.cm && hasHandler(doc.cm, \"beforeChange\");\n\n    var loop = function ( i ) {\n      var change = event.changes[i];\n      change.origin = type;\n      if (filter && !filterChange(doc, change, false)) {\n        source.length = 0;\n        return {}\n      }\n\n      antiChanges.push(historyChangeFromChange(doc, change));\n\n      var after = i ? computeSelAfterChange(doc, change) : lst(source);\n      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));\n      if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }\n      var rebased = [];\n\n      // Propagate to the linked documents\n      linkedDocs(doc, function (doc, sharedHist) {\n        if (!sharedHist && indexOf(rebased, doc.history) == -1) {\n          rebaseHist(doc.history, change);\n          rebased.push(doc.history);\n        }\n        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));\n      });\n    };\n\n    for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {\n      var returned = loop( i$1 );\n\n      if ( returned ) return returned.v;\n    }\n  }\n\n  // Sub-views need their line numbers shifted when text is added\n  // above or below them in the parent document.\n  function shiftDoc(doc, distance) {\n    if (distance == 0) { return }\n    doc.first += distance;\n    doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(\n      Pos(range.anchor.line + distance, range.anchor.ch),\n      Pos(range.head.line + distance, range.head.ch)\n    ); }), doc.sel.primIndex);\n    if (doc.cm) {\n      regChange(doc.cm, doc.first, doc.first - distance, distance);\n      for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)\n        { regLineChange(doc.cm, l, \"gutter\"); }\n    }\n  }\n\n  // More lower-level change function, handling only a single document\n  // (not linked ones).\n  function makeChangeSingleDoc(doc, change, selAfter, spans) {\n    if (doc.cm && !doc.cm.curOp)\n      { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }\n\n    if (change.to.line < doc.first) {\n      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));\n      return\n    }\n    if (change.from.line > doc.lastLine()) { return }\n\n    // Clip the change to the size of this doc\n    if (change.from.line < doc.first) {\n      var shift = change.text.length - 1 - (doc.first - change.from.line);\n      shiftDoc(doc, shift);\n      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),\n                text: [lst(change.text)], origin: change.origin};\n    }\n    var last = doc.lastLine();\n    if (change.to.line > last) {\n      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),\n                text: [change.text[0]], origin: change.origin};\n    }\n\n    change.removed = getBetween(doc, change.from, change.to);\n\n    if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }\n    if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }\n    else { updateDoc(doc, change, spans); }\n    setSelectionNoUndo(doc, selAfter, sel_dontScroll);\n  }\n\n  // Handle the interaction of a change to a document with the editor\n  // that this document is part of.\n  function makeChangeSingleDocInEditor(cm, change, spans) {\n    var doc = cm.doc, display = cm.display, from = change.from, to = change.to;\n\n    var recomputeMaxLength = false, checkWidthStart = from.line;\n    if (!cm.options.lineWrapping) {\n      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));\n      doc.iter(checkWidthStart, to.line + 1, function (line) {\n        if (line == display.maxLine) {\n          recomputeMaxLength = true;\n          return true\n        }\n      });\n    }\n\n    if (doc.sel.contains(change.from, change.to) > -1)\n      { signalCursorActivity(cm); }\n\n    updateDoc(doc, change, spans, estimateHeight(cm));\n\n    if (!cm.options.lineWrapping) {\n      doc.iter(checkWidthStart, from.line + change.text.length, function (line) {\n        var len = lineLength(line);\n        if (len > display.maxLineLength) {\n          display.maxLine = line;\n          display.maxLineLength = len;\n          display.maxLineChanged = true;\n          recomputeMaxLength = false;\n        }\n      });\n      if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }\n    }\n\n    retreatFrontier(doc, from.line);\n    startWorker(cm, 400);\n\n    var lendiff = change.text.length - (to.line - from.line) - 1;\n    // Remember that these lines changed, for updating the display\n    if (change.full)\n      { regChange(cm); }\n    else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))\n      { regLineChange(cm, from.line, \"text\"); }\n    else\n      { regChange(cm, from.line, to.line + 1, lendiff); }\n\n    var changesHandler = hasHandler(cm, \"changes\"), changeHandler = hasHandler(cm, \"change\");\n    if (changeHandler || changesHandler) {\n      var obj = {\n        from: from, to: to,\n        text: change.text,\n        removed: change.removed,\n        origin: change.origin\n      };\n      if (changeHandler) { signalLater(cm, \"change\", cm, obj); }\n      if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }\n    }\n    cm.display.selForContextMenu = null;\n  }\n\n  function replaceRange(doc, code, from, to, origin) {\n    var assign;\n\n    if (!to) { to = from; }\n    if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }\n    if (typeof code == \"string\") { code = doc.splitLines(code); }\n    makeChange(doc, {from: from, to: to, text: code, origin: origin});\n  }\n\n  // Rebasing/resetting history to deal with externally-sourced changes\n\n  function rebaseHistSelSingle(pos, from, to, diff) {\n    if (to < pos.line) {\n      pos.line += diff;\n    } else if (from < pos.line) {\n      pos.line = from;\n      pos.ch = 0;\n    }\n  }\n\n  // Tries to rebase an array of history events given a change in the\n  // document. If the change touches the same lines as the event, the\n  // event, and everything 'behind' it, is discarded. If the change is\n  // before the event, the event's positions are updated. Uses a\n  // copy-on-write scheme for the positions, to avoid having to\n  // reallocate them all on every rebase, but also avoid problems with\n  // shared position objects being unsafely updated.\n  function rebaseHistArray(array, from, to, diff) {\n    for (var i = 0; i < array.length; ++i) {\n      var sub = array[i], ok = true;\n      if (sub.ranges) {\n        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }\n        for (var j = 0; j < sub.ranges.length; j++) {\n          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);\n          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);\n        }\n        continue\n      }\n      for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {\n        var cur = sub.changes[j$1];\n        if (to < cur.from.line) {\n          cur.from = Pos(cur.from.line + diff, cur.from.ch);\n          cur.to = Pos(cur.to.line + diff, cur.to.ch);\n        } else if (from <= cur.to.line) {\n          ok = false;\n          break\n        }\n      }\n      if (!ok) {\n        array.splice(0, i + 1);\n        i = 0;\n      }\n    }\n  }\n\n  function rebaseHist(hist, change) {\n    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;\n    rebaseHistArray(hist.done, from, to, diff);\n    rebaseHistArray(hist.undone, from, to, diff);\n  }\n\n  // Utility for applying a change to a line by handle or number,\n  // returning the number and optionally registering the line as\n  // changed.\n  function changeLine(doc, handle, changeType, op) {\n    var no = handle, line = handle;\n    if (typeof handle == \"number\") { line = getLine(doc, clipLine(doc, handle)); }\n    else { no = lineNo(handle); }\n    if (no == null) { return null }\n    if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }\n    return line\n  }\n\n  // The document is represented as a BTree consisting of leaves, with\n  // chunk of lines in them, and branches, with up to ten leaves or\n  // other branch nodes below them. The top node is always a branch\n  // node, and is the document object itself (meaning it has\n  // additional methods and properties).\n  //\n  // All nodes have parent links. The tree is used both to go from\n  // line numbers to line objects, and to go from objects to numbers.\n  // It also indexes by height, and is used to convert between height\n  // and line object, and to find the total height of the document.\n  //\n  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html\n\n  function LeafChunk(lines) {\n    var this$1 = this;\n\n    this.lines = lines;\n    this.parent = null;\n    var height = 0;\n    for (var i = 0; i < lines.length; ++i) {\n      lines[i].parent = this$1;\n      height += lines[i].height;\n    }\n    this.height = height;\n  }\n\n  LeafChunk.prototype = {\n    chunkSize: function() { return this.lines.length },\n\n    // Remove the n lines at offset 'at'.\n    removeInner: function(at, n) {\n      var this$1 = this;\n\n      for (var i = at, e = at + n; i < e; ++i) {\n        var line = this$1.lines[i];\n        this$1.height -= line.height;\n        cleanUpLine(line);\n        signalLater(line, \"delete\");\n      }\n      this.lines.splice(at, n);\n    },\n\n    // Helper used to collapse a small branch into a single leaf.\n    collapse: function(lines) {\n      lines.push.apply(lines, this.lines);\n    },\n\n    // Insert the given array of lines at offset 'at', count them as\n    // having the given height.\n    insertInner: function(at, lines, height) {\n      var this$1 = this;\n\n      this.height += height;\n      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));\n      for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; }\n    },\n\n    // Used to iterate over a part of the tree.\n    iterN: function(at, n, op) {\n      var this$1 = this;\n\n      for (var e = at + n; at < e; ++at)\n        { if (op(this$1.lines[at])) { return true } }\n    }\n  };\n\n  function BranchChunk(children) {\n    var this$1 = this;\n\n    this.children = children;\n    var size = 0, height = 0;\n    for (var i = 0; i < children.length; ++i) {\n      var ch = children[i];\n      size += ch.chunkSize(); height += ch.height;\n      ch.parent = this$1;\n    }\n    this.size = size;\n    this.height = height;\n    this.parent = null;\n  }\n\n  BranchChunk.prototype = {\n    chunkSize: function() { return this.size },\n\n    removeInner: function(at, n) {\n      var this$1 = this;\n\n      this.size -= n;\n      for (var i = 0; i < this.children.length; ++i) {\n        var child = this$1.children[i], sz = child.chunkSize();\n        if (at < sz) {\n          var rm = Math.min(n, sz - at), oldHeight = child.height;\n          child.removeInner(at, rm);\n          this$1.height -= oldHeight - child.height;\n          if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; }\n          if ((n -= rm) == 0) { break }\n          at = 0;\n        } else { at -= sz; }\n      }\n      // If the result is smaller than 25 lines, ensure that it is a\n      // single leaf node.\n      if (this.size - n < 25 &&\n          (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {\n        var lines = [];\n        this.collapse(lines);\n        this.children = [new LeafChunk(lines)];\n        this.children[0].parent = this;\n      }\n    },\n\n    collapse: function(lines) {\n      var this$1 = this;\n\n      for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); }\n    },\n\n    insertInner: function(at, lines, height) {\n      var this$1 = this;\n\n      this.size += lines.length;\n      this.height += height;\n      for (var i = 0; i < this.children.length; ++i) {\n        var child = this$1.children[i], sz = child.chunkSize();\n        if (at <= sz) {\n          child.insertInner(at, lines, height);\n          if (child.lines && child.lines.length > 50) {\n            // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.\n            // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.\n            var remaining = child.lines.length % 25 + 25;\n            for (var pos = remaining; pos < child.lines.length;) {\n              var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));\n              child.height -= leaf.height;\n              this$1.children.splice(++i, 0, leaf);\n              leaf.parent = this$1;\n            }\n            child.lines = child.lines.slice(0, remaining);\n            this$1.maybeSpill();\n          }\n          break\n        }\n        at -= sz;\n      }\n    },\n\n    // When a node has grown, check whether it should be split.\n    maybeSpill: function() {\n      if (this.children.length <= 10) { return }\n      var me = this;\n      do {\n        var spilled = me.children.splice(me.children.length - 5, 5);\n        var sibling = new BranchChunk(spilled);\n        if (!me.parent) { // Become the parent node\n          var copy = new BranchChunk(me.children);\n          copy.parent = me;\n          me.children = [copy, sibling];\n          me = copy;\n       } else {\n          me.size -= sibling.size;\n          me.height -= sibling.height;\n          var myIndex = indexOf(me.parent.children, me);\n          me.parent.children.splice(myIndex + 1, 0, sibling);\n        }\n        sibling.parent = me.parent;\n      } while (me.children.length > 10)\n      me.parent.maybeSpill();\n    },\n\n    iterN: function(at, n, op) {\n      var this$1 = this;\n\n      for (var i = 0; i < this.children.length; ++i) {\n        var child = this$1.children[i], sz = child.chunkSize();\n        if (at < sz) {\n          var used = Math.min(n, sz - at);\n          if (child.iterN(at, used, op)) { return true }\n          if ((n -= used) == 0) { break }\n          at = 0;\n        } else { at -= sz; }\n      }\n    }\n  };\n\n  // Line widgets are block elements displayed above or below a line.\n\n  var LineWidget = function(doc, node, options) {\n    var this$1 = this;\n\n    if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))\n      { this$1[opt] = options[opt]; } } }\n    this.doc = doc;\n    this.node = node;\n  };\n\n  LineWidget.prototype.clear = function () {\n      var this$1 = this;\n\n    var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);\n    if (no == null || !ws) { return }\n    for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } }\n    if (!ws.length) { line.widgets = null; }\n    var height = widgetHeight(this);\n    updateLineHeight(line, Math.max(0, line.height - height));\n    if (cm) {\n      runInOp(cm, function () {\n        adjustScrollWhenAboveVisible(cm, line, -height);\n        regLineChange(cm, no, \"widget\");\n      });\n      signalLater(cm, \"lineWidgetCleared\", cm, this, no);\n    }\n  };\n\n  LineWidget.prototype.changed = function () {\n      var this$1 = this;\n\n    var oldH = this.height, cm = this.doc.cm, line = this.line;\n    this.height = null;\n    var diff = widgetHeight(this) - oldH;\n    if (!diff) { return }\n    if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }\n    if (cm) {\n      runInOp(cm, function () {\n        cm.curOp.forceUpdate = true;\n        adjustScrollWhenAboveVisible(cm, line, diff);\n        signalLater(cm, \"lineWidgetChanged\", cm, this$1, lineNo(line));\n      });\n    }\n  };\n  eventMixin(LineWidget);\n\n  function adjustScrollWhenAboveVisible(cm, line, diff) {\n    if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))\n      { addToScrollTop(cm, diff); }\n  }\n\n  function addLineWidget(doc, handle, node, options) {\n    var widget = new LineWidget(doc, node, options);\n    var cm = doc.cm;\n    if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }\n    changeLine(doc, handle, \"widget\", function (line) {\n      var widgets = line.widgets || (line.widgets = []);\n      if (widget.insertAt == null) { widgets.push(widget); }\n      else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }\n      widget.line = line;\n      if (cm && !lineIsHidden(doc, line)) {\n        var aboveVisible = heightAtLine(line) < doc.scrollTop;\n        updateLineHeight(line, line.height + widgetHeight(widget));\n        if (aboveVisible) { addToScrollTop(cm, widget.height); }\n        cm.curOp.forceUpdate = true;\n      }\n      return true\n    });\n    if (cm) { signalLater(cm, \"lineWidgetAdded\", cm, widget, typeof handle == \"number\" ? handle : lineNo(handle)); }\n    return widget\n  }\n\n  // TEXTMARKERS\n\n  // Created with markText and setBookmark methods. A TextMarker is a\n  // handle that can be used to clear or find a marked position in the\n  // document. Line objects hold arrays (markedSpans) containing\n  // {from, to, marker} object pointing to such marker objects, and\n  // indicating that such a marker is present on that line. Multiple\n  // lines may point to the same marker when it spans across lines.\n  // The spans will have null for their from/to properties when the\n  // marker continues beyond the start/end of the line. Markers have\n  // links back to the lines they currently touch.\n\n  // Collapsed markers have unique ids, in order to be able to order\n  // them, which is needed for uniquely determining an outer marker\n  // when they overlap (they may nest, but not partially overlap).\n  var nextMarkerId = 0;\n\n  var TextMarker = function(doc, type) {\n    this.lines = [];\n    this.type = type;\n    this.doc = doc;\n    this.id = ++nextMarkerId;\n  };\n\n  // Clear the marker.\n  TextMarker.prototype.clear = function () {\n      var this$1 = this;\n\n    if (this.explicitlyCleared) { return }\n    var cm = this.doc.cm, withOp = cm && !cm.curOp;\n    if (withOp) { startOperation(cm); }\n    if (hasHandler(this, \"clear\")) {\n      var found = this.find();\n      if (found) { signalLater(this, \"clear\", found.from, found.to); }\n    }\n    var min = null, max = null;\n    for (var i = 0; i < this.lines.length; ++i) {\n      var line = this$1.lines[i];\n      var span = getMarkedSpanFor(line.markedSpans, this$1);\n      if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), \"text\"); }\n      else if (cm) {\n        if (span.to != null) { max = lineNo(line); }\n        if (span.from != null) { min = lineNo(line); }\n      }\n      line.markedSpans = removeMarkedSpan(line.markedSpans, span);\n      if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)\n        { updateLineHeight(line, textHeight(cm.display)); }\n    }\n    if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {\n      var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual);\n      if (len > cm.display.maxLineLength) {\n        cm.display.maxLine = visual;\n        cm.display.maxLineLength = len;\n        cm.display.maxLineChanged = true;\n      }\n    } }\n\n    if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }\n    this.lines.length = 0;\n    this.explicitlyCleared = true;\n    if (this.atomic && this.doc.cantEdit) {\n      this.doc.cantEdit = false;\n      if (cm) { reCheckSelection(cm.doc); }\n    }\n    if (cm) { signalLater(cm, \"markerCleared\", cm, this, min, max); }\n    if (withOp) { endOperation(cm); }\n    if (this.parent) { this.parent.clear(); }\n  };\n\n  // Find the position of the marker in the document. Returns a {from,\n  // to} object by default. Side can be passed to get a specific side\n  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the\n  // Pos objects returned contain a line object, rather than a line\n  // number (used to prevent looking up the same line twice).\n  TextMarker.prototype.find = function (side, lineObj) {\n      var this$1 = this;\n\n    if (side == null && this.type == \"bookmark\") { side = 1; }\n    var from, to;\n    for (var i = 0; i < this.lines.length; ++i) {\n      var line = this$1.lines[i];\n      var span = getMarkedSpanFor(line.markedSpans, this$1);\n      if (span.from != null) {\n        from = Pos(lineObj ? line : lineNo(line), span.from);\n        if (side == -1) { return from }\n      }\n      if (span.to != null) {\n        to = Pos(lineObj ? line : lineNo(line), span.to);\n        if (side == 1) { return to }\n      }\n    }\n    return from && {from: from, to: to}\n  };\n\n  // Signals that the marker's widget changed, and surrounding layout\n  // should be recomputed.\n  TextMarker.prototype.changed = function () {\n      var this$1 = this;\n\n    var pos = this.find(-1, true), widget = this, cm = this.doc.cm;\n    if (!pos || !cm) { return }\n    runInOp(cm, function () {\n      var line = pos.line, lineN = lineNo(pos.line);\n      var view = findViewForLine(cm, lineN);\n      if (view) {\n        clearLineMeasurementCacheFor(view);\n        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;\n      }\n      cm.curOp.updateMaxLine = true;\n      if (!lineIsHidden(widget.doc, line) && widget.height != null) {\n        var oldHeight = widget.height;\n        widget.height = null;\n        var dHeight = widgetHeight(widget) - oldHeight;\n        if (dHeight)\n          { updateLineHeight(line, line.height + dHeight); }\n      }\n      signalLater(cm, \"markerChanged\", cm, this$1);\n    });\n  };\n\n  TextMarker.prototype.attachLine = function (line) {\n    if (!this.lines.length && this.doc.cm) {\n      var op = this.doc.cm.curOp;\n      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)\n        { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }\n    }\n    this.lines.push(line);\n  };\n\n  TextMarker.prototype.detachLine = function (line) {\n    this.lines.splice(indexOf(this.lines, line), 1);\n    if (!this.lines.length && this.doc.cm) {\n      var op = this.doc.cm.curOp\n      ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);\n    }\n  };\n  eventMixin(TextMarker);\n\n  // Create a marker, wire it up to the right lines, and\n  function markText(doc, from, to, options, type) {\n    // Shared markers (across linked documents) are handled separately\n    // (markTextShared will call out to this again, once per\n    // document).\n    if (options && options.shared) { return markTextShared(doc, from, to, options, type) }\n    // Ensure we are in an operation.\n    if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }\n\n    var marker = new TextMarker(doc, type), diff = cmp(from, to);\n    if (options) { copyObj(options, marker, false); }\n    // Don't connect empty markers unless clearWhenEmpty is false\n    if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)\n      { return marker }\n    if (marker.replacedWith) {\n      // Showing up as a widget implies collapsed (widget replaces text)\n      marker.collapsed = true;\n      marker.widgetNode = eltP(\"span\", [marker.replacedWith], \"CodeMirror-widget\");\n      if (!options.handleMouseEvents) { marker.widgetNode.setAttribute(\"cm-ignore-events\", \"true\"); }\n      if (options.insertLeft) { marker.widgetNode.insertLeft = true; }\n    }\n    if (marker.collapsed) {\n      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||\n          from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))\n        { throw new Error(\"Inserting collapsed marker partially overlapping an existing one\") }\n      seeCollapsedSpans();\n    }\n\n    if (marker.addToHistory)\n      { addChangeToHistory(doc, {from: from, to: to, origin: \"markText\"}, doc.sel, NaN); }\n\n    var curLine = from.line, cm = doc.cm, updateMaxLine;\n    doc.iter(curLine, to.line + 1, function (line) {\n      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)\n        { updateMaxLine = true; }\n      if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }\n      addMarkedSpan(line, new MarkedSpan(marker,\n                                         curLine == from.line ? from.ch : null,\n                                         curLine == to.line ? to.ch : null));\n      ++curLine;\n    });\n    // lineIsHidden depends on the presence of the spans, so needs a second pass\n    if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {\n      if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }\n    }); }\n\n    if (marker.clearOnEnter) { on(marker, \"beforeCursorEnter\", function () { return marker.clear(); }); }\n\n    if (marker.readOnly) {\n      seeReadOnlySpans();\n      if (doc.history.done.length || doc.history.undone.length)\n        { doc.clearHistory(); }\n    }\n    if (marker.collapsed) {\n      marker.id = ++nextMarkerId;\n      marker.atomic = true;\n    }\n    if (cm) {\n      // Sync editor state\n      if (updateMaxLine) { cm.curOp.updateMaxLine = true; }\n      if (marker.collapsed)\n        { regChange(cm, from.line, to.line + 1); }\n      else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)\n        { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, \"text\"); } }\n      if (marker.atomic) { reCheckSelection(cm.doc); }\n      signalLater(cm, \"markerAdded\", cm, marker);\n    }\n    return marker\n  }\n\n  // SHARED TEXTMARKERS\n\n  // A shared marker spans multiple linked documents. It is\n  // implemented as a meta-marker-object controlling multiple normal\n  // markers.\n  var SharedTextMarker = function(markers, primary) {\n    var this$1 = this;\n\n    this.markers = markers;\n    this.primary = primary;\n    for (var i = 0; i < markers.length; ++i)\n      { markers[i].parent = this$1; }\n  };\n\n  SharedTextMarker.prototype.clear = function () {\n      var this$1 = this;\n\n    if (this.explicitlyCleared) { return }\n    this.explicitlyCleared = true;\n    for (var i = 0; i < this.markers.length; ++i)\n      { this$1.markers[i].clear(); }\n    signalLater(this, \"clear\");\n  };\n\n  SharedTextMarker.prototype.find = function (side, lineObj) {\n    return this.primary.find(side, lineObj)\n  };\n  eventMixin(SharedTextMarker);\n\n  function markTextShared(doc, from, to, options, type) {\n    options = copyObj(options);\n    options.shared = false;\n    var markers = [markText(doc, from, to, options, type)], primary = markers[0];\n    var widget = options.widgetNode;\n    linkedDocs(doc, function (doc) {\n      if (widget) { options.widgetNode = widget.cloneNode(true); }\n      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));\n      for (var i = 0; i < doc.linked.length; ++i)\n        { if (doc.linked[i].isParent) { return } }\n      primary = lst(markers);\n    });\n    return new SharedTextMarker(markers, primary)\n  }\n\n  function findSharedMarkers(doc) {\n    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })\n  }\n\n  function copySharedMarkers(doc, markers) {\n    for (var i = 0; i < markers.length; i++) {\n      var marker = markers[i], pos = marker.find();\n      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);\n      if (cmp(mFrom, mTo)) {\n        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);\n        marker.markers.push(subMark);\n        subMark.parent = marker;\n      }\n    }\n  }\n\n  function detachSharedMarkers(markers) {\n    var loop = function ( i ) {\n      var marker = markers[i], linked = [marker.primary.doc];\n      linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });\n      for (var j = 0; j < marker.markers.length; j++) {\n        var subMarker = marker.markers[j];\n        if (indexOf(linked, subMarker.doc) == -1) {\n          subMarker.parent = null;\n          marker.markers.splice(j--, 1);\n        }\n      }\n    };\n\n    for (var i = 0; i < markers.length; i++) loop( i );\n  }\n\n  var nextDocId = 0;\n  var Doc = function(text, mode, firstLine, lineSep, direction) {\n    if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }\n    if (firstLine == null) { firstLine = 0; }\n\n    BranchChunk.call(this, [new LeafChunk([new Line(\"\", null)])]);\n    this.first = firstLine;\n    this.scrollTop = this.scrollLeft = 0;\n    this.cantEdit = false;\n    this.cleanGeneration = 1;\n    this.modeFrontier = this.highlightFrontier = firstLine;\n    var start = Pos(firstLine, 0);\n    this.sel = simpleSelection(start);\n    this.history = new History(null);\n    this.id = ++nextDocId;\n    this.modeOption = mode;\n    this.lineSep = lineSep;\n    this.direction = (direction == \"rtl\") ? \"rtl\" : \"ltr\";\n    this.extend = false;\n\n    if (typeof text == \"string\") { text = this.splitLines(text); }\n    updateDoc(this, {from: start, to: start, text: text});\n    setSelection(this, simpleSelection(start), sel_dontScroll);\n  };\n\n  Doc.prototype = createObj(BranchChunk.prototype, {\n    constructor: Doc,\n    // Iterate over the document. Supports two forms -- with only one\n    // argument, it calls that for each line in the document. With\n    // three, it iterates over the range given by the first two (with\n    // the second being non-inclusive).\n    iter: function(from, to, op) {\n      if (op) { this.iterN(from - this.first, to - from, op); }\n      else { this.iterN(this.first, this.first + this.size, from); }\n    },\n\n    // Non-public interface for adding and removing lines.\n    insert: function(at, lines) {\n      var height = 0;\n      for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }\n      this.insertInner(at - this.first, lines, height);\n    },\n    remove: function(at, n) { this.removeInner(at - this.first, n); },\n\n    // From here, the methods are part of the public interface. Most\n    // are also available from CodeMirror (editor) instances.\n\n    getValue: function(lineSep) {\n      var lines = getLines(this, this.first, this.first + this.size);\n      if (lineSep === false) { return lines }\n      return lines.join(lineSep || this.lineSeparator())\n    },\n    setValue: docMethodOp(function(code) {\n      var top = Pos(this.first, 0), last = this.first + this.size - 1;\n      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),\n                        text: this.splitLines(code), origin: \"setValue\", full: true}, true);\n      if (this.cm) { scrollToCoords(this.cm, 0, 0); }\n      setSelection(this, simpleSelection(top), sel_dontScroll);\n    }),\n    replaceRange: function(code, from, to, origin) {\n      from = clipPos(this, from);\n      to = to ? clipPos(this, to) : from;\n      replaceRange(this, code, from, to, origin);\n    },\n    getRange: function(from, to, lineSep) {\n      var lines = getBetween(this, clipPos(this, from), clipPos(this, to));\n      if (lineSep === false) { return lines }\n      return lines.join(lineSep || this.lineSeparator())\n    },\n\n    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},\n\n    getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},\n    getLineNumber: function(line) {return lineNo(line)},\n\n    getLineHandleVisualStart: function(line) {\n      if (typeof line == \"number\") { line = getLine(this, line); }\n      return visualLine(line)\n    },\n\n    lineCount: function() {return this.size},\n    firstLine: function() {return this.first},\n    lastLine: function() {return this.first + this.size - 1},\n\n    clipPos: function(pos) {return clipPos(this, pos)},\n\n    getCursor: function(start) {\n      var range$$1 = this.sel.primary(), pos;\n      if (start == null || start == \"head\") { pos = range$$1.head; }\n      else if (start == \"anchor\") { pos = range$$1.anchor; }\n      else if (start == \"end\" || start == \"to\" || start === false) { pos = range$$1.to(); }\n      else { pos = range$$1.from(); }\n      return pos\n    },\n    listSelections: function() { return this.sel.ranges },\n    somethingSelected: function() {return this.sel.somethingSelected()},\n\n    setCursor: docMethodOp(function(line, ch, options) {\n      setSimpleSelection(this, clipPos(this, typeof line == \"number\" ? Pos(line, ch || 0) : line), null, options);\n    }),\n    setSelection: docMethodOp(function(anchor, head, options) {\n      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);\n    }),\n    extendSelection: docMethodOp(function(head, other, options) {\n      extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);\n    }),\n    extendSelections: docMethodOp(function(heads, options) {\n      extendSelections(this, clipPosArray(this, heads), options);\n    }),\n    extendSelectionsBy: docMethodOp(function(f, options) {\n      var heads = map(this.sel.ranges, f);\n      extendSelections(this, clipPosArray(this, heads), options);\n    }),\n    setSelections: docMethodOp(function(ranges, primary, options) {\n      var this$1 = this;\n\n      if (!ranges.length) { return }\n      var out = [];\n      for (var i = 0; i < ranges.length; i++)\n        { out[i] = new Range(clipPos(this$1, ranges[i].anchor),\n                           clipPos(this$1, ranges[i].head)); }\n      if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }\n      setSelection(this, normalizeSelection(this.cm, out, primary), options);\n    }),\n    addSelection: docMethodOp(function(anchor, head, options) {\n      var ranges = this.sel.ranges.slice(0);\n      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));\n      setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);\n    }),\n\n    getSelection: function(lineSep) {\n      var this$1 = this;\n\n      var ranges = this.sel.ranges, lines;\n      for (var i = 0; i < ranges.length; i++) {\n        var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());\n        lines = lines ? lines.concat(sel) : sel;\n      }\n      if (lineSep === false) { return lines }\n      else { return lines.join(lineSep || this.lineSeparator()) }\n    },\n    getSelections: function(lineSep) {\n      var this$1 = this;\n\n      var parts = [], ranges = this.sel.ranges;\n      for (var i = 0; i < ranges.length; i++) {\n        var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());\n        if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); }\n        parts[i] = sel;\n      }\n      return parts\n    },\n    replaceSelection: function(code, collapse, origin) {\n      var dup = [];\n      for (var i = 0; i < this.sel.ranges.length; i++)\n        { dup[i] = code; }\n      this.replaceSelections(dup, collapse, origin || \"+input\");\n    },\n    replaceSelections: docMethodOp(function(code, collapse, origin) {\n      var this$1 = this;\n\n      var changes = [], sel = this.sel;\n      for (var i = 0; i < sel.ranges.length; i++) {\n        var range$$1 = sel.ranges[i];\n        changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin};\n      }\n      var newSel = collapse && collapse != \"end\" && computeReplacedSel(this, changes, collapse);\n      for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)\n        { makeChange(this$1, changes[i$1]); }\n      if (newSel) { setSelectionReplaceHistory(this, newSel); }\n      else if (this.cm) { ensureCursorVisible(this.cm); }\n    }),\n    undo: docMethodOp(function() {makeChangeFromHistory(this, \"undo\");}),\n    redo: docMethodOp(function() {makeChangeFromHistory(this, \"redo\");}),\n    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, \"undo\", true);}),\n    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, \"redo\", true);}),\n\n    setExtending: function(val) {this.extend = val;},\n    getExtending: function() {return this.extend},\n\n    historySize: function() {\n      var hist = this.history, done = 0, undone = 0;\n      for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }\n      for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }\n      return {undo: done, redo: undone}\n    },\n    clearHistory: function() {this.history = new History(this.history.maxGeneration);},\n\n    markClean: function() {\n      this.cleanGeneration = this.changeGeneration(true);\n    },\n    changeGeneration: function(forceSplit) {\n      if (forceSplit)\n        { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }\n      return this.history.generation\n    },\n    isClean: function (gen) {\n      return this.history.generation == (gen || this.cleanGeneration)\n    },\n\n    getHistory: function() {\n      return {done: copyHistoryArray(this.history.done),\n              undone: copyHistoryArray(this.history.undone)}\n    },\n    setHistory: function(histData) {\n      var hist = this.history = new History(this.history.maxGeneration);\n      hist.done = copyHistoryArray(histData.done.slice(0), null, true);\n      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);\n    },\n\n    setGutterMarker: docMethodOp(function(line, gutterID, value) {\n      return changeLine(this, line, \"gutter\", function (line) {\n        var markers = line.gutterMarkers || (line.gutterMarkers = {});\n        markers[gutterID] = value;\n        if (!value && isEmpty(markers)) { line.gutterMarkers = null; }\n        return true\n      })\n    }),\n\n    clearGutter: docMethodOp(function(gutterID) {\n      var this$1 = this;\n\n      this.iter(function (line) {\n        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {\n          changeLine(this$1, line, \"gutter\", function () {\n            line.gutterMarkers[gutterID] = null;\n            if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }\n            return true\n          });\n        }\n      });\n    }),\n\n    lineInfo: function(line) {\n      var n;\n      if (typeof line == \"number\") {\n        if (!isLine(this, line)) { return null }\n        n = line;\n        line = getLine(this, line);\n        if (!line) { return null }\n      } else {\n        n = lineNo(line);\n        if (n == null) { return null }\n      }\n      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,\n              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,\n              widgets: line.widgets}\n    },\n\n    addLineClass: docMethodOp(function(handle, where, cls) {\n      return changeLine(this, handle, where == \"gutter\" ? \"gutter\" : \"class\", function (line) {\n        var prop = where == \"text\" ? \"textClass\"\n                 : where == \"background\" ? \"bgClass\"\n                 : where == \"gutter\" ? \"gutterClass\" : \"wrapClass\";\n        if (!line[prop]) { line[prop] = cls; }\n        else if (classTest(cls).test(line[prop])) { return false }\n        else { line[prop] += \" \" + cls; }\n        return true\n      })\n    }),\n    removeLineClass: docMethodOp(function(handle, where, cls) {\n      return changeLine(this, handle, where == \"gutter\" ? \"gutter\" : \"class\", function (line) {\n        var prop = where == \"text\" ? \"textClass\"\n                 : where == \"background\" ? \"bgClass\"\n                 : where == \"gutter\" ? \"gutterClass\" : \"wrapClass\";\n        var cur = line[prop];\n        if (!cur) { return false }\n        else if (cls == null) { line[prop] = null; }\n        else {\n          var found = cur.match(classTest(cls));\n          if (!found) { return false }\n          var end = found.index + found[0].length;\n          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? \"\" : \" \") + cur.slice(end) || null;\n        }\n        return true\n      })\n    }),\n\n    addLineWidget: docMethodOp(function(handle, node, options) {\n      return addLineWidget(this, handle, node, options)\n    }),\n    removeLineWidget: function(widget) { widget.clear(); },\n\n    markText: function(from, to, options) {\n      return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || \"range\")\n    },\n    setBookmark: function(pos, options) {\n      var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),\n                      insertLeft: options && options.insertLeft,\n                      clearWhenEmpty: false, shared: options && options.shared,\n                      handleMouseEvents: options && options.handleMouseEvents};\n      pos = clipPos(this, pos);\n      return markText(this, pos, pos, realOpts, \"bookmark\")\n    },\n    findMarksAt: function(pos) {\n      pos = clipPos(this, pos);\n      var markers = [], spans = getLine(this, pos.line).markedSpans;\n      if (spans) { for (var i = 0; i < spans.length; ++i) {\n        var span = spans[i];\n        if ((span.from == null || span.from <= pos.ch) &&\n            (span.to == null || span.to >= pos.ch))\n          { markers.push(span.marker.parent || span.marker); }\n      } }\n      return markers\n    },\n    findMarks: function(from, to, filter) {\n      from = clipPos(this, from); to = clipPos(this, to);\n      var found = [], lineNo$$1 = from.line;\n      this.iter(from.line, to.line + 1, function (line) {\n        var spans = line.markedSpans;\n        if (spans) { for (var i = 0; i < spans.length; i++) {\n          var span = spans[i];\n          if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||\n                span.from == null && lineNo$$1 != from.line ||\n                span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&\n              (!filter || filter(span.marker)))\n            { found.push(span.marker.parent || span.marker); }\n        } }\n        ++lineNo$$1;\n      });\n      return found\n    },\n    getAllMarks: function() {\n      var markers = [];\n      this.iter(function (line) {\n        var sps = line.markedSpans;\n        if (sps) { for (var i = 0; i < sps.length; ++i)\n          { if (sps[i].from != null) { markers.push(sps[i].marker); } } }\n      });\n      return markers\n    },\n\n    posFromIndex: function(off) {\n      var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length;\n      this.iter(function (line) {\n        var sz = line.text.length + sepSize;\n        if (sz > off) { ch = off; return true }\n        off -= sz;\n        ++lineNo$$1;\n      });\n      return clipPos(this, Pos(lineNo$$1, ch))\n    },\n    indexFromPos: function (coords) {\n      coords = clipPos(this, coords);\n      var index = coords.ch;\n      if (coords.line < this.first || coords.ch < 0) { return 0 }\n      var sepSize = this.lineSeparator().length;\n      this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value\n        index += line.text.length + sepSize;\n      });\n      return index\n    },\n\n    copy: function(copyHistory) {\n      var doc = new Doc(getLines(this, this.first, this.first + this.size),\n                        this.modeOption, this.first, this.lineSep, this.direction);\n      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;\n      doc.sel = this.sel;\n      doc.extend = false;\n      if (copyHistory) {\n        doc.history.undoDepth = this.history.undoDepth;\n        doc.setHistory(this.getHistory());\n      }\n      return doc\n    },\n\n    linkedDoc: function(options) {\n      if (!options) { options = {}; }\n      var from = this.first, to = this.first + this.size;\n      if (options.from != null && options.from > from) { from = options.from; }\n      if (options.to != null && options.to < to) { to = options.to; }\n      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);\n      if (options.sharedHist) { copy.history = this.history\n      ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});\n      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];\n      copySharedMarkers(copy, findSharedMarkers(this));\n      return copy\n    },\n    unlinkDoc: function(other) {\n      var this$1 = this;\n\n      if (other instanceof CodeMirror) { other = other.doc; }\n      if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {\n        var link = this$1.linked[i];\n        if (link.doc != other) { continue }\n        this$1.linked.splice(i, 1);\n        other.unlinkDoc(this$1);\n        detachSharedMarkers(findSharedMarkers(this$1));\n        break\n      } }\n      // If the histories were shared, split them again\n      if (other.history == this.history) {\n        var splitIds = [other.id];\n        linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);\n        other.history = new History(null);\n        other.history.done = copyHistoryArray(this.history.done, splitIds);\n        other.history.undone = copyHistoryArray(this.history.undone, splitIds);\n      }\n    },\n    iterLinkedDocs: function(f) {linkedDocs(this, f);},\n\n    getMode: function() {return this.mode},\n    getEditor: function() {return this.cm},\n\n    splitLines: function(str) {\n      if (this.lineSep) { return str.split(this.lineSep) }\n      return splitLinesAuto(str)\n    },\n    lineSeparator: function() { return this.lineSep || \"\\n\" },\n\n    setDirection: docMethodOp(function (dir) {\n      if (dir != \"rtl\") { dir = \"ltr\"; }\n      if (dir == this.direction) { return }\n      this.direction = dir;\n      this.iter(function (line) { return line.order = null; });\n      if (this.cm) { directionChanged(this.cm); }\n    })\n  });\n\n  // Public alias.\n  Doc.prototype.eachLine = Doc.prototype.iter;\n\n  // Kludge to work around strange IE behavior where it'll sometimes\n  // re-fire a series of drag-related events right after the drop (#1551)\n  var lastDrop = 0;\n\n  function onDrop(e) {\n    var cm = this;\n    clearDragCursor(cm);\n    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))\n      { return }\n    e_preventDefault(e);\n    if (ie) { lastDrop = +new Date; }\n    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;\n    if (!pos || cm.isReadOnly()) { return }\n    // Might be a file drop, in which case we simply extract the text\n    // and insert it.\n    if (files && files.length && window.FileReader && window.File) {\n      var n = files.length, text = Array(n), read = 0;\n      var loadFile = function (file, i) {\n        if (cm.options.allowDropFileTypes &&\n            indexOf(cm.options.allowDropFileTypes, file.type) == -1)\n          { return }\n\n        var reader = new FileReader;\n        reader.onload = operation(cm, function () {\n          var content = reader.result;\n          if (/[\\x00-\\x08\\x0e-\\x1f]{2}/.test(content)) { content = \"\"; }\n          text[i] = content;\n          if (++read == n) {\n            pos = clipPos(cm.doc, pos);\n            var change = {from: pos, to: pos,\n                          text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),\n                          origin: \"paste\"};\n            makeChange(cm.doc, change);\n            setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));\n          }\n        });\n        reader.readAsText(file);\n      };\n      for (var i = 0; i < n; ++i) { loadFile(files[i], i); }\n    } else { // Normal drop\n      // Don't do a replace if the drop happened inside of the selected text.\n      if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {\n        cm.state.draggingText(e);\n        // Ensure the editor is re-focused\n        setTimeout(function () { return cm.display.input.focus(); }, 20);\n        return\n      }\n      try {\n        var text$1 = e.dataTransfer.getData(\"Text\");\n        if (text$1) {\n          var selected;\n          if (cm.state.draggingText && !cm.state.draggingText.copy)\n            { selected = cm.listSelections(); }\n          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));\n          if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)\n            { replaceRange(cm.doc, \"\", selected[i$1].anchor, selected[i$1].head, \"drag\"); } }\n          cm.replaceSelection(text$1, \"around\", \"paste\");\n          cm.display.input.focus();\n        }\n      }\n      catch(e){}\n    }\n  }\n\n  function onDragStart(cm, e) {\n    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }\n    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }\n\n    e.dataTransfer.setData(\"Text\", cm.getSelection());\n    e.dataTransfer.effectAllowed = \"copyMove\";\n\n    // Use dummy image instead of default browsers image.\n    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.\n    if (e.dataTransfer.setDragImage && !safari) {\n      var img = elt(\"img\", null, null, \"position: fixed; left: 0; top: 0;\");\n      img.src = \"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\";\n      if (presto) {\n        img.width = img.height = 1;\n        cm.display.wrapper.appendChild(img);\n        // Force a relayout, or Opera won't use our image for some obscure reason\n        img._top = img.offsetTop;\n      }\n      e.dataTransfer.setDragImage(img, 0, 0);\n      if (presto) { img.parentNode.removeChild(img); }\n    }\n  }\n\n  function onDragOver(cm, e) {\n    var pos = posFromMouse(cm, e);\n    if (!pos) { return }\n    var frag = document.createDocumentFragment();\n    drawSelectionCursor(cm, pos, frag);\n    if (!cm.display.dragCursor) {\n      cm.display.dragCursor = elt(\"div\", null, \"CodeMirror-cursors CodeMirror-dragcursors\");\n      cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);\n    }\n    removeChildrenAndAdd(cm.display.dragCursor, frag);\n  }\n\n  function clearDragCursor(cm) {\n    if (cm.display.dragCursor) {\n      cm.display.lineSpace.removeChild(cm.display.dragCursor);\n      cm.display.dragCursor = null;\n    }\n  }\n\n  // These must be handled carefully, because naively registering a\n  // handler for each editor will cause the editors to never be\n  // garbage collected.\n\n  function forEachCodeMirror(f) {\n    if (!document.getElementsByClassName) { return }\n    var byClass = document.getElementsByClassName(\"CodeMirror\");\n    for (var i = 0; i < byClass.length; i++) {\n      var cm = byClass[i].CodeMirror;\n      if (cm) { f(cm); }\n    }\n  }\n\n  var globalsRegistered = false;\n  function ensureGlobalHandlers() {\n    if (globalsRegistered) { return }\n    registerGlobalHandlers();\n    globalsRegistered = true;\n  }\n  function registerGlobalHandlers() {\n    // When the window resizes, we need to refresh active editors.\n    var resizeTimer;\n    on(window, \"resize\", function () {\n      if (resizeTimer == null) { resizeTimer = setTimeout(function () {\n        resizeTimer = null;\n        forEachCodeMirror(onResize);\n      }, 100); }\n    });\n    // When the window loses focus, we want to show the editor as blurred\n    on(window, \"blur\", function () { return forEachCodeMirror(onBlur); });\n  }\n  // Called when the window resizes\n  function onResize(cm) {\n    var d = cm.display;\n    // Might be a text scaling operation, clear size caches.\n    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;\n    d.scrollbarsClipped = false;\n    cm.setSize();\n  }\n\n  var keyNames = {\n    3: \"Pause\", 8: \"Backspace\", 9: \"Tab\", 13: \"Enter\", 16: \"Shift\", 17: \"Ctrl\", 18: \"Alt\",\n    19: \"Pause\", 20: \"CapsLock\", 27: \"Esc\", 32: \"Space\", 33: \"PageUp\", 34: \"PageDown\", 35: \"End\",\n    36: \"Home\", 37: \"Left\", 38: \"Up\", 39: \"Right\", 40: \"Down\", 44: \"PrintScrn\", 45: \"Insert\",\n    46: \"Delete\", 59: \";\", 61: \"=\", 91: \"Mod\", 92: \"Mod\", 93: \"Mod\",\n    106: \"*\", 107: \"=\", 109: \"-\", 110: \".\", 111: \"/\", 127: \"Delete\", 145: \"ScrollLock\",\n    173: \"-\", 186: \";\", 187: \"=\", 188: \",\", 189: \"-\", 190: \".\", 191: \"/\", 192: \"`\", 219: \"[\", 220: \"\\\\\",\n    221: \"]\", 222: \"'\", 63232: \"Up\", 63233: \"Down\", 63234: \"Left\", 63235: \"Right\", 63272: \"Delete\",\n    63273: \"Home\", 63275: \"End\", 63276: \"PageUp\", 63277: \"PageDown\", 63302: \"Insert\"\n  };\n\n  // Number keys\n  for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }\n  // Alphabetic keys\n  for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }\n  // Function keys\n  for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = \"F\" + i$2; }\n\n  var keyMap = {};\n\n  keyMap.basic = {\n    \"Left\": \"goCharLeft\", \"Right\": \"goCharRight\", \"Up\": \"goLineUp\", \"Down\": \"goLineDown\",\n    \"End\": \"goLineEnd\", \"Home\": \"goLineStartSmart\", \"PageUp\": \"goPageUp\", \"PageDown\": \"goPageDown\",\n    \"Delete\": \"delCharAfter\", \"Backspace\": \"delCharBefore\", \"Shift-Backspace\": \"delCharBefore\",\n    \"Tab\": \"defaultTab\", \"Shift-Tab\": \"indentAuto\",\n    \"Enter\": \"newlineAndIndent\", \"Insert\": \"toggleOverwrite\",\n    \"Esc\": \"singleSelection\"\n  };\n  // Note that the save and find-related commands aren't defined by\n  // default. User code or addons can define them. Unknown commands\n  // are simply ignored.\n  keyMap.pcDefault = {\n    \"Ctrl-A\": \"selectAll\", \"Ctrl-D\": \"deleteLine\", \"Ctrl-Z\": \"undo\", \"Shift-Ctrl-Z\": \"redo\", \"Ctrl-Y\": \"redo\",\n    \"Ctrl-Home\": \"goDocStart\", \"Ctrl-End\": \"goDocEnd\", \"Ctrl-Up\": \"goLineUp\", \"Ctrl-Down\": \"goLineDown\",\n    \"Ctrl-Left\": \"goGroupLeft\", \"Ctrl-Right\": \"goGroupRight\", \"Alt-Left\": \"goLineStart\", \"Alt-Right\": \"goLineEnd\",\n    \"Ctrl-Backspace\": \"delGroupBefore\", \"Ctrl-Delete\": \"delGroupAfter\", \"Ctrl-S\": \"save\", \"Ctrl-F\": \"find\",\n    \"Ctrl-G\": \"findNext\", \"Shift-Ctrl-G\": \"findPrev\", \"Shift-Ctrl-F\": \"replace\", \"Shift-Ctrl-R\": \"replaceAll\",\n    \"Ctrl-[\": \"indentLess\", \"Ctrl-]\": \"indentMore\",\n    \"Ctrl-U\": \"undoSelection\", \"Shift-Ctrl-U\": \"redoSelection\", \"Alt-U\": \"redoSelection\",\n    \"fallthrough\": \"basic\"\n  };\n  // Very basic readline/emacs-style bindings, which are standard on Mac.\n  keyMap.emacsy = {\n    \"Ctrl-F\": \"goCharRight\", \"Ctrl-B\": \"goCharLeft\", \"Ctrl-P\": \"goLineUp\", \"Ctrl-N\": \"goLineDown\",\n    \"Alt-F\": \"goWordRight\", \"Alt-B\": \"goWordLeft\", \"Ctrl-A\": \"goLineStart\", \"Ctrl-E\": \"goLineEnd\",\n    \"Ctrl-V\": \"goPageDown\", \"Shift-Ctrl-V\": \"goPageUp\", \"Ctrl-D\": \"delCharAfter\", \"Ctrl-H\": \"delCharBefore\",\n    \"Alt-D\": \"delWordAfter\", \"Alt-Backspace\": \"delWordBefore\", \"Ctrl-K\": \"killLine\", \"Ctrl-T\": \"transposeChars\",\n    \"Ctrl-O\": \"openLine\"\n  };\n  keyMap.macDefault = {\n    \"Cmd-A\": \"selectAll\", \"Cmd-D\": \"deleteLine\", \"Cmd-Z\": \"undo\", \"Shift-Cmd-Z\": \"redo\", \"Cmd-Y\": \"redo\",\n    \"Cmd-Home\": \"goDocStart\", \"Cmd-Up\": \"goDocStart\", \"Cmd-End\": \"goDocEnd\", \"Cmd-Down\": \"goDocEnd\", \"Alt-Left\": \"goGroupLeft\",\n    \"Alt-Right\": \"goGroupRight\", \"Cmd-Left\": \"goLineLeft\", \"Cmd-Right\": \"goLineRight\", \"Alt-Backspace\": \"delGroupBefore\",\n    \"Ctrl-Alt-Backspace\": \"delGroupAfter\", \"Alt-Delete\": \"delGroupAfter\", \"Cmd-S\": \"save\", \"Cmd-F\": \"find\",\n    \"Cmd-G\": \"findNext\", \"Shift-Cmd-G\": \"findPrev\", \"Cmd-Alt-F\": \"replace\", \"Shift-Cmd-Alt-F\": \"replaceAll\",\n    \"Cmd-[\": \"indentLess\", \"Cmd-]\": \"indentMore\", \"Cmd-Backspace\": \"delWrappedLineLeft\", \"Cmd-Delete\": \"delWrappedLineRight\",\n    \"Cmd-U\": \"undoSelection\", \"Shift-Cmd-U\": \"redoSelection\", \"Ctrl-Up\": \"goDocStart\", \"Ctrl-Down\": \"goDocEnd\",\n    \"fallthrough\": [\"basic\", \"emacsy\"]\n  };\n  keyMap[\"default\"] = mac ? keyMap.macDefault : keyMap.pcDefault;\n\n  // KEYMAP DISPATCH\n\n  function normalizeKeyName(name) {\n    var parts = name.split(/-(?!$)/);\n    name = parts[parts.length - 1];\n    var alt, ctrl, shift, cmd;\n    for (var i = 0; i < parts.length - 1; i++) {\n      var mod = parts[i];\n      if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }\n      else if (/^a(lt)?$/i.test(mod)) { alt = true; }\n      else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }\n      else if (/^s(hift)?$/i.test(mod)) { shift = true; }\n      else { throw new Error(\"Unrecognized modifier name: \" + mod) }\n    }\n    if (alt) { name = \"Alt-\" + name; }\n    if (ctrl) { name = \"Ctrl-\" + name; }\n    if (cmd) { name = \"Cmd-\" + name; }\n    if (shift) { name = \"Shift-\" + name; }\n    return name\n  }\n\n  // This is a kludge to keep keymaps mostly working as raw objects\n  // (backwards compatibility) while at the same time support features\n  // like normalization and multi-stroke key bindings. It compiles a\n  // new normalized keymap, and then updates the old object to reflect\n  // this.\n  function normalizeKeyMap(keymap) {\n    var copy = {};\n    for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {\n      var value = keymap[keyname];\n      if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }\n      if (value == \"...\") { delete keymap[keyname]; continue }\n\n      var keys = map(keyname.split(\" \"), normalizeKeyName);\n      for (var i = 0; i < keys.length; i++) {\n        var val = (void 0), name = (void 0);\n        if (i == keys.length - 1) {\n          name = keys.join(\" \");\n          val = value;\n        } else {\n          name = keys.slice(0, i + 1).join(\" \");\n          val = \"...\";\n        }\n        var prev = copy[name];\n        if (!prev) { copy[name] = val; }\n        else if (prev != val) { throw new Error(\"Inconsistent bindings for \" + name) }\n      }\n      delete keymap[keyname];\n    } }\n    for (var prop in copy) { keymap[prop] = copy[prop]; }\n    return keymap\n  }\n\n  function lookupKey(key, map$$1, handle, context) {\n    map$$1 = getKeyMap(map$$1);\n    var found = map$$1.call ? map$$1.call(key, context) : map$$1[key];\n    if (found === false) { return \"nothing\" }\n    if (found === \"...\") { return \"multi\" }\n    if (found != null && handle(found)) { return \"handled\" }\n\n    if (map$$1.fallthrough) {\n      if (Object.prototype.toString.call(map$$1.fallthrough) != \"[object Array]\")\n        { return lookupKey(key, map$$1.fallthrough, handle, context) }\n      for (var i = 0; i < map$$1.fallthrough.length; i++) {\n        var result = lookupKey(key, map$$1.fallthrough[i], handle, context);\n        if (result) { return result }\n      }\n    }\n  }\n\n  // Modifier key presses don't count as 'real' key presses for the\n  // purpose of keymap fallthrough.\n  function isModifierKey(value) {\n    var name = typeof value == \"string\" ? value : keyNames[value.keyCode];\n    return name == \"Ctrl\" || name == \"Alt\" || name == \"Shift\" || name == \"Mod\"\n  }\n\n  function addModifierNames(name, event, noShift) {\n    var base = name;\n    if (event.altKey && base != \"Alt\") { name = \"Alt-\" + name; }\n    if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != \"Ctrl\") { name = \"Ctrl-\" + name; }\n    if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != \"Cmd\") { name = \"Cmd-\" + name; }\n    if (!noShift && event.shiftKey && base != \"Shift\") { name = \"Shift-\" + name; }\n    return name\n  }\n\n  // Look up the name of a key as indicated by an event object.\n  function keyName(event, noShift) {\n    if (presto && event.keyCode == 34 && event[\"char\"]) { return false }\n    var name = keyNames[event.keyCode];\n    if (name == null || event.altGraphKey) { return false }\n    // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,\n    // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)\n    if (event.keyCode == 3 && event.code) { name = event.code; }\n    return addModifierNames(name, event, noShift)\n  }\n\n  function getKeyMap(val) {\n    return typeof val == \"string\" ? keyMap[val] : val\n  }\n\n  // Helper for deleting text near the selection(s), used to implement\n  // backspace, delete, and similar functionality.\n  function deleteNearSelection(cm, compute) {\n    var ranges = cm.doc.sel.ranges, kill = [];\n    // Build up a set of ranges to kill first, merging overlapping\n    // ranges.\n    for (var i = 0; i < ranges.length; i++) {\n      var toKill = compute(ranges[i]);\n      while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {\n        var replaced = kill.pop();\n        if (cmp(replaced.from, toKill.from) < 0) {\n          toKill.from = replaced.from;\n          break\n        }\n      }\n      kill.push(toKill);\n    }\n    // Next, remove those actual ranges.\n    runInOp(cm, function () {\n      for (var i = kill.length - 1; i >= 0; i--)\n        { replaceRange(cm.doc, \"\", kill[i].from, kill[i].to, \"+delete\"); }\n      ensureCursorVisible(cm);\n    });\n  }\n\n  function moveCharLogically(line, ch, dir) {\n    var target = skipExtendingChars(line.text, ch + dir, dir);\n    return target < 0 || target > line.text.length ? null : target\n  }\n\n  function moveLogically(line, start, dir) {\n    var ch = moveCharLogically(line, start.ch, dir);\n    return ch == null ? null : new Pos(start.line, ch, dir < 0 ? \"after\" : \"before\")\n  }\n\n  function endOfLine(visually, cm, lineObj, lineNo, dir) {\n    if (visually) {\n      var order = getOrder(lineObj, cm.doc.direction);\n      if (order) {\n        var part = dir < 0 ? lst(order) : order[0];\n        var moveInStorageOrder = (dir < 0) == (part.level == 1);\n        var sticky = moveInStorageOrder ? \"after\" : \"before\";\n        var ch;\n        // With a wrapped rtl chunk (possibly spanning multiple bidi parts),\n        // it could be that the last bidi part is not on the last visual line,\n        // since visual lines contain content order-consecutive chunks.\n        // Thus, in rtl, we are looking for the first (content-order) character\n        // in the rtl chunk that is on the last line (that is, the same line\n        // as the last (content-order) character).\n        if (part.level > 0 || cm.doc.direction == \"rtl\") {\n          var prep = prepareMeasureForLine(cm, lineObj);\n          ch = dir < 0 ? lineObj.text.length - 1 : 0;\n          var targetTop = measureCharPrepared(cm, prep, ch).top;\n          ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);\n          if (sticky == \"before\") { ch = moveCharLogically(lineObj, ch, 1); }\n        } else { ch = dir < 0 ? part.to : part.from; }\n        return new Pos(lineNo, ch, sticky)\n      }\n    }\n    return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? \"before\" : \"after\")\n  }\n\n  function moveVisually(cm, line, start, dir) {\n    var bidi = getOrder(line, cm.doc.direction);\n    if (!bidi) { return moveLogically(line, start, dir) }\n    if (start.ch >= line.text.length) {\n      start.ch = line.text.length;\n      start.sticky = \"before\";\n    } else if (start.ch <= 0) {\n      start.ch = 0;\n      start.sticky = \"after\";\n    }\n    var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];\n    if (cm.doc.direction == \"ltr\" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {\n      // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,\n      // nothing interesting happens.\n      return moveLogically(line, start, dir)\n    }\n\n    var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };\n    var prep;\n    var getWrappedLineExtent = function (ch) {\n      if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }\n      prep = prep || prepareMeasureForLine(cm, line);\n      return wrappedLineExtentChar(cm, line, prep, ch)\n    };\n    var wrappedLineExtent = getWrappedLineExtent(start.sticky == \"before\" ? mv(start, -1) : start.ch);\n\n    if (cm.doc.direction == \"rtl\" || part.level == 1) {\n      var moveInStorageOrder = (part.level == 1) == (dir < 0);\n      var ch = mv(start, moveInStorageOrder ? 1 : -1);\n      if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {\n        // Case 2: We move within an rtl part or in an rtl editor on the same visual line\n        var sticky = moveInStorageOrder ? \"before\" : \"after\";\n        return new Pos(start.line, ch, sticky)\n      }\n    }\n\n    // Case 3: Could not move within this bidi part in this visual line, so leave\n    // the current bidi part\n\n    var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {\n      var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder\n        ? new Pos(start.line, mv(ch, 1), \"before\")\n        : new Pos(start.line, ch, \"after\"); };\n\n      for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {\n        var part = bidi[partPos];\n        var moveInStorageOrder = (dir > 0) == (part.level != 1);\n        var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);\n        if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }\n        ch = moveInStorageOrder ? part.from : mv(part.to, -1);\n        if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }\n      }\n    };\n\n    // Case 3a: Look for other bidi parts on the same visual line\n    var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);\n    if (res) { return res }\n\n    // Case 3b: Look for other bidi parts on the next visual line\n    var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);\n    if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {\n      res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));\n      if (res) { return res }\n    }\n\n    // Case 4: Nowhere to move\n    return null\n  }\n\n  // Commands are parameter-less actions that can be performed on an\n  // editor, mostly used for keybindings.\n  var commands = {\n    selectAll: selectAll,\n    singleSelection: function (cm) { return cm.setSelection(cm.getCursor(\"anchor\"), cm.getCursor(\"head\"), sel_dontScroll); },\n    killLine: function (cm) { return deleteNearSelection(cm, function (range) {\n      if (range.empty()) {\n        var len = getLine(cm.doc, range.head.line).text.length;\n        if (range.head.ch == len && range.head.line < cm.lastLine())\n          { return {from: range.head, to: Pos(range.head.line + 1, 0)} }\n        else\n          { return {from: range.head, to: Pos(range.head.line, len)} }\n      } else {\n        return {from: range.from(), to: range.to()}\n      }\n    }); },\n    deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({\n      from: Pos(range.from().line, 0),\n      to: clipPos(cm.doc, Pos(range.to().line + 1, 0))\n    }); }); },\n    delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({\n      from: Pos(range.from().line, 0), to: range.from()\n    }); }); },\n    delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {\n      var top = cm.charCoords(range.head, \"div\").top + 5;\n      var leftPos = cm.coordsChar({left: 0, top: top}, \"div\");\n      return {from: leftPos, to: range.from()}\n    }); },\n    delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {\n      var top = cm.charCoords(range.head, \"div\").top + 5;\n      var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, \"div\");\n      return {from: range.from(), to: rightPos }\n    }); },\n    undo: function (cm) { return cm.undo(); },\n    redo: function (cm) { return cm.redo(); },\n    undoSelection: function (cm) { return cm.undoSelection(); },\n    redoSelection: function (cm) { return cm.redoSelection(); },\n    goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },\n    goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },\n    goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },\n      {origin: \"+move\", bias: 1}\n    ); },\n    goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },\n      {origin: \"+move\", bias: 1}\n    ); },\n    goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },\n      {origin: \"+move\", bias: -1}\n    ); },\n    goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {\n      var top = cm.cursorCoords(range.head, \"div\").top + 5;\n      return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, \"div\")\n    }, sel_move); },\n    goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {\n      var top = cm.cursorCoords(range.head, \"div\").top + 5;\n      return cm.coordsChar({left: 0, top: top}, \"div\")\n    }, sel_move); },\n    goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {\n      var top = cm.cursorCoords(range.head, \"div\").top + 5;\n      var pos = cm.coordsChar({left: 0, top: top}, \"div\");\n      if (pos.ch < cm.getLine(pos.line).search(/\\S/)) { return lineStartSmart(cm, range.head) }\n      return pos\n    }, sel_move); },\n    goLineUp: function (cm) { return cm.moveV(-1, \"line\"); },\n    goLineDown: function (cm) { return cm.moveV(1, \"line\"); },\n    goPageUp: function (cm) { return cm.moveV(-1, \"page\"); },\n    goPageDown: function (cm) { return cm.moveV(1, \"page\"); },\n    goCharLeft: function (cm) { return cm.moveH(-1, \"char\"); },\n    goCharRight: function (cm) { return cm.moveH(1, \"char\"); },\n    goColumnLeft: function (cm) { return cm.moveH(-1, \"column\"); },\n    goColumnRight: function (cm) { return cm.moveH(1, \"column\"); },\n    goWordLeft: function (cm) { return cm.moveH(-1, \"word\"); },\n    goGroupRight: function (cm) { return cm.moveH(1, \"group\"); },\n    goGroupLeft: function (cm) { return cm.moveH(-1, \"group\"); },\n    goWordRight: function (cm) { return cm.moveH(1, \"word\"); },\n    delCharBefore: function (cm) { return cm.deleteH(-1, \"char\"); },\n    delCharAfter: function (cm) { return cm.deleteH(1, \"char\"); },\n    delWordBefore: function (cm) { return cm.deleteH(-1, \"word\"); },\n    delWordAfter: function (cm) { return cm.deleteH(1, \"word\"); },\n    delGroupBefore: function (cm) { return cm.deleteH(-1, \"group\"); },\n    delGroupAfter: function (cm) { return cm.deleteH(1, \"group\"); },\n    indentAuto: function (cm) { return cm.indentSelection(\"smart\"); },\n    indentMore: function (cm) { return cm.indentSelection(\"add\"); },\n    indentLess: function (cm) { return cm.indentSelection(\"subtract\"); },\n    insertTab: function (cm) { return cm.replaceSelection(\"\\t\"); },\n    insertSoftTab: function (cm) {\n      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;\n      for (var i = 0; i < ranges.length; i++) {\n        var pos = ranges[i].from();\n        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);\n        spaces.push(spaceStr(tabSize - col % tabSize));\n      }\n      cm.replaceSelections(spaces);\n    },\n    defaultTab: function (cm) {\n      if (cm.somethingSelected()) { cm.indentSelection(\"add\"); }\n      else { cm.execCommand(\"insertTab\"); }\n    },\n    // Swap the two chars left and right of each selection's head.\n    // Move cursor behind the two swapped characters afterwards.\n    //\n    // Doesn't consider line feeds a character.\n    // Doesn't scan more than one line above to find a character.\n    // Doesn't do anything on an empty line.\n    // Doesn't do anything with non-empty selections.\n    transposeChars: function (cm) { return runInOp(cm, function () {\n      var ranges = cm.listSelections(), newSel = [];\n      for (var i = 0; i < ranges.length; i++) {\n        if (!ranges[i].empty()) { continue }\n        var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;\n        if (line) {\n          if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }\n          if (cur.ch > 0) {\n            cur = new Pos(cur.line, cur.ch + 1);\n            cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),\n                            Pos(cur.line, cur.ch - 2), cur, \"+transpose\");\n          } else if (cur.line > cm.doc.first) {\n            var prev = getLine(cm.doc, cur.line - 1).text;\n            if (prev) {\n              cur = new Pos(cur.line, 1);\n              cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +\n                              prev.charAt(prev.length - 1),\n                              Pos(cur.line - 1, prev.length - 1), cur, \"+transpose\");\n            }\n          }\n        }\n        newSel.push(new Range(cur, cur));\n      }\n      cm.setSelections(newSel);\n    }); },\n    newlineAndIndent: function (cm) { return runInOp(cm, function () {\n      var sels = cm.listSelections();\n      for (var i = sels.length - 1; i >= 0; i--)\n        { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, \"+input\"); }\n      sels = cm.listSelections();\n      for (var i$1 = 0; i$1 < sels.length; i$1++)\n        { cm.indentLine(sels[i$1].from().line, null, true); }\n      ensureCursorVisible(cm);\n    }); },\n    openLine: function (cm) { return cm.replaceSelection(\"\\n\", \"start\"); },\n    toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }\n  };\n\n\n  function lineStart(cm, lineN) {\n    var line = getLine(cm.doc, lineN);\n    var visual = visualLine(line);\n    if (visual != line) { lineN = lineNo(visual); }\n    return endOfLine(true, cm, visual, lineN, 1)\n  }\n  function lineEnd(cm, lineN) {\n    var line = getLine(cm.doc, lineN);\n    var visual = visualLineEnd(line);\n    if (visual != line) { lineN = lineNo(visual); }\n    return endOfLine(true, cm, line, lineN, -1)\n  }\n  function lineStartSmart(cm, pos) {\n    var start = lineStart(cm, pos.line);\n    var line = getLine(cm.doc, start.line);\n    var order = getOrder(line, cm.doc.direction);\n    if (!order || order[0].level == 0) {\n      var firstNonWS = Math.max(0, line.text.search(/\\S/));\n      var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;\n      return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)\n    }\n    return start\n  }\n\n  // Run a handler that was bound to a key.\n  function doHandleBinding(cm, bound, dropShift) {\n    if (typeof bound == \"string\") {\n      bound = commands[bound];\n      if (!bound) { return false }\n    }\n    // Ensure previous input has been read, so that the handler sees a\n    // consistent view of the document\n    cm.display.input.ensurePolled();\n    var prevShift = cm.display.shift, done = false;\n    try {\n      if (cm.isReadOnly()) { cm.state.suppressEdits = true; }\n      if (dropShift) { cm.display.shift = false; }\n      done = bound(cm) != Pass;\n    } finally {\n      cm.display.shift = prevShift;\n      cm.state.suppressEdits = false;\n    }\n    return done\n  }\n\n  function lookupKeyForEditor(cm, name, handle) {\n    for (var i = 0; i < cm.state.keyMaps.length; i++) {\n      var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);\n      if (result) { return result }\n    }\n    return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))\n      || lookupKey(name, cm.options.keyMap, handle, cm)\n  }\n\n  // Note that, despite the name, this function is also used to check\n  // for bound mouse clicks.\n\n  var stopSeq = new Delayed;\n\n  function dispatchKey(cm, name, e, handle) {\n    var seq = cm.state.keySeq;\n    if (seq) {\n      if (isModifierKey(name)) { return \"handled\" }\n      if (/\\'$/.test(name))\n        { cm.state.keySeq = null; }\n      else\n        { stopSeq.set(50, function () {\n          if (cm.state.keySeq == seq) {\n            cm.state.keySeq = null;\n            cm.display.input.reset();\n          }\n        }); }\n      if (dispatchKeyInner(cm, seq + \" \" + name, e, handle)) { return true }\n    }\n    return dispatchKeyInner(cm, name, e, handle)\n  }\n\n  function dispatchKeyInner(cm, name, e, handle) {\n    var result = lookupKeyForEditor(cm, name, handle);\n\n    if (result == \"multi\")\n      { cm.state.keySeq = name; }\n    if (result == \"handled\")\n      { signalLater(cm, \"keyHandled\", cm, name, e); }\n\n    if (result == \"handled\" || result == \"multi\") {\n      e_preventDefault(e);\n      restartBlink(cm);\n    }\n\n    return !!result\n  }\n\n  // Handle a key from the keydown event.\n  function handleKeyBinding(cm, e) {\n    var name = keyName(e, true);\n    if (!name) { return false }\n\n    if (e.shiftKey && !cm.state.keySeq) {\n      // First try to resolve full name (including 'Shift-'). Failing\n      // that, see if there is a cursor-motion command (starting with\n      // 'go') bound to the keyname without 'Shift-'.\n      return dispatchKey(cm, \"Shift-\" + name, e, function (b) { return doHandleBinding(cm, b, true); })\n          || dispatchKey(cm, name, e, function (b) {\n               if (typeof b == \"string\" ? /^go[A-Z]/.test(b) : b.motion)\n                 { return doHandleBinding(cm, b) }\n             })\n    } else {\n      return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })\n    }\n  }\n\n  // Handle a key from the keypress event\n  function handleCharBinding(cm, e, ch) {\n    return dispatchKey(cm, \"'\" + ch + \"'\", e, function (b) { return doHandleBinding(cm, b, true); })\n  }\n\n  var lastStoppedKey = null;\n  function onKeyDown(e) {\n    var cm = this;\n    cm.curOp.focus = activeElt();\n    if (signalDOMEvent(cm, e)) { return }\n    // IE does strange things with escape.\n    if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }\n    var code = e.keyCode;\n    cm.display.shift = code == 16 || e.shiftKey;\n    var handled = handleKeyBinding(cm, e);\n    if (presto) {\n      lastStoppedKey = handled ? code : null;\n      // Opera has no cut event... we try to at least catch the key combo\n      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))\n        { cm.replaceSelection(\"\", null, \"cut\"); }\n    }\n\n    // Turn mouse into crosshair when Alt is held on Mac.\n    if (code == 18 && !/\\bCodeMirror-crosshair\\b/.test(cm.display.lineDiv.className))\n      { showCrossHair(cm); }\n  }\n\n  function showCrossHair(cm) {\n    var lineDiv = cm.display.lineDiv;\n    addClass(lineDiv, \"CodeMirror-crosshair\");\n\n    function up(e) {\n      if (e.keyCode == 18 || !e.altKey) {\n        rmClass(lineDiv, \"CodeMirror-crosshair\");\n        off(document, \"keyup\", up);\n        off(document, \"mouseover\", up);\n      }\n    }\n    on(document, \"keyup\", up);\n    on(document, \"mouseover\", up);\n  }\n\n  function onKeyUp(e) {\n    if (e.keyCode == 16) { this.doc.sel.shift = false; }\n    signalDOMEvent(this, e);\n  }\n\n  function onKeyPress(e) {\n    var cm = this;\n    if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }\n    var keyCode = e.keyCode, charCode = e.charCode;\n    if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}\n    if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }\n    var ch = String.fromCharCode(charCode == null ? keyCode : charCode);\n    // Some browsers fire keypress events for backspace\n    if (ch == \"\\x08\") { return }\n    if (handleCharBinding(cm, e, ch)) { return }\n    cm.display.input.onKeyPress(e);\n  }\n\n  var DOUBLECLICK_DELAY = 400;\n\n  var PastClick = function(time, pos, button) {\n    this.time = time;\n    this.pos = pos;\n    this.button = button;\n  };\n\n  PastClick.prototype.compare = function (time, pos, button) {\n    return this.time + DOUBLECLICK_DELAY > time &&\n      cmp(pos, this.pos) == 0 && button == this.button\n  };\n\n  var lastClick, lastDoubleClick;\n  function clickRepeat(pos, button) {\n    var now = +new Date;\n    if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {\n      lastClick = lastDoubleClick = null;\n      return \"triple\"\n    } else if (lastClick && lastClick.compare(now, pos, button)) {\n      lastDoubleClick = new PastClick(now, pos, button);\n      lastClick = null;\n      return \"double\"\n    } else {\n      lastClick = new PastClick(now, pos, button);\n      lastDoubleClick = null;\n      return \"single\"\n    }\n  }\n\n  // A mouse down can be a single click, double click, triple click,\n  // start of selection drag, start of text drag, new cursor\n  // (ctrl-click), rectangle drag (alt-drag), or xwin\n  // middle-click-paste. Or it might be a click on something we should\n  // not interfere with, such as a scrollbar or widget.\n  function onMouseDown(e) {\n    var cm = this, display = cm.display;\n    if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }\n    display.input.ensurePolled();\n    display.shift = e.shiftKey;\n\n    if (eventInWidget(display, e)) {\n      if (!webkit) {\n        // Briefly turn off draggability, to allow widgets to do\n        // normal dragging things.\n        display.scroller.draggable = false;\n        setTimeout(function () { return display.scroller.draggable = true; }, 100);\n      }\n      return\n    }\n    if (clickInGutter(cm, e)) { return }\n    var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : \"single\";\n    window.focus();\n\n    // #3261: make sure, that we're not starting a second selection\n    if (button == 1 && cm.state.selectingText)\n      { cm.state.selectingText(e); }\n\n    if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }\n\n    if (button == 1) {\n      if (pos) { leftButtonDown(cm, pos, repeat, e); }\n      else if (e_target(e) == display.scroller) { e_preventDefault(e); }\n    } else if (button == 2) {\n      if (pos) { extendSelection(cm.doc, pos); }\n      setTimeout(function () { return display.input.focus(); }, 20);\n    } else if (button == 3) {\n      if (captureRightClick) { cm.display.input.onContextMenu(e); }\n      else { delayBlurEvent(cm); }\n    }\n  }\n\n  function handleMappedButton(cm, button, pos, repeat, event) {\n    var name = \"Click\";\n    if (repeat == \"double\") { name = \"Double\" + name; }\n    else if (repeat == \"triple\") { name = \"Triple\" + name; }\n    name = (button == 1 ? \"Left\" : button == 2 ? \"Middle\" : \"Right\") + name;\n\n    return dispatchKey(cm,  addModifierNames(name, event), event, function (bound) {\n      if (typeof bound == \"string\") { bound = commands[bound]; }\n      if (!bound) { return false }\n      var done = false;\n      try {\n        if (cm.isReadOnly()) { cm.state.suppressEdits = true; }\n        done = bound(cm, pos) != Pass;\n      } finally {\n        cm.state.suppressEdits = false;\n      }\n      return done\n    })\n  }\n\n  function configureMouse(cm, repeat, event) {\n    var option = cm.getOption(\"configureMouse\");\n    var value = option ? option(cm, repeat, event) : {};\n    if (value.unit == null) {\n      var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;\n      value.unit = rect ? \"rectangle\" : repeat == \"single\" ? \"char\" : repeat == \"double\" ? \"word\" : \"line\";\n    }\n    if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }\n    if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }\n    if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }\n    return value\n  }\n\n  function leftButtonDown(cm, pos, repeat, event) {\n    if (ie) { setTimeout(bind(ensureFocus, cm), 0); }\n    else { cm.curOp.focus = activeElt(); }\n\n    var behavior = configureMouse(cm, repeat, event);\n\n    var sel = cm.doc.sel, contained;\n    if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&\n        repeat == \"single\" && (contained = sel.contains(pos)) > -1 &&\n        (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&\n        (cmp(contained.to(), pos) > 0 || pos.xRel < 0))\n      { leftButtonStartDrag(cm, event, pos, behavior); }\n    else\n      { leftButtonSelect(cm, event, pos, behavior); }\n  }\n\n  // Start a text drag. When it ends, see if any dragging actually\n  // happen, and treat as a click if it didn't.\n  function leftButtonStartDrag(cm, event, pos, behavior) {\n    var display = cm.display, moved = false;\n    var dragEnd = operation(cm, function (e) {\n      if (webkit) { display.scroller.draggable = false; }\n      cm.state.draggingText = false;\n      off(display.wrapper.ownerDocument, \"mouseup\", dragEnd);\n      off(display.wrapper.ownerDocument, \"mousemove\", mouseMove);\n      off(display.scroller, \"dragstart\", dragStart);\n      off(display.scroller, \"drop\", dragEnd);\n      if (!moved) {\n        e_preventDefault(e);\n        if (!behavior.addNew)\n          { extendSelection(cm.doc, pos, null, null, behavior.extend); }\n        // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)\n        if (webkit || ie && ie_version == 9)\n          { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); }\n        else\n          { display.input.focus(); }\n      }\n    });\n    var mouseMove = function(e2) {\n      moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;\n    };\n    var dragStart = function () { return moved = true; };\n    // Let the drag handler handle this.\n    if (webkit) { display.scroller.draggable = true; }\n    cm.state.draggingText = dragEnd;\n    dragEnd.copy = !behavior.moveOnDrag;\n    // IE's approach to draggable\n    if (display.scroller.dragDrop) { display.scroller.dragDrop(); }\n    on(display.wrapper.ownerDocument, \"mouseup\", dragEnd);\n    on(display.wrapper.ownerDocument, \"mousemove\", mouseMove);\n    on(display.scroller, \"dragstart\", dragStart);\n    on(display.scroller, \"drop\", dragEnd);\n\n    delayBlurEvent(cm);\n    setTimeout(function () { return display.input.focus(); }, 20);\n  }\n\n  function rangeForUnit(cm, pos, unit) {\n    if (unit == \"char\") { return new Range(pos, pos) }\n    if (unit == \"word\") { return cm.findWordAt(pos) }\n    if (unit == \"line\") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }\n    var result = unit(cm, pos);\n    return new Range(result.from, result.to)\n  }\n\n  // Normal selection, as opposed to text dragging.\n  function leftButtonSelect(cm, event, start, behavior) {\n    var display = cm.display, doc = cm.doc;\n    e_preventDefault(event);\n\n    var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;\n    if (behavior.addNew && !behavior.extend) {\n      ourIndex = doc.sel.contains(start);\n      if (ourIndex > -1)\n        { ourRange = ranges[ourIndex]; }\n      else\n        { ourRange = new Range(start, start); }\n    } else {\n      ourRange = doc.sel.primary();\n      ourIndex = doc.sel.primIndex;\n    }\n\n    if (behavior.unit == \"rectangle\") {\n      if (!behavior.addNew) { ourRange = new Range(start, start); }\n      start = posFromMouse(cm, event, true, true);\n      ourIndex = -1;\n    } else {\n      var range$$1 = rangeForUnit(cm, start, behavior.unit);\n      if (behavior.extend)\n        { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); }\n      else\n        { ourRange = range$$1; }\n    }\n\n    if (!behavior.addNew) {\n      ourIndex = 0;\n      setSelection(doc, new Selection([ourRange], 0), sel_mouse);\n      startSel = doc.sel;\n    } else if (ourIndex == -1) {\n      ourIndex = ranges.length;\n      setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),\n                   {scroll: false, origin: \"*mouse\"});\n    } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == \"char\" && !behavior.extend) {\n      setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),\n                   {scroll: false, origin: \"*mouse\"});\n      startSel = doc.sel;\n    } else {\n      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);\n    }\n\n    var lastPos = start;\n    function extendTo(pos) {\n      if (cmp(lastPos, pos) == 0) { return }\n      lastPos = pos;\n\n      if (behavior.unit == \"rectangle\") {\n        var ranges = [], tabSize = cm.options.tabSize;\n        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);\n        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);\n        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);\n        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));\n             line <= end; line++) {\n          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);\n          if (left == right)\n            { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }\n          else if (text.length > leftPos)\n            { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }\n        }\n        if (!ranges.length) { ranges.push(new Range(start, start)); }\n        setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),\n                     {origin: \"*mouse\", scroll: false});\n        cm.scrollIntoView(pos);\n      } else {\n        var oldRange = ourRange;\n        var range$$1 = rangeForUnit(cm, pos, behavior.unit);\n        var anchor = oldRange.anchor, head;\n        if (cmp(range$$1.anchor, anchor) > 0) {\n          head = range$$1.head;\n          anchor = minPos(oldRange.from(), range$$1.anchor);\n        } else {\n          head = range$$1.anchor;\n          anchor = maxPos(oldRange.to(), range$$1.head);\n        }\n        var ranges$1 = startSel.ranges.slice(0);\n        ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));\n        setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);\n      }\n    }\n\n    var editorSize = display.wrapper.getBoundingClientRect();\n    // Used to ensure timeout re-tries don't fire when another extend\n    // happened in the meantime (clearTimeout isn't reliable -- at\n    // least on Chrome, the timeouts still happen even when cleared,\n    // if the clear happens after their scheduled firing time).\n    var counter = 0;\n\n    function extend(e) {\n      var curCount = ++counter;\n      var cur = posFromMouse(cm, e, true, behavior.unit == \"rectangle\");\n      if (!cur) { return }\n      if (cmp(cur, lastPos) != 0) {\n        cm.curOp.focus = activeElt();\n        extendTo(cur);\n        var visible = visibleLines(display, doc);\n        if (cur.line >= visible.to || cur.line < visible.from)\n          { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }\n      } else {\n        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;\n        if (outside) { setTimeout(operation(cm, function () {\n          if (counter != curCount) { return }\n          display.scroller.scrollTop += outside;\n          extend(e);\n        }), 50); }\n      }\n    }\n\n    function done(e) {\n      cm.state.selectingText = false;\n      counter = Infinity;\n      e_preventDefault(e);\n      display.input.focus();\n      off(display.wrapper.ownerDocument, \"mousemove\", move);\n      off(display.wrapper.ownerDocument, \"mouseup\", up);\n      doc.history.lastSelOrigin = null;\n    }\n\n    var move = operation(cm, function (e) {\n      if (e.buttons === 0 || !e_button(e)) { done(e); }\n      else { extend(e); }\n    });\n    var up = operation(cm, done);\n    cm.state.selectingText = up;\n    on(display.wrapper.ownerDocument, \"mousemove\", move);\n    on(display.wrapper.ownerDocument, \"mouseup\", up);\n  }\n\n  // Used when mouse-selecting to adjust the anchor to the proper side\n  // of a bidi jump depending on the visual position of the head.\n  function bidiSimplify(cm, range$$1) {\n    var anchor = range$$1.anchor;\n    var head = range$$1.head;\n    var anchorLine = getLine(cm.doc, anchor.line);\n    if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 }\n    var order = getOrder(anchorLine);\n    if (!order) { return range$$1 }\n    var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];\n    if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 }\n    var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);\n    if (boundary == 0 || boundary == order.length) { return range$$1 }\n\n    // Compute the relative visual position of the head compared to the\n    // anchor (<0 is to the left, >0 to the right)\n    var leftSide;\n    if (head.line != anchor.line) {\n      leftSide = (head.line - anchor.line) * (cm.doc.direction == \"ltr\" ? 1 : -1) > 0;\n    } else {\n      var headIndex = getBidiPartAt(order, head.ch, head.sticky);\n      var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);\n      if (headIndex == boundary - 1 || headIndex == boundary)\n        { leftSide = dir < 0; }\n      else\n        { leftSide = dir > 0; }\n    }\n\n    var usePart = order[boundary + (leftSide ? -1 : 0)];\n    var from = leftSide == (usePart.level == 1);\n    var ch = from ? usePart.from : usePart.to, sticky = from ? \"after\" : \"before\";\n    return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head)\n  }\n\n\n  // Determines whether an event happened in the gutter, and fires the\n  // handlers for the corresponding event.\n  function gutterEvent(cm, e, type, prevent) {\n    var mX, mY;\n    if (e.touches) {\n      mX = e.touches[0].clientX;\n      mY = e.touches[0].clientY;\n    } else {\n      try { mX = e.clientX; mY = e.clientY; }\n      catch(e) { return false }\n    }\n    if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }\n    if (prevent) { e_preventDefault(e); }\n\n    var display = cm.display;\n    var lineBox = display.lineDiv.getBoundingClientRect();\n\n    if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }\n    mY -= lineBox.top - display.viewOffset;\n\n    for (var i = 0; i < cm.options.gutters.length; ++i) {\n      var g = display.gutters.childNodes[i];\n      if (g && g.getBoundingClientRect().right >= mX) {\n        var line = lineAtHeight(cm.doc, mY);\n        var gutter = cm.options.gutters[i];\n        signal(cm, type, cm, line, gutter, e);\n        return e_defaultPrevented(e)\n      }\n    }\n  }\n\n  function clickInGutter(cm, e) {\n    return gutterEvent(cm, e, \"gutterClick\", true)\n  }\n\n  // CONTEXT MENU HANDLING\n\n  // To make the context menu work, we need to briefly unhide the\n  // textarea (making it as unobtrusive as possible) to let the\n  // right-click take effect on it.\n  function onContextMenu(cm, e) {\n    if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }\n    if (signalDOMEvent(cm, e, \"contextmenu\")) { return }\n    if (!captureRightClick) { cm.display.input.onContextMenu(e); }\n  }\n\n  function contextMenuInGutter(cm, e) {\n    if (!hasHandler(cm, \"gutterContextMenu\")) { return false }\n    return gutterEvent(cm, e, \"gutterContextMenu\", false)\n  }\n\n  function themeChanged(cm) {\n    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\\s*cm-s-\\S+/g, \"\") +\n      cm.options.theme.replace(/(^|\\s)\\s*/g, \" cm-s-\");\n    clearCaches(cm);\n  }\n\n  var Init = {toString: function(){return \"CodeMirror.Init\"}};\n\n  var defaults = {};\n  var optionHandlers = {};\n\n  function defineOptions(CodeMirror) {\n    var optionHandlers = CodeMirror.optionHandlers;\n\n    function option(name, deflt, handle, notOnInit) {\n      CodeMirror.defaults[name] = deflt;\n      if (handle) { optionHandlers[name] =\n        notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }\n    }\n\n    CodeMirror.defineOption = option;\n\n    // Passed to option handlers when there is no old value.\n    CodeMirror.Init = Init;\n\n    // These two are, on init, called from the constructor because they\n    // have to be initialized before the editor can start at all.\n    option(\"value\", \"\", function (cm, val) { return cm.setValue(val); }, true);\n    option(\"mode\", null, function (cm, val) {\n      cm.doc.modeOption = val;\n      loadMode(cm);\n    }, true);\n\n    option(\"indentUnit\", 2, loadMode, true);\n    option(\"indentWithTabs\", false);\n    option(\"smartIndent\", true);\n    option(\"tabSize\", 4, function (cm) {\n      resetModeState(cm);\n      clearCaches(cm);\n      regChange(cm);\n    }, true);\n\n    option(\"lineSeparator\", null, function (cm, val) {\n      cm.doc.lineSep = val;\n      if (!val) { return }\n      var newBreaks = [], lineNo = cm.doc.first;\n      cm.doc.iter(function (line) {\n        for (var pos = 0;;) {\n          var found = line.text.indexOf(val, pos);\n          if (found == -1) { break }\n          pos = found + val.length;\n          newBreaks.push(Pos(lineNo, found));\n        }\n        lineNo++;\n      });\n      for (var i = newBreaks.length - 1; i >= 0; i--)\n        { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }\n    });\n    option(\"specialChars\", /[\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u061c\\u200b-\\u200f\\u2028\\u2029\\ufeff]/g, function (cm, val, old) {\n      cm.state.specialChars = new RegExp(val.source + (val.test(\"\\t\") ? \"\" : \"|\\t\"), \"g\");\n      if (old != Init) { cm.refresh(); }\n    });\n    option(\"specialCharPlaceholder\", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);\n    option(\"electricChars\", true);\n    option(\"inputStyle\", mobile ? \"contenteditable\" : \"textarea\", function () {\n      throw new Error(\"inputStyle can not (yet) be changed in a running editor\") // FIXME\n    }, true);\n    option(\"spellcheck\", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);\n    option(\"rtlMoveVisually\", !windows);\n    option(\"wholeLineUpdateBefore\", true);\n\n    option(\"theme\", \"default\", function (cm) {\n      themeChanged(cm);\n      guttersChanged(cm);\n    }, true);\n    option(\"keyMap\", \"default\", function (cm, val, old) {\n      var next = getKeyMap(val);\n      var prev = old != Init && getKeyMap(old);\n      if (prev && prev.detach) { prev.detach(cm, next); }\n      if (next.attach) { next.attach(cm, prev || null); }\n    });\n    option(\"extraKeys\", null);\n    option(\"configureMouse\", null);\n\n    option(\"lineWrapping\", false, wrappingChanged, true);\n    option(\"gutters\", [], function (cm) {\n      setGuttersForLineNumbers(cm.options);\n      guttersChanged(cm);\n    }, true);\n    option(\"fixedGutter\", true, function (cm, val) {\n      cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + \"px\" : \"0\";\n      cm.refresh();\n    }, true);\n    option(\"coverGutterNextToScrollbar\", false, function (cm) { return updateScrollbars(cm); }, true);\n    option(\"scrollbarStyle\", \"native\", function (cm) {\n      initScrollbars(cm);\n      updateScrollbars(cm);\n      cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);\n      cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);\n    }, true);\n    option(\"lineNumbers\", false, function (cm) {\n      setGuttersForLineNumbers(cm.options);\n      guttersChanged(cm);\n    }, true);\n    option(\"firstLineNumber\", 1, guttersChanged, true);\n    option(\"lineNumberFormatter\", function (integer) { return integer; }, guttersChanged, true);\n    option(\"showCursorWhenSelecting\", false, updateSelection, true);\n\n    option(\"resetSelectionOnContextMenu\", true);\n    option(\"lineWiseCopyCut\", true);\n    option(\"pasteLinesPerSelection\", true);\n    option(\"selectionsMayTouch\", false);\n\n    option(\"readOnly\", false, function (cm, val) {\n      if (val == \"nocursor\") {\n        onBlur(cm);\n        cm.display.input.blur();\n      }\n      cm.display.input.readOnlyChanged(val);\n    });\n    option(\"disableInput\", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);\n    option(\"dragDrop\", true, dragDropChanged);\n    option(\"allowDropFileTypes\", null);\n\n    option(\"cursorBlinkRate\", 530);\n    option(\"cursorScrollMargin\", 0);\n    option(\"cursorHeight\", 1, updateSelection, true);\n    option(\"singleCursorHeightPerLine\", true, updateSelection, true);\n    option(\"workTime\", 100);\n    option(\"workDelay\", 100);\n    option(\"flattenSpans\", true, resetModeState, true);\n    option(\"addModeClass\", false, resetModeState, true);\n    option(\"pollInterval\", 100);\n    option(\"undoDepth\", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });\n    option(\"historyEventDelay\", 1250);\n    option(\"viewportMargin\", 10, function (cm) { return cm.refresh(); }, true);\n    option(\"maxHighlightLength\", 10000, resetModeState, true);\n    option(\"moveInputWithCursor\", true, function (cm, val) {\n      if (!val) { cm.display.input.resetPosition(); }\n    });\n\n    option(\"tabindex\", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || \"\"; });\n    option(\"autofocus\", null);\n    option(\"direction\", \"ltr\", function (cm, val) { return cm.doc.setDirection(val); }, true);\n    option(\"phrases\", null);\n  }\n\n  function guttersChanged(cm) {\n    updateGutters(cm);\n    regChange(cm);\n    alignHorizontally(cm);\n  }\n\n  function dragDropChanged(cm, value, old) {\n    var wasOn = old && old != Init;\n    if (!value != !wasOn) {\n      var funcs = cm.display.dragFunctions;\n      var toggle = value ? on : off;\n      toggle(cm.display.scroller, \"dragstart\", funcs.start);\n      toggle(cm.display.scroller, \"dragenter\", funcs.enter);\n      toggle(cm.display.scroller, \"dragover\", funcs.over);\n      toggle(cm.display.scroller, \"dragleave\", funcs.leave);\n      toggle(cm.display.scroller, \"drop\", funcs.drop);\n    }\n  }\n\n  function wrappingChanged(cm) {\n    if (cm.options.lineWrapping) {\n      addClass(cm.display.wrapper, \"CodeMirror-wrap\");\n      cm.display.sizer.style.minWidth = \"\";\n      cm.display.sizerWidth = null;\n    } else {\n      rmClass(cm.display.wrapper, \"CodeMirror-wrap\");\n      findMaxLine(cm);\n    }\n    estimateLineHeights(cm);\n    regChange(cm);\n    clearCaches(cm);\n    setTimeout(function () { return updateScrollbars(cm); }, 100);\n  }\n\n  // A CodeMirror instance represents an editor. This is the object\n  // that user code is usually dealing with.\n\n  function CodeMirror(place, options) {\n    var this$1 = this;\n\n    if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }\n\n    this.options = options = options ? copyObj(options) : {};\n    // Determine effective options based on given values and defaults.\n    copyObj(defaults, options, false);\n    setGuttersForLineNumbers(options);\n\n    var doc = options.value;\n    if (typeof doc == \"string\") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }\n    else if (options.mode) { doc.modeOption = options.mode; }\n    this.doc = doc;\n\n    var input = new CodeMirror.inputStyles[options.inputStyle](this);\n    var display = this.display = new Display(place, doc, input);\n    display.wrapper.CodeMirror = this;\n    updateGutters(this);\n    themeChanged(this);\n    if (options.lineWrapping)\n      { this.display.wrapper.className += \" CodeMirror-wrap\"; }\n    initScrollbars(this);\n\n    this.state = {\n      keyMaps: [],  // stores maps added by addKeyMap\n      overlays: [], // highlighting overlays, as added by addOverlay\n      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info\n      overwrite: false,\n      delayingBlurEvent: false,\n      focused: false,\n      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode\n      pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll\n      selectingText: false,\n      draggingText: false,\n      highlight: new Delayed(), // stores highlight worker timeout\n      keySeq: null,  // Unfinished key sequence\n      specialChars: null\n    };\n\n    if (options.autofocus && !mobile) { display.input.focus(); }\n\n    // Override magic textarea content restore that IE sometimes does\n    // on our hidden textarea on reload\n    if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }\n\n    registerEventHandlers(this);\n    ensureGlobalHandlers();\n\n    startOperation(this);\n    this.curOp.forceUpdate = true;\n    attachDoc(this, doc);\n\n    if ((options.autofocus && !mobile) || this.hasFocus())\n      { setTimeout(bind(onFocus, this), 20); }\n    else\n      { onBlur(this); }\n\n    for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))\n      { optionHandlers[opt](this$1, options[opt], Init); } }\n    maybeUpdateLineNumberWidth(this);\n    if (options.finishInit) { options.finishInit(this); }\n    for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); }\n    endOperation(this);\n    // Suppress optimizelegibility in Webkit, since it breaks text\n    // measuring on line wrapping boundaries.\n    if (webkit && options.lineWrapping &&\n        getComputedStyle(display.lineDiv).textRendering == \"optimizelegibility\")\n      { display.lineDiv.style.textRendering = \"auto\"; }\n  }\n\n  // The default configuration options.\n  CodeMirror.defaults = defaults;\n  // Functions to run when options are changed.\n  CodeMirror.optionHandlers = optionHandlers;\n\n  // Attach the necessary event handlers when initializing the editor\n  function registerEventHandlers(cm) {\n    var d = cm.display;\n    on(d.scroller, \"mousedown\", operation(cm, onMouseDown));\n    // Older IE's will not fire a second mousedown for a double click\n    if (ie && ie_version < 11)\n      { on(d.scroller, \"dblclick\", operation(cm, function (e) {\n        if (signalDOMEvent(cm, e)) { return }\n        var pos = posFromMouse(cm, e);\n        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }\n        e_preventDefault(e);\n        var word = cm.findWordAt(pos);\n        extendSelection(cm.doc, word.anchor, word.head);\n      })); }\n    else\n      { on(d.scroller, \"dblclick\", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }\n    // Some browsers fire contextmenu *after* opening the menu, at\n    // which point we can't mess with it anymore. Context menu is\n    // handled in onMouseDown for these browsers.\n    on(d.scroller, \"contextmenu\", function (e) { return onContextMenu(cm, e); });\n\n    // Used to suppress mouse event handling when a touch happens\n    var touchFinished, prevTouch = {end: 0};\n    function finishTouch() {\n      if (d.activeTouch) {\n        touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);\n        prevTouch = d.activeTouch;\n        prevTouch.end = +new Date;\n      }\n    }\n    function isMouseLikeTouchEvent(e) {\n      if (e.touches.length != 1) { return false }\n      var touch = e.touches[0];\n      return touch.radiusX <= 1 && touch.radiusY <= 1\n    }\n    function farAway(touch, other) {\n      if (other.left == null) { return true }\n      var dx = other.left - touch.left, dy = other.top - touch.top;\n      return dx * dx + dy * dy > 20 * 20\n    }\n    on(d.scroller, \"touchstart\", function (e) {\n      if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {\n        d.input.ensurePolled();\n        clearTimeout(touchFinished);\n        var now = +new Date;\n        d.activeTouch = {start: now, moved: false,\n                         prev: now - prevTouch.end <= 300 ? prevTouch : null};\n        if (e.touches.length == 1) {\n          d.activeTouch.left = e.touches[0].pageX;\n          d.activeTouch.top = e.touches[0].pageY;\n        }\n      }\n    });\n    on(d.scroller, \"touchmove\", function () {\n      if (d.activeTouch) { d.activeTouch.moved = true; }\n    });\n    on(d.scroller, \"touchend\", function (e) {\n      var touch = d.activeTouch;\n      if (touch && !eventInWidget(d, e) && touch.left != null &&\n          !touch.moved && new Date - touch.start < 300) {\n        var pos = cm.coordsChar(d.activeTouch, \"page\"), range;\n        if (!touch.prev || farAway(touch, touch.prev)) // Single tap\n          { range = new Range(pos, pos); }\n        else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap\n          { range = cm.findWordAt(pos); }\n        else // Triple tap\n          { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }\n        cm.setSelection(range.anchor, range.head);\n        cm.focus();\n        e_preventDefault(e);\n      }\n      finishTouch();\n    });\n    on(d.scroller, \"touchcancel\", finishTouch);\n\n    // Sync scrolling between fake scrollbars and real scrollable\n    // area, ensure viewport is updated when scrolling.\n    on(d.scroller, \"scroll\", function () {\n      if (d.scroller.clientHeight) {\n        updateScrollTop(cm, d.scroller.scrollTop);\n        setScrollLeft(cm, d.scroller.scrollLeft, true);\n        signal(cm, \"scroll\", cm);\n      }\n    });\n\n    // Listen to wheel events in order to try and update the viewport on time.\n    on(d.scroller, \"mousewheel\", function (e) { return onScrollWheel(cm, e); });\n    on(d.scroller, \"DOMMouseScroll\", function (e) { return onScrollWheel(cm, e); });\n\n    // Prevent wrapper from ever scrolling\n    on(d.wrapper, \"scroll\", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });\n\n    d.dragFunctions = {\n      enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},\n      over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},\n      start: function (e) { return onDragStart(cm, e); },\n      drop: operation(cm, onDrop),\n      leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}\n    };\n\n    var inp = d.input.getField();\n    on(inp, \"keyup\", function (e) { return onKeyUp.call(cm, e); });\n    on(inp, \"keydown\", operation(cm, onKeyDown));\n    on(inp, \"keypress\", operation(cm, onKeyPress));\n    on(inp, \"focus\", function (e) { return onFocus(cm, e); });\n    on(inp, \"blur\", function (e) { return onBlur(cm, e); });\n  }\n\n  var initHooks = [];\n  CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };\n\n  // Indent the given line. The how parameter can be \"smart\",\n  // \"add\"/null, \"subtract\", or \"prev\". When aggressive is false\n  // (typically set to true for forced single-line indents), empty\n  // lines are not indented, and places where the mode returns Pass\n  // are left alone.\n  function indentLine(cm, n, how, aggressive) {\n    var doc = cm.doc, state;\n    if (how == null) { how = \"add\"; }\n    if (how == \"smart\") {\n      // Fall back to \"prev\" when the mode doesn't have an indentation\n      // method.\n      if (!doc.mode.indent) { how = \"prev\"; }\n      else { state = getContextBefore(cm, n).state; }\n    }\n\n    var tabSize = cm.options.tabSize;\n    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);\n    if (line.stateAfter) { line.stateAfter = null; }\n    var curSpaceString = line.text.match(/^\\s*/)[0], indentation;\n    if (!aggressive && !/\\S/.test(line.text)) {\n      indentation = 0;\n      how = \"not\";\n    } else if (how == \"smart\") {\n      indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);\n      if (indentation == Pass || indentation > 150) {\n        if (!aggressive) { return }\n        how = \"prev\";\n      }\n    }\n    if (how == \"prev\") {\n      if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }\n      else { indentation = 0; }\n    } else if (how == \"add\") {\n      indentation = curSpace + cm.options.indentUnit;\n    } else if (how == \"subtract\") {\n      indentation = curSpace - cm.options.indentUnit;\n    } else if (typeof how == \"number\") {\n      indentation = curSpace + how;\n    }\n    indentation = Math.max(0, indentation);\n\n    var indentString = \"\", pos = 0;\n    if (cm.options.indentWithTabs)\n      { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += \"\\t\";} }\n    if (pos < indentation) { indentString += spaceStr(indentation - pos); }\n\n    if (indentString != curSpaceString) {\n      replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), \"+input\");\n      line.stateAfter = null;\n      return true\n    } else {\n      // Ensure that, if the cursor was in the whitespace at the start\n      // of the line, it is moved to the end of that space.\n      for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {\n        var range = doc.sel.ranges[i$1];\n        if (range.head.line == n && range.head.ch < curSpaceString.length) {\n          var pos$1 = Pos(n, curSpaceString.length);\n          replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));\n          break\n        }\n      }\n    }\n  }\n\n  // This will be set to a {lineWise: bool, text: [string]} object, so\n  // that, when pasting, we know what kind of selections the copied\n  // text was made out of.\n  var lastCopied = null;\n\n  function setLastCopied(newLastCopied) {\n    lastCopied = newLastCopied;\n  }\n\n  function applyTextInput(cm, inserted, deleted, sel, origin) {\n    var doc = cm.doc;\n    cm.display.shift = false;\n    if (!sel) { sel = doc.sel; }\n\n    var paste = cm.state.pasteIncoming || origin == \"paste\";\n    var textLines = splitLinesAuto(inserted), multiPaste = null;\n    // When pasting N lines into N selections, insert one line per selection\n    if (paste && sel.ranges.length > 1) {\n      if (lastCopied && lastCopied.text.join(\"\\n\") == inserted) {\n        if (sel.ranges.length % lastCopied.text.length == 0) {\n          multiPaste = [];\n          for (var i = 0; i < lastCopied.text.length; i++)\n            { multiPaste.push(doc.splitLines(lastCopied.text[i])); }\n        }\n      } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {\n        multiPaste = map(textLines, function (l) { return [l]; });\n      }\n    }\n\n    var updateInput;\n    // Normal behavior is to insert the new text into every selection\n    for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {\n      var range$$1 = sel.ranges[i$1];\n      var from = range$$1.from(), to = range$$1.to();\n      if (range$$1.empty()) {\n        if (deleted && deleted > 0) // Handle deletion\n          { from = Pos(from.line, from.ch - deleted); }\n        else if (cm.state.overwrite && !paste) // Handle overwrite\n          { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }\n        else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join(\"\\n\") == inserted)\n          { from = to = Pos(from.line, 0); }\n      }\n      updateInput = cm.curOp.updateInput;\n      var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,\n                         origin: origin || (paste ? \"paste\" : cm.state.cutIncoming ? \"cut\" : \"+input\")};\n      makeChange(cm.doc, changeEvent);\n      signalLater(cm, \"inputRead\", cm, changeEvent);\n    }\n    if (inserted && !paste)\n      { triggerElectric(cm, inserted); }\n\n    ensureCursorVisible(cm);\n    cm.curOp.updateInput = updateInput;\n    cm.curOp.typing = true;\n    cm.state.pasteIncoming = cm.state.cutIncoming = false;\n  }\n\n  function handlePaste(e, cm) {\n    var pasted = e.clipboardData && e.clipboardData.getData(\"Text\");\n    if (pasted) {\n      e.preventDefault();\n      if (!cm.isReadOnly() && !cm.options.disableInput)\n        { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, \"paste\"); }); }\n      return true\n    }\n  }\n\n  function triggerElectric(cm, inserted) {\n    // When an 'electric' character is inserted, immediately trigger a reindent\n    if (!cm.options.electricChars || !cm.options.smartIndent) { return }\n    var sel = cm.doc.sel;\n\n    for (var i = sel.ranges.length - 1; i >= 0; i--) {\n      var range$$1 = sel.ranges[i];\n      if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }\n      var mode = cm.getModeAt(range$$1.head);\n      var indented = false;\n      if (mode.electricChars) {\n        for (var j = 0; j < mode.electricChars.length; j++)\n          { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {\n            indented = indentLine(cm, range$$1.head.line, \"smart\");\n            break\n          } }\n      } else if (mode.electricInput) {\n        if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))\n          { indented = indentLine(cm, range$$1.head.line, \"smart\"); }\n      }\n      if (indented) { signalLater(cm, \"electricInput\", cm, range$$1.head.line); }\n    }\n  }\n\n  function copyableRanges(cm) {\n    var text = [], ranges = [];\n    for (var i = 0; i < cm.doc.sel.ranges.length; i++) {\n      var line = cm.doc.sel.ranges[i].head.line;\n      var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};\n      ranges.push(lineRange);\n      text.push(cm.getRange(lineRange.anchor, lineRange.head));\n    }\n    return {text: text, ranges: ranges}\n  }\n\n  function disableBrowserMagic(field, spellcheck) {\n    field.setAttribute(\"autocorrect\", \"off\");\n    field.setAttribute(\"autocapitalize\", \"off\");\n    field.setAttribute(\"spellcheck\", !!spellcheck);\n  }\n\n  function hiddenTextarea() {\n    var te = elt(\"textarea\", null, null, \"position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none\");\n    var div = elt(\"div\", [te], null, \"overflow: hidden; position: relative; width: 3px; height: 0px;\");\n    // The textarea is kept positioned near the cursor to prevent the\n    // fact that it'll be scrolled into view on input from scrolling\n    // our fake cursor out of view. On webkit, when wrap=off, paste is\n    // very slow. So make the area wide instead.\n    if (webkit) { te.style.width = \"1000px\"; }\n    else { te.setAttribute(\"wrap\", \"off\"); }\n    // If border: 0; -- iOS fails to open keyboard (issue #1287)\n    if (ios) { te.style.border = \"1px solid black\"; }\n    disableBrowserMagic(te);\n    return div\n  }\n\n  // The publicly visible API. Note that methodOp(f) means\n  // 'wrap f in an operation, performed on its `this` parameter'.\n\n  // This is not the complete set of editor methods. Most of the\n  // methods defined on the Doc type are also injected into\n  // CodeMirror.prototype, for backwards compatibility and\n  // convenience.\n\n  function addEditorMethods(CodeMirror) {\n    var optionHandlers = CodeMirror.optionHandlers;\n\n    var helpers = CodeMirror.helpers = {};\n\n    CodeMirror.prototype = {\n      constructor: CodeMirror,\n      focus: function(){window.focus(); this.display.input.focus();},\n\n      setOption: function(option, value) {\n        var options = this.options, old = options[option];\n        if (options[option] == value && option != \"mode\") { return }\n        options[option] = value;\n        if (optionHandlers.hasOwnProperty(option))\n          { operation(this, optionHandlers[option])(this, value, old); }\n        signal(this, \"optionChange\", this, option);\n      },\n\n      getOption: function(option) {return this.options[option]},\n      getDoc: function() {return this.doc},\n\n      addKeyMap: function(map$$1, bottom) {\n        this.state.keyMaps[bottom ? \"push\" : \"unshift\"](getKeyMap(map$$1));\n      },\n      removeKeyMap: function(map$$1) {\n        var maps = this.state.keyMaps;\n        for (var i = 0; i < maps.length; ++i)\n          { if (maps[i] == map$$1 || maps[i].name == map$$1) {\n            maps.splice(i, 1);\n            return true\n          } }\n      },\n\n      addOverlay: methodOp(function(spec, options) {\n        var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);\n        if (mode.startState) { throw new Error(\"Overlays may not be stateful.\") }\n        insertSorted(this.state.overlays,\n                     {mode: mode, modeSpec: spec, opaque: options && options.opaque,\n                      priority: (options && options.priority) || 0},\n                     function (overlay) { return overlay.priority; });\n        this.state.modeGen++;\n        regChange(this);\n      }),\n      removeOverlay: methodOp(function(spec) {\n        var this$1 = this;\n\n        var overlays = this.state.overlays;\n        for (var i = 0; i < overlays.length; ++i) {\n          var cur = overlays[i].modeSpec;\n          if (cur == spec || typeof spec == \"string\" && cur.name == spec) {\n            overlays.splice(i, 1);\n            this$1.state.modeGen++;\n            regChange(this$1);\n            return\n          }\n        }\n      }),\n\n      indentLine: methodOp(function(n, dir, aggressive) {\n        if (typeof dir != \"string\" && typeof dir != \"number\") {\n          if (dir == null) { dir = this.options.smartIndent ? \"smart\" : \"prev\"; }\n          else { dir = dir ? \"add\" : \"subtract\"; }\n        }\n        if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }\n      }),\n      indentSelection: methodOp(function(how) {\n        var this$1 = this;\n\n        var ranges = this.doc.sel.ranges, end = -1;\n        for (var i = 0; i < ranges.length; i++) {\n          var range$$1 = ranges[i];\n          if (!range$$1.empty()) {\n            var from = range$$1.from(), to = range$$1.to();\n            var start = Math.max(end, from.line);\n            end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;\n            for (var j = start; j < end; ++j)\n              { indentLine(this$1, j, how); }\n            var newRanges = this$1.doc.sel.ranges;\n            if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)\n              { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }\n          } else if (range$$1.head.line > end) {\n            indentLine(this$1, range$$1.head.line, how, true);\n            end = range$$1.head.line;\n            if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); }\n          }\n        }\n      }),\n\n      // Fetch the parser token for a given character. Useful for hacks\n      // that want to inspect the mode state (say, for completion).\n      getTokenAt: function(pos, precise) {\n        return takeToken(this, pos, precise)\n      },\n\n      getLineTokens: function(line, precise) {\n        return takeToken(this, Pos(line), precise, true)\n      },\n\n      getTokenTypeAt: function(pos) {\n        pos = clipPos(this.doc, pos);\n        var styles = getLineStyles(this, getLine(this.doc, pos.line));\n        var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;\n        var type;\n        if (ch == 0) { type = styles[2]; }\n        else { for (;;) {\n          var mid = (before + after) >> 1;\n          if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }\n          else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }\n          else { type = styles[mid * 2 + 2]; break }\n        } }\n        var cut = type ? type.indexOf(\"overlay \") : -1;\n        return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)\n      },\n\n      getModeAt: function(pos) {\n        var mode = this.doc.mode;\n        if (!mode.innerMode) { return mode }\n        return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode\n      },\n\n      getHelper: function(pos, type) {\n        return this.getHelpers(pos, type)[0]\n      },\n\n      getHelpers: function(pos, type) {\n        var this$1 = this;\n\n        var found = [];\n        if (!helpers.hasOwnProperty(type)) { return found }\n        var help = helpers[type], mode = this.getModeAt(pos);\n        if (typeof mode[type] == \"string\") {\n          if (help[mode[type]]) { found.push(help[mode[type]]); }\n        } else if (mode[type]) {\n          for (var i = 0; i < mode[type].length; i++) {\n            var val = help[mode[type][i]];\n            if (val) { found.push(val); }\n          }\n        } else if (mode.helperType && help[mode.helperType]) {\n          found.push(help[mode.helperType]);\n        } else if (help[mode.name]) {\n          found.push(help[mode.name]);\n        }\n        for (var i$1 = 0; i$1 < help._global.length; i$1++) {\n          var cur = help._global[i$1];\n          if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)\n            { found.push(cur.val); }\n        }\n        return found\n      },\n\n      getStateAfter: function(line, precise) {\n        var doc = this.doc;\n        line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);\n        return getContextBefore(this, line + 1, precise).state\n      },\n\n      cursorCoords: function(start, mode) {\n        var pos, range$$1 = this.doc.sel.primary();\n        if (start == null) { pos = range$$1.head; }\n        else if (typeof start == \"object\") { pos = clipPos(this.doc, start); }\n        else { pos = start ? range$$1.from() : range$$1.to(); }\n        return cursorCoords(this, pos, mode || \"page\")\n      },\n\n      charCoords: function(pos, mode) {\n        return charCoords(this, clipPos(this.doc, pos), mode || \"page\")\n      },\n\n      coordsChar: function(coords, mode) {\n        coords = fromCoordSystem(this, coords, mode || \"page\");\n        return coordsChar(this, coords.left, coords.top)\n      },\n\n      lineAtHeight: function(height, mode) {\n        height = fromCoordSystem(this, {top: height, left: 0}, mode || \"page\").top;\n        return lineAtHeight(this.doc, height + this.display.viewOffset)\n      },\n      heightAtLine: function(line, mode, includeWidgets) {\n        var end = false, lineObj;\n        if (typeof line == \"number\") {\n          var last = this.doc.first + this.doc.size - 1;\n          if (line < this.doc.first) { line = this.doc.first; }\n          else if (line > last) { line = last; end = true; }\n          lineObj = getLine(this.doc, line);\n        } else {\n          lineObj = line;\n        }\n        return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || \"page\", includeWidgets || end).top +\n          (end ? this.doc.height - heightAtLine(lineObj) : 0)\n      },\n\n      defaultTextHeight: function() { return textHeight(this.display) },\n      defaultCharWidth: function() { return charWidth(this.display) },\n\n      getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},\n\n      addWidget: function(pos, node, scroll, vert, horiz) {\n        var display = this.display;\n        pos = cursorCoords(this, clipPos(this.doc, pos));\n        var top = pos.bottom, left = pos.left;\n        node.style.position = \"absolute\";\n        node.setAttribute(\"cm-ignore-events\", \"true\");\n        this.display.input.setUneditable(node);\n        display.sizer.appendChild(node);\n        if (vert == \"over\") {\n          top = pos.top;\n        } else if (vert == \"above\" || vert == \"near\") {\n          var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),\n          hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);\n          // Default to positioning above (if specified and possible); otherwise default to positioning below\n          if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)\n            { top = pos.top - node.offsetHeight; }\n          else if (pos.bottom + node.offsetHeight <= vspace)\n            { top = pos.bottom; }\n          if (left + node.offsetWidth > hspace)\n            { left = hspace - node.offsetWidth; }\n        }\n        node.style.top = top + \"px\";\n        node.style.left = node.style.right = \"\";\n        if (horiz == \"right\") {\n          left = display.sizer.clientWidth - node.offsetWidth;\n          node.style.right = \"0px\";\n        } else {\n          if (horiz == \"left\") { left = 0; }\n          else if (horiz == \"middle\") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }\n          node.style.left = left + \"px\";\n        }\n        if (scroll)\n          { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }\n      },\n\n      triggerOnKeyDown: methodOp(onKeyDown),\n      triggerOnKeyPress: methodOp(onKeyPress),\n      triggerOnKeyUp: onKeyUp,\n      triggerOnMouseDown: methodOp(onMouseDown),\n\n      execCommand: function(cmd) {\n        if (commands.hasOwnProperty(cmd))\n          { return commands[cmd].call(null, this) }\n      },\n\n      triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),\n\n      findPosH: function(from, amount, unit, visually) {\n        var this$1 = this;\n\n        var dir = 1;\n        if (amount < 0) { dir = -1; amount = -amount; }\n        var cur = clipPos(this.doc, from);\n        for (var i = 0; i < amount; ++i) {\n          cur = findPosH(this$1.doc, cur, dir, unit, visually);\n          if (cur.hitSide) { break }\n        }\n        return cur\n      },\n\n      moveH: methodOp(function(dir, unit) {\n        var this$1 = this;\n\n        this.extendSelectionsBy(function (range$$1) {\n          if (this$1.display.shift || this$1.doc.extend || range$$1.empty())\n            { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }\n          else\n            { return dir < 0 ? range$$1.from() : range$$1.to() }\n        }, sel_move);\n      }),\n\n      deleteH: methodOp(function(dir, unit) {\n        var sel = this.doc.sel, doc = this.doc;\n        if (sel.somethingSelected())\n          { doc.replaceSelection(\"\", null, \"+delete\"); }\n        else\n          { deleteNearSelection(this, function (range$$1) {\n            var other = findPosH(doc, range$$1.head, dir, unit, false);\n            return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}\n          }); }\n      }),\n\n      findPosV: function(from, amount, unit, goalColumn) {\n        var this$1 = this;\n\n        var dir = 1, x = goalColumn;\n        if (amount < 0) { dir = -1; amount = -amount; }\n        var cur = clipPos(this.doc, from);\n        for (var i = 0; i < amount; ++i) {\n          var coords = cursorCoords(this$1, cur, \"div\");\n          if (x == null) { x = coords.left; }\n          else { coords.left = x; }\n          cur = findPosV(this$1, coords, dir, unit);\n          if (cur.hitSide) { break }\n        }\n        return cur\n      },\n\n      moveV: methodOp(function(dir, unit) {\n        var this$1 = this;\n\n        var doc = this.doc, goals = [];\n        var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();\n        doc.extendSelectionsBy(function (range$$1) {\n          if (collapse)\n            { return dir < 0 ? range$$1.from() : range$$1.to() }\n          var headPos = cursorCoords(this$1, range$$1.head, \"div\");\n          if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; }\n          goals.push(headPos.left);\n          var pos = findPosV(this$1, headPos, dir, unit);\n          if (unit == \"page\" && range$$1 == doc.sel.primary())\n            { addToScrollTop(this$1, charCoords(this$1, pos, \"div\").top - headPos.top); }\n          return pos\n        }, sel_move);\n        if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)\n          { doc.sel.ranges[i].goalColumn = goals[i]; } }\n      }),\n\n      // Find the word at the given position (as returned by coordsChar).\n      findWordAt: function(pos) {\n        var doc = this.doc, line = getLine(doc, pos.line).text;\n        var start = pos.ch, end = pos.ch;\n        if (line) {\n          var helper = this.getHelper(pos, \"wordChars\");\n          if ((pos.sticky == \"before\" || end == line.length) && start) { --start; } else { ++end; }\n          var startChar = line.charAt(start);\n          var check = isWordChar(startChar, helper)\n            ? function (ch) { return isWordChar(ch, helper); }\n            : /\\s/.test(startChar) ? function (ch) { return /\\s/.test(ch); }\n            : function (ch) { return (!/\\s/.test(ch) && !isWordChar(ch)); };\n          while (start > 0 && check(line.charAt(start - 1))) { --start; }\n          while (end < line.length && check(line.charAt(end))) { ++end; }\n        }\n        return new Range(Pos(pos.line, start), Pos(pos.line, end))\n      },\n\n      toggleOverwrite: function(value) {\n        if (value != null && value == this.state.overwrite) { return }\n        if (this.state.overwrite = !this.state.overwrite)\n          { addClass(this.display.cursorDiv, \"CodeMirror-overwrite\"); }\n        else\n          { rmClass(this.display.cursorDiv, \"CodeMirror-overwrite\"); }\n\n        signal(this, \"overwriteToggle\", this, this.state.overwrite);\n      },\n      hasFocus: function() { return this.display.input.getField() == activeElt() },\n      isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },\n\n      scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),\n      getScrollInfo: function() {\n        var scroller = this.display.scroller;\n        return {left: scroller.scrollLeft, top: scroller.scrollTop,\n                height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,\n                width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,\n                clientHeight: displayHeight(this), clientWidth: displayWidth(this)}\n      },\n\n      scrollIntoView: methodOp(function(range$$1, margin) {\n        if (range$$1 == null) {\n          range$$1 = {from: this.doc.sel.primary().head, to: null};\n          if (margin == null) { margin = this.options.cursorScrollMargin; }\n        } else if (typeof range$$1 == \"number\") {\n          range$$1 = {from: Pos(range$$1, 0), to: null};\n        } else if (range$$1.from == null) {\n          range$$1 = {from: range$$1, to: null};\n        }\n        if (!range$$1.to) { range$$1.to = range$$1.from; }\n        range$$1.margin = margin || 0;\n\n        if (range$$1.from.line != null) {\n          scrollToRange(this, range$$1);\n        } else {\n          scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin);\n        }\n      }),\n\n      setSize: methodOp(function(width, height) {\n        var this$1 = this;\n\n        var interpret = function (val) { return typeof val == \"number\" || /^\\d+$/.test(String(val)) ? val + \"px\" : val; };\n        if (width != null) { this.display.wrapper.style.width = interpret(width); }\n        if (height != null) { this.display.wrapper.style.height = interpret(height); }\n        if (this.options.lineWrapping) { clearLineMeasurementCache(this); }\n        var lineNo$$1 = this.display.viewFrom;\n        this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {\n          if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)\n            { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, \"widget\"); break } } }\n          ++lineNo$$1;\n        });\n        this.curOp.forceUpdate = true;\n        signal(this, \"refresh\", this);\n      }),\n\n      operation: function(f){return runInOp(this, f)},\n      startOperation: function(){return startOperation(this)},\n      endOperation: function(){return endOperation(this)},\n\n      refresh: methodOp(function() {\n        var oldHeight = this.display.cachedTextHeight;\n        regChange(this);\n        this.curOp.forceUpdate = true;\n        clearCaches(this);\n        scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);\n        updateGutterSpace(this);\n        if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)\n          { estimateLineHeights(this); }\n        signal(this, \"refresh\", this);\n      }),\n\n      swapDoc: methodOp(function(doc) {\n        var old = this.doc;\n        old.cm = null;\n        attachDoc(this, doc);\n        clearCaches(this);\n        this.display.input.reset();\n        scrollToCoords(this, doc.scrollLeft, doc.scrollTop);\n        this.curOp.forceScroll = true;\n        signalLater(this, \"swapDoc\", this, old);\n        return old\n      }),\n\n      phrase: function(phraseText) {\n        var phrases = this.options.phrases;\n        return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText\n      },\n\n      getInputField: function(){return this.display.input.getField()},\n      getWrapperElement: function(){return this.display.wrapper},\n      getScrollerElement: function(){return this.display.scroller},\n      getGutterElement: function(){return this.display.gutters}\n    };\n    eventMixin(CodeMirror);\n\n    CodeMirror.registerHelper = function(type, name, value) {\n      if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }\n      helpers[type][name] = value;\n    };\n    CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {\n      CodeMirror.registerHelper(type, name, value);\n      helpers[type]._global.push({pred: predicate, val: value});\n    };\n  }\n\n  // Used for horizontal relative motion. Dir is -1 or 1 (left or\n  // right), unit can be \"char\", \"column\" (like char, but doesn't\n  // cross line boundaries), \"word\" (across next word), or \"group\" (to\n  // the start of next group of word or non-word-non-whitespace\n  // chars). The visually param controls whether, in right-to-left\n  // text, direction 1 means to move towards the next index in the\n  // string, or towards the character to the right of the current\n  // position. The resulting position will have a hitSide=true\n  // property if it reached the end of the document.\n  function findPosH(doc, pos, dir, unit, visually) {\n    var oldPos = pos;\n    var origDir = dir;\n    var lineObj = getLine(doc, pos.line);\n    function findNextLine() {\n      var l = pos.line + dir;\n      if (l < doc.first || l >= doc.first + doc.size) { return false }\n      pos = new Pos(l, pos.ch, pos.sticky);\n      return lineObj = getLine(doc, l)\n    }\n    function moveOnce(boundToLine) {\n      var next;\n      if (visually) {\n        next = moveVisually(doc.cm, lineObj, pos, dir);\n      } else {\n        next = moveLogically(lineObj, pos, dir);\n      }\n      if (next == null) {\n        if (!boundToLine && findNextLine())\n          { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); }\n        else\n          { return false }\n      } else {\n        pos = next;\n      }\n      return true\n    }\n\n    if (unit == \"char\") {\n      moveOnce();\n    } else if (unit == \"column\") {\n      moveOnce(true);\n    } else if (unit == \"word\" || unit == \"group\") {\n      var sawType = null, group = unit == \"group\";\n      var helper = doc.cm && doc.cm.getHelper(pos, \"wordChars\");\n      for (var first = true;; first = false) {\n        if (dir < 0 && !moveOnce(!first)) { break }\n        var cur = lineObj.text.charAt(pos.ch) || \"\\n\";\n        var type = isWordChar(cur, helper) ? \"w\"\n          : group && cur == \"\\n\" ? \"n\"\n          : !group || /\\s/.test(cur) ? null\n          : \"p\";\n        if (group && !first && !type) { type = \"s\"; }\n        if (sawType && sawType != type) {\n          if (dir < 0) {dir = 1; moveOnce(); pos.sticky = \"after\";}\n          break\n        }\n\n        if (type) { sawType = type; }\n        if (dir > 0 && !moveOnce(!first)) { break }\n      }\n    }\n    var result = skipAtomic(doc, pos, oldPos, origDir, true);\n    if (equalCursorPos(oldPos, result)) { result.hitSide = true; }\n    return result\n  }\n\n  // For relative vertical movement. Dir may be -1 or 1. Unit can be\n  // \"page\" or \"line\". The resulting position will have a hitSide=true\n  // property if it reached the end of the document.\n  function findPosV(cm, pos, dir, unit) {\n    var doc = cm.doc, x = pos.left, y;\n    if (unit == \"page\") {\n      var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);\n      var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);\n      y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;\n\n    } else if (unit == \"line\") {\n      y = dir > 0 ? pos.bottom + 3 : pos.top - 3;\n    }\n    var target;\n    for (;;) {\n      target = coordsChar(cm, x, y);\n      if (!target.outside) { break }\n      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }\n      y += dir * 5;\n    }\n    return target\n  }\n\n  // CONTENTEDITABLE INPUT STYLE\n\n  var ContentEditableInput = function(cm) {\n    this.cm = cm;\n    this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;\n    this.polling = new Delayed();\n    this.composing = null;\n    this.gracePeriod = false;\n    this.readDOMTimeout = null;\n  };\n\n  ContentEditableInput.prototype.init = function (display) {\n      var this$1 = this;\n\n    var input = this, cm = input.cm;\n    var div = input.div = display.lineDiv;\n    disableBrowserMagic(div, cm.options.spellcheck);\n\n    on(div, \"paste\", function (e) {\n      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }\n      // IE doesn't fire input events, so we schedule a read for the pasted content in this way\n      if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }\n    });\n\n    on(div, \"compositionstart\", function (e) {\n      this$1.composing = {data: e.data, done: false};\n    });\n    on(div, \"compositionupdate\", function (e) {\n      if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }\n    });\n    on(div, \"compositionend\", function (e) {\n      if (this$1.composing) {\n        if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }\n        this$1.composing.done = true;\n      }\n    });\n\n    on(div, \"touchstart\", function () { return input.forceCompositionEnd(); });\n\n    on(div, \"input\", function () {\n      if (!this$1.composing) { this$1.readFromDOMSoon(); }\n    });\n\n    function onCopyCut(e) {\n      if (signalDOMEvent(cm, e)) { return }\n      if (cm.somethingSelected()) {\n        setLastCopied({lineWise: false, text: cm.getSelections()});\n        if (e.type == \"cut\") { cm.replaceSelection(\"\", null, \"cut\"); }\n      } else if (!cm.options.lineWiseCopyCut) {\n        return\n      } else {\n        var ranges = copyableRanges(cm);\n        setLastCopied({lineWise: true, text: ranges.text});\n        if (e.type == \"cut\") {\n          cm.operation(function () {\n            cm.setSelections(ranges.ranges, 0, sel_dontScroll);\n            cm.replaceSelection(\"\", null, \"cut\");\n          });\n        }\n      }\n      if (e.clipboardData) {\n        e.clipboardData.clearData();\n        var content = lastCopied.text.join(\"\\n\");\n        // iOS exposes the clipboard API, but seems to discard content inserted into it\n        e.clipboardData.setData(\"Text\", content);\n        if (e.clipboardData.getData(\"Text\") == content) {\n          e.preventDefault();\n          return\n        }\n      }\n      // Old-fashioned briefly-focus-a-textarea hack\n      var kludge = hiddenTextarea(), te = kludge.firstChild;\n      cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);\n      te.value = lastCopied.text.join(\"\\n\");\n      var hadFocus = document.activeElement;\n      selectInput(te);\n      setTimeout(function () {\n        cm.display.lineSpace.removeChild(kludge);\n        hadFocus.focus();\n        if (hadFocus == div) { input.showPrimarySelection(); }\n      }, 50);\n    }\n    on(div, \"copy\", onCopyCut);\n    on(div, \"cut\", onCopyCut);\n  };\n\n  ContentEditableInput.prototype.prepareSelection = function () {\n    var result = prepareSelection(this.cm, false);\n    result.focus = this.cm.state.focused;\n    return result\n  };\n\n  ContentEditableInput.prototype.showSelection = function (info, takeFocus) {\n    if (!info || !this.cm.display.view.length) { return }\n    if (info.focus || takeFocus) { this.showPrimarySelection(); }\n    this.showMultipleSelections(info);\n  };\n\n  ContentEditableInput.prototype.getSelection = function () {\n    return this.cm.display.wrapper.ownerDocument.getSelection()\n  };\n\n  ContentEditableInput.prototype.showPrimarySelection = function () {\n    var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();\n    var from = prim.from(), to = prim.to();\n\n    if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {\n      sel.removeAllRanges();\n      return\n    }\n\n    var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);\n    var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);\n    if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&\n        cmp(minPos(curAnchor, curFocus), from) == 0 &&\n        cmp(maxPos(curAnchor, curFocus), to) == 0)\n      { return }\n\n    var view = cm.display.view;\n    var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||\n        {node: view[0].measure.map[2], offset: 0};\n    var end = to.line < cm.display.viewTo && posToDOM(cm, to);\n    if (!end) {\n      var measure = view[view.length - 1].measure;\n      var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;\n      end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]};\n    }\n\n    if (!start || !end) {\n      sel.removeAllRanges();\n      return\n    }\n\n    var old = sel.rangeCount && sel.getRangeAt(0), rng;\n    try { rng = range(start.node, start.offset, end.offset, end.node); }\n    catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible\n    if (rng) {\n      if (!gecko && cm.state.focused) {\n        sel.collapse(start.node, start.offset);\n        if (!rng.collapsed) {\n          sel.removeAllRanges();\n          sel.addRange(rng);\n        }\n      } else {\n        sel.removeAllRanges();\n        sel.addRange(rng);\n      }\n      if (old && sel.anchorNode == null) { sel.addRange(old); }\n      else if (gecko) { this.startGracePeriod(); }\n    }\n    this.rememberSelection();\n  };\n\n  ContentEditableInput.prototype.startGracePeriod = function () {\n      var this$1 = this;\n\n    clearTimeout(this.gracePeriod);\n    this.gracePeriod = setTimeout(function () {\n      this$1.gracePeriod = false;\n      if (this$1.selectionChanged())\n        { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }\n    }, 20);\n  };\n\n  ContentEditableInput.prototype.showMultipleSelections = function (info) {\n    removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);\n    removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);\n  };\n\n  ContentEditableInput.prototype.rememberSelection = function () {\n    var sel = this.getSelection();\n    this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;\n    this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;\n  };\n\n  ContentEditableInput.prototype.selectionInEditor = function () {\n    var sel = this.getSelection();\n    if (!sel.rangeCount) { return false }\n    var node = sel.getRangeAt(0).commonAncestorContainer;\n    return contains(this.div, node)\n  };\n\n  ContentEditableInput.prototype.focus = function () {\n    if (this.cm.options.readOnly != \"nocursor\") {\n      if (!this.selectionInEditor())\n        { this.showSelection(this.prepareSelection(), true); }\n      this.div.focus();\n    }\n  };\n  ContentEditableInput.prototype.blur = function () { this.div.blur(); };\n  ContentEditableInput.prototype.getField = function () { return this.div };\n\n  ContentEditableInput.prototype.supportsTouch = function () { return true };\n\n  ContentEditableInput.prototype.receivedFocus = function () {\n    var input = this;\n    if (this.selectionInEditor())\n      { this.pollSelection(); }\n    else\n      { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }\n\n    function poll() {\n      if (input.cm.state.focused) {\n        input.pollSelection();\n        input.polling.set(input.cm.options.pollInterval, poll);\n      }\n    }\n    this.polling.set(this.cm.options.pollInterval, poll);\n  };\n\n  ContentEditableInput.prototype.selectionChanged = function () {\n    var sel = this.getSelection();\n    return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||\n      sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset\n  };\n\n  ContentEditableInput.prototype.pollSelection = function () {\n    if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }\n    var sel = this.getSelection(), cm = this.cm;\n    // On Android Chrome (version 56, at least), backspacing into an\n    // uneditable block element will put the cursor in that element,\n    // and then, because it's not editable, hide the virtual keyboard.\n    // Because Android doesn't allow us to actually detect backspace\n    // presses in a sane way, this code checks for when that happens\n    // and simulates a backspace press in this case.\n    if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) {\n      this.cm.triggerOnKeyDown({type: \"keydown\", keyCode: 8, preventDefault: Math.abs});\n      this.blur();\n      this.focus();\n      return\n    }\n    if (this.composing) { return }\n    this.rememberSelection();\n    var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);\n    var head = domToPos(cm, sel.focusNode, sel.focusOffset);\n    if (anchor && head) { runInOp(cm, function () {\n      setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);\n      if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }\n    }); }\n  };\n\n  ContentEditableInput.prototype.pollContent = function () {\n    if (this.readDOMTimeout != null) {\n      clearTimeout(this.readDOMTimeout);\n      this.readDOMTimeout = null;\n    }\n\n    var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();\n    var from = sel.from(), to = sel.to();\n    if (from.ch == 0 && from.line > cm.firstLine())\n      { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }\n    if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())\n      { to = Pos(to.line + 1, 0); }\n    if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }\n\n    var fromIndex, fromLine, fromNode;\n    if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {\n      fromLine = lineNo(display.view[0].line);\n      fromNode = display.view[0].node;\n    } else {\n      fromLine = lineNo(display.view[fromIndex].line);\n      fromNode = display.view[fromIndex - 1].node.nextSibling;\n    }\n    var toIndex = findViewIndex(cm, to.line);\n    var toLine, toNode;\n    if (toIndex == display.view.length - 1) {\n      toLine = display.viewTo - 1;\n      toNode = display.lineDiv.lastChild;\n    } else {\n      toLine = lineNo(display.view[toIndex + 1].line) - 1;\n      toNode = display.view[toIndex + 1].node.previousSibling;\n    }\n\n    if (!fromNode) { return false }\n    var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));\n    var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));\n    while (newText.length > 1 && oldText.length > 1) {\n      if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }\n      else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }\n      else { break }\n    }\n\n    var cutFront = 0, cutEnd = 0;\n    var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);\n    while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))\n      { ++cutFront; }\n    var newBot = lst(newText), oldBot = lst(oldText);\n    var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),\n                             oldBot.length - (oldText.length == 1 ? cutFront : 0));\n    while (cutEnd < maxCutEnd &&\n           newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))\n      { ++cutEnd; }\n    // Try to move start of change to start of selection if ambiguous\n    if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {\n      while (cutFront && cutFront > from.ch &&\n             newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {\n        cutFront--;\n        cutEnd++;\n      }\n    }\n\n    newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\\u200b+/, \"\");\n    newText[0] = newText[0].slice(cutFront).replace(/\\u200b+$/, \"\");\n\n    var chFrom = Pos(fromLine, cutFront);\n    var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);\n    if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {\n      replaceRange(cm.doc, newText, chFrom, chTo, \"+input\");\n      return true\n    }\n  };\n\n  ContentEditableInput.prototype.ensurePolled = function () {\n    this.forceCompositionEnd();\n  };\n  ContentEditableInput.prototype.reset = function () {\n    this.forceCompositionEnd();\n  };\n  ContentEditableInput.prototype.forceCompositionEnd = function () {\n    if (!this.composing) { return }\n    clearTimeout(this.readDOMTimeout);\n    this.composing = null;\n    this.updateFromDOM();\n    this.div.blur();\n    this.div.focus();\n  };\n  ContentEditableInput.prototype.readFromDOMSoon = function () {\n      var this$1 = this;\n\n    if (this.readDOMTimeout != null) { return }\n    this.readDOMTimeout = setTimeout(function () {\n      this$1.readDOMTimeout = null;\n      if (this$1.composing) {\n        if (this$1.composing.done) { this$1.composing = null; }\n        else { return }\n      }\n      this$1.updateFromDOM();\n    }, 80);\n  };\n\n  ContentEditableInput.prototype.updateFromDOM = function () {\n      var this$1 = this;\n\n    if (this.cm.isReadOnly() || !this.pollContent())\n      { runInOp(this.cm, function () { return regChange(this$1.cm); }); }\n  };\n\n  ContentEditableInput.prototype.setUneditable = function (node) {\n    node.contentEditable = \"false\";\n  };\n\n  ContentEditableInput.prototype.onKeyPress = function (e) {\n    if (e.charCode == 0 || this.composing) { return }\n    e.preventDefault();\n    if (!this.cm.isReadOnly())\n      { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }\n  };\n\n  ContentEditableInput.prototype.readOnlyChanged = function (val) {\n    this.div.contentEditable = String(val != \"nocursor\");\n  };\n\n  ContentEditableInput.prototype.onContextMenu = function () {};\n  ContentEditableInput.prototype.resetPosition = function () {};\n\n  ContentEditableInput.prototype.needsContentAttribute = true;\n\n  function posToDOM(cm, pos) {\n    var view = findViewForLine(cm, pos.line);\n    if (!view || view.hidden) { return null }\n    var line = getLine(cm.doc, pos.line);\n    var info = mapFromLineView(view, line, pos.line);\n\n    var order = getOrder(line, cm.doc.direction), side = \"left\";\n    if (order) {\n      var partPos = getBidiPartAt(order, pos.ch);\n      side = partPos % 2 ? \"right\" : \"left\";\n    }\n    var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);\n    result.offset = result.collapse == \"right\" ? result.end : result.start;\n    return result\n  }\n\n  function isInGutter(node) {\n    for (var scan = node; scan; scan = scan.parentNode)\n      { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }\n    return false\n  }\n\n  function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }\n\n  function domTextBetween(cm, from, to, fromLine, toLine) {\n    var text = \"\", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;\n    function recognizeMarker(id) { return function (marker) { return marker.id == id; } }\n    function close() {\n      if (closing) {\n        text += lineSep;\n        if (extraLinebreak) { text += lineSep; }\n        closing = extraLinebreak = false;\n      }\n    }\n    function addText(str) {\n      if (str) {\n        close();\n        text += str;\n      }\n    }\n    function walk(node) {\n      if (node.nodeType == 1) {\n        var cmText = node.getAttribute(\"cm-text\");\n        if (cmText) {\n          addText(cmText);\n          return\n        }\n        var markerID = node.getAttribute(\"cm-marker\"), range$$1;\n        if (markerID) {\n          var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));\n          if (found.length && (range$$1 = found[0].find(0)))\n            { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); }\n          return\n        }\n        if (node.getAttribute(\"contenteditable\") == \"false\") { return }\n        var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);\n        if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }\n\n        if (isBlock) { close(); }\n        for (var i = 0; i < node.childNodes.length; i++)\n          { walk(node.childNodes[i]); }\n\n        if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }\n        if (isBlock) { closing = true; }\n      } else if (node.nodeType == 3) {\n        addText(node.nodeValue.replace(/\\u200b/g, \"\").replace(/\\u00a0/g, \" \"));\n      }\n    }\n    for (;;) {\n      walk(from);\n      if (from == to) { break }\n      from = from.nextSibling;\n      extraLinebreak = false;\n    }\n    return text\n  }\n\n  function domToPos(cm, node, offset) {\n    var lineNode;\n    if (node == cm.display.lineDiv) {\n      lineNode = cm.display.lineDiv.childNodes[offset];\n      if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }\n      node = null; offset = 0;\n    } else {\n      for (lineNode = node;; lineNode = lineNode.parentNode) {\n        if (!lineNode || lineNode == cm.display.lineDiv) { return null }\n        if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }\n      }\n    }\n    for (var i = 0; i < cm.display.view.length; i++) {\n      var lineView = cm.display.view[i];\n      if (lineView.node == lineNode)\n        { return locateNodeInLineView(lineView, node, offset) }\n    }\n  }\n\n  function locateNodeInLineView(lineView, node, offset) {\n    var wrapper = lineView.text.firstChild, bad = false;\n    if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }\n    if (node == wrapper) {\n      bad = true;\n      node = wrapper.childNodes[offset];\n      offset = 0;\n      if (!node) {\n        var line = lineView.rest ? lst(lineView.rest) : lineView.line;\n        return badPos(Pos(lineNo(line), line.text.length), bad)\n      }\n    }\n\n    var textNode = node.nodeType == 3 ? node : null, topNode = node;\n    if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {\n      textNode = node.firstChild;\n      if (offset) { offset = textNode.nodeValue.length; }\n    }\n    while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }\n    var measure = lineView.measure, maps = measure.maps;\n\n    function find(textNode, topNode, offset) {\n      for (var i = -1; i < (maps ? maps.length : 0); i++) {\n        var map$$1 = i < 0 ? measure.map : maps[i];\n        for (var j = 0; j < map$$1.length; j += 3) {\n          var curNode = map$$1[j + 2];\n          if (curNode == textNode || curNode == topNode) {\n            var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);\n            var ch = map$$1[j] + offset;\n            if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; }\n            return Pos(line, ch)\n          }\n        }\n      }\n    }\n    var found = find(textNode, topNode, offset);\n    if (found) { return badPos(found, bad) }\n\n    // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems\n    for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {\n      found = find(after, after.firstChild, 0);\n      if (found)\n        { return badPos(Pos(found.line, found.ch - dist), bad) }\n      else\n        { dist += after.textContent.length; }\n    }\n    for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {\n      found = find(before, before.firstChild, -1);\n      if (found)\n        { return badPos(Pos(found.line, found.ch + dist$1), bad) }\n      else\n        { dist$1 += before.textContent.length; }\n    }\n  }\n\n  // TEXTAREA INPUT STYLE\n\n  var TextareaInput = function(cm) {\n    this.cm = cm;\n    // See input.poll and input.reset\n    this.prevInput = \"\";\n\n    // Flag that indicates whether we expect input to appear real soon\n    // now (after some event like 'keypress' or 'input') and are\n    // polling intensively.\n    this.pollingFast = false;\n    // Self-resetting timeout for the poller\n    this.polling = new Delayed();\n    // Used to work around IE issue with selection being forgotten when focus moves away from textarea\n    this.hasSelection = false;\n    this.composing = null;\n  };\n\n  TextareaInput.prototype.init = function (display) {\n      var this$1 = this;\n\n    var input = this, cm = this.cm;\n    this.createField(display);\n    var te = this.textarea;\n\n    display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);\n\n    // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)\n    if (ios) { te.style.width = \"0px\"; }\n\n    on(te, \"input\", function () {\n      if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }\n      input.poll();\n    });\n\n    on(te, \"paste\", function (e) {\n      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }\n\n      cm.state.pasteIncoming = true;\n      input.fastPoll();\n    });\n\n    function prepareCopyCut(e) {\n      if (signalDOMEvent(cm, e)) { return }\n      if (cm.somethingSelected()) {\n        setLastCopied({lineWise: false, text: cm.getSelections()});\n      } else if (!cm.options.lineWiseCopyCut) {\n        return\n      } else {\n        var ranges = copyableRanges(cm);\n        setLastCopied({lineWise: true, text: ranges.text});\n        if (e.type == \"cut\") {\n          cm.setSelections(ranges.ranges, null, sel_dontScroll);\n        } else {\n          input.prevInput = \"\";\n          te.value = ranges.text.join(\"\\n\");\n          selectInput(te);\n        }\n      }\n      if (e.type == \"cut\") { cm.state.cutIncoming = true; }\n    }\n    on(te, \"cut\", prepareCopyCut);\n    on(te, \"copy\", prepareCopyCut);\n\n    on(display.scroller, \"paste\", function (e) {\n      if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }\n      cm.state.pasteIncoming = true;\n      input.focus();\n    });\n\n    // Prevent normal selection in the editor (we handle our own)\n    on(display.lineSpace, \"selectstart\", function (e) {\n      if (!eventInWidget(display, e)) { e_preventDefault(e); }\n    });\n\n    on(te, \"compositionstart\", function () {\n      var start = cm.getCursor(\"from\");\n      if (input.composing) { input.composing.range.clear(); }\n      input.composing = {\n        start: start,\n        range: cm.markText(start, cm.getCursor(\"to\"), {className: \"CodeMirror-composing\"})\n      };\n    });\n    on(te, \"compositionend\", function () {\n      if (input.composing) {\n        input.poll();\n        input.composing.range.clear();\n        input.composing = null;\n      }\n    });\n  };\n\n  TextareaInput.prototype.createField = function (_display) {\n    // Wraps and hides input textarea\n    this.wrapper = hiddenTextarea();\n    // The semihidden textarea that is focused when the editor is\n    // focused, and receives input.\n    this.textarea = this.wrapper.firstChild;\n  };\n\n  TextareaInput.prototype.prepareSelection = function () {\n    // Redraw the selection and/or cursor\n    var cm = this.cm, display = cm.display, doc = cm.doc;\n    var result = prepareSelection(cm);\n\n    // Move the hidden textarea near the cursor to prevent scrolling artifacts\n    if (cm.options.moveInputWithCursor) {\n      var headPos = cursorCoords(cm, doc.sel.primary().head, \"div\");\n      var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();\n      result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,\n                                          headPos.top + lineOff.top - wrapOff.top));\n      result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,\n                                           headPos.left + lineOff.left - wrapOff.left));\n    }\n\n    return result\n  };\n\n  TextareaInput.prototype.showSelection = function (drawn) {\n    var cm = this.cm, display = cm.display;\n    removeChildrenAndAdd(display.cursorDiv, drawn.cursors);\n    removeChildrenAndAdd(display.selectionDiv, drawn.selection);\n    if (drawn.teTop != null) {\n      this.wrapper.style.top = drawn.teTop + \"px\";\n      this.wrapper.style.left = drawn.teLeft + \"px\";\n    }\n  };\n\n  // Reset the input to correspond to the selection (or to be empty,\n  // when not typing and nothing is selected)\n  TextareaInput.prototype.reset = function (typing) {\n    if (this.contextMenuPending || this.composing) { return }\n    var cm = this.cm;\n    if (cm.somethingSelected()) {\n      this.prevInput = \"\";\n      var content = cm.getSelection();\n      this.textarea.value = content;\n      if (cm.state.focused) { selectInput(this.textarea); }\n      if (ie && ie_version >= 9) { this.hasSelection = content; }\n    } else if (!typing) {\n      this.prevInput = this.textarea.value = \"\";\n      if (ie && ie_version >= 9) { this.hasSelection = null; }\n    }\n  };\n\n  TextareaInput.prototype.getField = function () { return this.textarea };\n\n  TextareaInput.prototype.supportsTouch = function () { return false };\n\n  TextareaInput.prototype.focus = function () {\n    if (this.cm.options.readOnly != \"nocursor\" && (!mobile || activeElt() != this.textarea)) {\n      try { this.textarea.focus(); }\n      catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM\n    }\n  };\n\n  TextareaInput.prototype.blur = function () { this.textarea.blur(); };\n\n  TextareaInput.prototype.resetPosition = function () {\n    this.wrapper.style.top = this.wrapper.style.left = 0;\n  };\n\n  TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };\n\n  // Poll for input changes, using the normal rate of polling. This\n  // runs as long as the editor is focused.\n  TextareaInput.prototype.slowPoll = function () {\n      var this$1 = this;\n\n    if (this.pollingFast) { return }\n    this.polling.set(this.cm.options.pollInterval, function () {\n      this$1.poll();\n      if (this$1.cm.state.focused) { this$1.slowPoll(); }\n    });\n  };\n\n  // When an event has just come in that is likely to add or change\n  // something in the input textarea, we poll faster, to ensure that\n  // the change appears on the screen quickly.\n  TextareaInput.prototype.fastPoll = function () {\n    var missed = false, input = this;\n    input.pollingFast = true;\n    function p() {\n      var changed = input.poll();\n      if (!changed && !missed) {missed = true; input.polling.set(60, p);}\n      else {input.pollingFast = false; input.slowPoll();}\n    }\n    input.polling.set(20, p);\n  };\n\n  // Read input from the textarea, and update the document to match.\n  // When something is selected, it is present in the textarea, and\n  // selected (unless it is huge, in which case a placeholder is\n  // used). When nothing is selected, the cursor sits after previously\n  // seen text (can be empty), which is stored in prevInput (we must\n  // not reset the textarea when typing, because that breaks IME).\n  TextareaInput.prototype.poll = function () {\n      var this$1 = this;\n\n    var cm = this.cm, input = this.textarea, prevInput = this.prevInput;\n    // Since this is called a *lot*, try to bail out as cheaply as\n    // possible when it is clear that nothing happened. hasSelection\n    // will be the case when there is a lot of text in the textarea,\n    // in which case reading its value would be expensive.\n    if (this.contextMenuPending || !cm.state.focused ||\n        (hasSelection(input) && !prevInput && !this.composing) ||\n        cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)\n      { return false }\n\n    var text = input.value;\n    // If nothing changed, bail.\n    if (text == prevInput && !cm.somethingSelected()) { return false }\n    // Work around nonsensical selection resetting in IE9/10, and\n    // inexplicable appearance of private area unicode characters on\n    // some key combos in Mac (#2689).\n    if (ie && ie_version >= 9 && this.hasSelection === text ||\n        mac && /[\\uf700-\\uf7ff]/.test(text)) {\n      cm.display.input.reset();\n      return false\n    }\n\n    if (cm.doc.sel == cm.display.selForContextMenu) {\n      var first = text.charCodeAt(0);\n      if (first == 0x200b && !prevInput) { prevInput = \"\\u200b\"; }\n      if (first == 0x21da) { this.reset(); return this.cm.execCommand(\"undo\") }\n    }\n    // Find the part of the input that is actually new\n    var same = 0, l = Math.min(prevInput.length, text.length);\n    while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }\n\n    runInOp(cm, function () {\n      applyTextInput(cm, text.slice(same), prevInput.length - same,\n                     null, this$1.composing ? \"*compose\" : null);\n\n      // Don't leave long text in the textarea, since it makes further polling slow\n      if (text.length > 1000 || text.indexOf(\"\\n\") > -1) { input.value = this$1.prevInput = \"\"; }\n      else { this$1.prevInput = text; }\n\n      if (this$1.composing) {\n        this$1.composing.range.clear();\n        this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor(\"to\"),\n                                           {className: \"CodeMirror-composing\"});\n      }\n    });\n    return true\n  };\n\n  TextareaInput.prototype.ensurePolled = function () {\n    if (this.pollingFast && this.poll()) { this.pollingFast = false; }\n  };\n\n  TextareaInput.prototype.onKeyPress = function () {\n    if (ie && ie_version >= 9) { this.hasSelection = null; }\n    this.fastPoll();\n  };\n\n  TextareaInput.prototype.onContextMenu = function (e) {\n    var input = this, cm = input.cm, display = cm.display, te = input.textarea;\n    var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;\n    if (!pos || presto) { return } // Opera is difficult.\n\n    // Reset the current text selection only if the click is done outside of the selection\n    // and 'resetSelectionOnContextMenu' option is true.\n    var reset = cm.options.resetSelectionOnContextMenu;\n    if (reset && cm.doc.sel.contains(pos) == -1)\n      { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }\n\n    var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;\n    input.wrapper.style.cssText = \"position: absolute\";\n    var wrapperBox = input.wrapper.getBoundingClientRect();\n    te.style.cssText = \"position: absolute; width: 30px; height: 30px;\\n      top: \" + (e.clientY - wrapperBox.top - 5) + \"px; left: \" + (e.clientX - wrapperBox.left - 5) + \"px;\\n      z-index: 1000; background: \" + (ie ? \"rgba(255, 255, 255, .05)\" : \"transparent\") + \";\\n      outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);\";\n    var oldScrollY;\n    if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)\n    display.input.focus();\n    if (webkit) { window.scrollTo(null, oldScrollY); }\n    display.input.reset();\n    // Adds \"Select all\" to context menu in FF\n    if (!cm.somethingSelected()) { te.value = input.prevInput = \" \"; }\n    input.contextMenuPending = true;\n    display.selForContextMenu = cm.doc.sel;\n    clearTimeout(display.detectingSelectAll);\n\n    // Select-all will be greyed out if there's nothing to select, so\n    // this adds a zero-width space so that we can later check whether\n    // it got selected.\n    function prepareSelectAllHack() {\n      if (te.selectionStart != null) {\n        var selected = cm.somethingSelected();\n        var extval = \"\\u200b\" + (selected ? te.value : \"\");\n        te.value = \"\\u21da\"; // Used to catch context-menu undo\n        te.value = extval;\n        input.prevInput = selected ? \"\" : \"\\u200b\";\n        te.selectionStart = 1; te.selectionEnd = extval.length;\n        // Re-set this, in case some other handler touched the\n        // selection in the meantime.\n        display.selForContextMenu = cm.doc.sel;\n      }\n    }\n    function rehide() {\n      input.contextMenuPending = false;\n      input.wrapper.style.cssText = oldWrapperCSS;\n      te.style.cssText = oldCSS;\n      if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }\n\n      // Try to detect the user choosing select-all\n      if (te.selectionStart != null) {\n        if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }\n        var i = 0, poll = function () {\n          if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&\n              te.selectionEnd > 0 && input.prevInput == \"\\u200b\") {\n            operation(cm, selectAll)(cm);\n          } else if (i++ < 10) {\n            display.detectingSelectAll = setTimeout(poll, 500);\n          } else {\n            display.selForContextMenu = null;\n            display.input.reset();\n          }\n        };\n        display.detectingSelectAll = setTimeout(poll, 200);\n      }\n    }\n\n    if (ie && ie_version >= 9) { prepareSelectAllHack(); }\n    if (captureRightClick) {\n      e_stop(e);\n      var mouseup = function () {\n        off(window, \"mouseup\", mouseup);\n        setTimeout(rehide, 20);\n      };\n      on(window, \"mouseup\", mouseup);\n    } else {\n      setTimeout(rehide, 50);\n    }\n  };\n\n  TextareaInput.prototype.readOnlyChanged = function (val) {\n    if (!val) { this.reset(); }\n    this.textarea.disabled = val == \"nocursor\";\n  };\n\n  TextareaInput.prototype.setUneditable = function () {};\n\n  TextareaInput.prototype.needsContentAttribute = false;\n\n  function fromTextArea(textarea, options) {\n    options = options ? copyObj(options) : {};\n    options.value = textarea.value;\n    if (!options.tabindex && textarea.tabIndex)\n      { options.tabindex = textarea.tabIndex; }\n    if (!options.placeholder && textarea.placeholder)\n      { options.placeholder = textarea.placeholder; }\n    // Set autofocus to true if this textarea is focused, or if it has\n    // autofocus and no other element is focused.\n    if (options.autofocus == null) {\n      var hasFocus = activeElt();\n      options.autofocus = hasFocus == textarea ||\n        textarea.getAttribute(\"autofocus\") != null && hasFocus == document.body;\n    }\n\n    function save() {textarea.value = cm.getValue();}\n\n    var realSubmit;\n    if (textarea.form) {\n      on(textarea.form, \"submit\", save);\n      // Deplorable hack to make the submit method do the right thing.\n      if (!options.leaveSubmitMethodAlone) {\n        var form = textarea.form;\n        realSubmit = form.submit;\n        try {\n          var wrappedSubmit = form.submit = function () {\n            save();\n            form.submit = realSubmit;\n            form.submit();\n            form.submit = wrappedSubmit;\n          };\n        } catch(e) {}\n      }\n    }\n\n    options.finishInit = function (cm) {\n      cm.save = save;\n      cm.getTextArea = function () { return textarea; };\n      cm.toTextArea = function () {\n        cm.toTextArea = isNaN; // Prevent this from being ran twice\n        save();\n        textarea.parentNode.removeChild(cm.getWrapperElement());\n        textarea.style.display = \"\";\n        if (textarea.form) {\n          off(textarea.form, \"submit\", save);\n          if (typeof textarea.form.submit == \"function\")\n            { textarea.form.submit = realSubmit; }\n        }\n      };\n    };\n\n    textarea.style.display = \"none\";\n    var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },\n      options);\n    return cm\n  }\n\n  function addLegacyProps(CodeMirror) {\n    CodeMirror.off = off;\n    CodeMirror.on = on;\n    CodeMirror.wheelEventPixels = wheelEventPixels;\n    CodeMirror.Doc = Doc;\n    CodeMirror.splitLines = splitLinesAuto;\n    CodeMirror.countColumn = countColumn;\n    CodeMirror.findColumn = findColumn;\n    CodeMirror.isWordChar = isWordCharBasic;\n    CodeMirror.Pass = Pass;\n    CodeMirror.signal = signal;\n    CodeMirror.Line = Line;\n    CodeMirror.changeEnd = changeEnd;\n    CodeMirror.scrollbarModel = scrollbarModel;\n    CodeMirror.Pos = Pos;\n    CodeMirror.cmpPos = cmp;\n    CodeMirror.modes = modes;\n    CodeMirror.mimeModes = mimeModes;\n    CodeMirror.resolveMode = resolveMode;\n    CodeMirror.getMode = getMode;\n    CodeMirror.modeExtensions = modeExtensions;\n    CodeMirror.extendMode = extendMode;\n    CodeMirror.copyState = copyState;\n    CodeMirror.startState = startState;\n    CodeMirror.innerMode = innerMode;\n    CodeMirror.commands = commands;\n    CodeMirror.keyMap = keyMap;\n    CodeMirror.keyName = keyName;\n    CodeMirror.isModifierKey = isModifierKey;\n    CodeMirror.lookupKey = lookupKey;\n    CodeMirror.normalizeKeyMap = normalizeKeyMap;\n    CodeMirror.StringStream = StringStream;\n    CodeMirror.SharedTextMarker = SharedTextMarker;\n    CodeMirror.TextMarker = TextMarker;\n    CodeMirror.LineWidget = LineWidget;\n    CodeMirror.e_preventDefault = e_preventDefault;\n    CodeMirror.e_stopPropagation = e_stopPropagation;\n    CodeMirror.e_stop = e_stop;\n    CodeMirror.addClass = addClass;\n    CodeMirror.contains = contains;\n    CodeMirror.rmClass = rmClass;\n    CodeMirror.keyNames = keyNames;\n  }\n\n  // EDITOR CONSTRUCTOR\n\n  defineOptions(CodeMirror);\n\n  addEditorMethods(CodeMirror);\n\n  // Set up methods on CodeMirror's prototype to redirect to the editor's document.\n  var dontDelegate = \"iter insert remove copy getEditor constructor\".split(\" \");\n  for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)\n    { CodeMirror.prototype[prop] = (function(method) {\n      return function() {return method.apply(this.doc, arguments)}\n    })(Doc.prototype[prop]); } }\n\n  eventMixin(Doc);\n  CodeMirror.inputStyles = {\"textarea\": TextareaInput, \"contenteditable\": ContentEditableInput};\n\n  // Extra arguments are stored as the mode's dependencies, which is\n  // used by (legacy) mechanisms like loadmode.js to automatically\n  // load a mode. (Preferred mechanism is the require/define calls.)\n  CodeMirror.defineMode = function(name/*, mode, …*/) {\n    if (!CodeMirror.defaults.mode && name != \"null\") { CodeMirror.defaults.mode = name; }\n    defineMode.apply(this, arguments);\n  };\n\n  CodeMirror.defineMIME = defineMIME;\n\n  // Minimal default mode.\n  CodeMirror.defineMode(\"null\", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });\n  CodeMirror.defineMIME(\"text/plain\", \"null\");\n\n  // EXTENSIONS\n\n  CodeMirror.defineExtension = function (name, func) {\n    CodeMirror.prototype[name] = func;\n  };\n  CodeMirror.defineDocExtension = function (name, func) {\n    Doc.prototype[name] = func;\n  };\n\n  CodeMirror.fromTextArea = fromTextArea;\n\n  addLegacyProps(CodeMirror);\n\n  CodeMirror.version = \"5.41.0\";\n\n  return CodeMirror;\n\n})));\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/mode/clike/clike.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nfunction Context(indented, column, type, info, align, prev) {\n  this.indented = indented;\n  this.column = column;\n  this.type = type;\n  this.info = info;\n  this.align = align;\n  this.prev = prev;\n}\nfunction pushContext(state, col, type, info) {\n  var indent = state.indented;\n  if (state.context && state.context.type == \"statement\" && type != \"statement\")\n    indent = state.context.indented;\n  return state.context = new Context(indent, col, type, info, null, state.context);\n}\nfunction popContext(state) {\n  var t = state.context.type;\n  if (t == \")\" || t == \"]\" || t == \"}\")\n    state.indented = state.context.indented;\n  return state.context = state.context.prev;\n}\n\nfunction typeBefore(stream, state, pos) {\n  if (state.prevToken == \"variable\" || state.prevToken == \"type\") return true;\n  if (/\\S(?:[^- ]>|[*\\]])\\s*$|\\*$/.test(stream.string.slice(0, pos))) return true;\n  if (state.typeAtEndOfLine && stream.column() == stream.indentation()) return true;\n}\n\nfunction isTopScope(context) {\n  for (;;) {\n    if (!context || context.type == \"top\") return true;\n    if (context.type == \"}\" && context.prev.info != \"namespace\") return false;\n    context = context.prev;\n  }\n}\n\nCodeMirror.defineMode(\"clike\", function(config, parserConfig) {\n  var indentUnit = config.indentUnit,\n      statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,\n      dontAlignCalls = parserConfig.dontAlignCalls,\n      keywords = parserConfig.keywords || {},\n      types = parserConfig.types || {},\n      builtin = parserConfig.builtin || {},\n      blockKeywords = parserConfig.blockKeywords || {},\n      defKeywords = parserConfig.defKeywords || {},\n      atoms = parserConfig.atoms || {},\n      hooks = parserConfig.hooks || {},\n      multiLineStrings = parserConfig.multiLineStrings,\n      indentStatements = parserConfig.indentStatements !== false,\n      indentSwitch = parserConfig.indentSwitch !== false,\n      namespaceSeparator = parserConfig.namespaceSeparator,\n      isPunctuationChar = parserConfig.isPunctuationChar || /[\\[\\]{}\\(\\),;\\:\\.]/,\n      numberStart = parserConfig.numberStart || /[\\d\\.]/,\n      number = parserConfig.number || /^(?:0x[a-f\\d]+|0b[01]+|(?:\\d+\\.?\\d*|\\.\\d+)(?:e[-+]?\\d+)?)(u|ll?|l|f)?/i,\n      isOperatorChar = parserConfig.isOperatorChar || /[+\\-*&%=<>!?|\\/]/,\n      isIdentifierChar = parserConfig.isIdentifierChar || /[\\w\\$_\\xa1-\\uffff]/,\n      // An optional function that takes a {string} token and returns true if it\n      // should be treated as a builtin.\n      isReservedIdentifier = parserConfig.isReservedIdentifier || false;\n\n  var curPunc, isDefKeyword;\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n    if (hooks[ch]) {\n      var result = hooks[ch](stream, state);\n      if (result !== false) return result;\n    }\n    if (ch == '\"' || ch == \"'\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    }\n    if (isPunctuationChar.test(ch)) {\n      curPunc = ch;\n      return null;\n    }\n    if (numberStart.test(ch)) {\n      stream.backUp(1)\n      if (stream.match(number)) return \"number\"\n      stream.next()\n    }\n    if (ch == \"/\") {\n      if (stream.eat(\"*\")) {\n        state.tokenize = tokenComment;\n        return tokenComment(stream, state);\n      }\n      if (stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n    }\n    if (isOperatorChar.test(ch)) {\n      while (!stream.match(/^\\/[\\/*]/, false) && stream.eat(isOperatorChar)) {}\n      return \"operator\";\n    }\n    stream.eatWhile(isIdentifierChar);\n    if (namespaceSeparator) while (stream.match(namespaceSeparator))\n      stream.eatWhile(isIdentifierChar);\n\n    var cur = stream.current();\n    if (contains(keywords, cur)) {\n      if (contains(blockKeywords, cur)) curPunc = \"newstatement\";\n      if (contains(defKeywords, cur)) isDefKeyword = true;\n      return \"keyword\";\n    }\n    if (contains(types, cur)) return \"type\";\n    if (contains(builtin, cur)\n        || (isReservedIdentifier && isReservedIdentifier(cur))) {\n      if (contains(blockKeywords, cur)) curPunc = \"newstatement\";\n      return \"builtin\";\n    }\n    if (contains(atoms, cur)) return \"atom\";\n    return \"variable\";\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, next, end = false;\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) {end = true; break;}\n        escaped = !escaped && next == \"\\\\\";\n      }\n      if (end || !(escaped || multiLineStrings))\n        state.tokenize = null;\n      return \"string\";\n    };\n  }\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize = null;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return \"comment\";\n  }\n\n  function maybeEOL(stream, state) {\n    if (parserConfig.typeFirstDefinitions && stream.eol() && isTopScope(state.context))\n      state.typeAtEndOfLine = typeBefore(stream, state, stream.pos)\n  }\n\n  // Interface\n\n  return {\n    startState: function(basecolumn) {\n      return {\n        tokenize: null,\n        context: new Context((basecolumn || 0) - indentUnit, 0, \"top\", null, false),\n        indented: 0,\n        startOfLine: true,\n        prevToken: null\n      };\n    },\n\n    token: function(stream, state) {\n      var ctx = state.context;\n      if (stream.sol()) {\n        if (ctx.align == null) ctx.align = false;\n        state.indented = stream.indentation();\n        state.startOfLine = true;\n      }\n      if (stream.eatSpace()) { maybeEOL(stream, state); return null; }\n      curPunc = isDefKeyword = null;\n      var style = (state.tokenize || tokenBase)(stream, state);\n      if (style == \"comment\" || style == \"meta\") return style;\n      if (ctx.align == null) ctx.align = true;\n\n      if (curPunc == \";\" || curPunc == \":\" || (curPunc == \",\" && stream.match(/^\\s*(?:\\/\\/.*)?$/, false)))\n        while (state.context.type == \"statement\") popContext(state);\n      else if (curPunc == \"{\") pushContext(state, stream.column(), \"}\");\n      else if (curPunc == \"[\") pushContext(state, stream.column(), \"]\");\n      else if (curPunc == \"(\") pushContext(state, stream.column(), \")\");\n      else if (curPunc == \"}\") {\n        while (ctx.type == \"statement\") ctx = popContext(state);\n        if (ctx.type == \"}\") ctx = popContext(state);\n        while (ctx.type == \"statement\") ctx = popContext(state);\n      }\n      else if (curPunc == ctx.type) popContext(state);\n      else if (indentStatements &&\n               (((ctx.type == \"}\" || ctx.type == \"top\") && curPunc != \";\") ||\n                (ctx.type == \"statement\" && curPunc == \"newstatement\"))) {\n        pushContext(state, stream.column(), \"statement\", stream.current());\n      }\n\n      if (style == \"variable\" &&\n          ((state.prevToken == \"def\" ||\n            (parserConfig.typeFirstDefinitions && typeBefore(stream, state, stream.start) &&\n             isTopScope(state.context) && stream.match(/^\\s*\\(/, false)))))\n        style = \"def\";\n\n      if (hooks.token) {\n        var result = hooks.token(stream, state, style);\n        if (result !== undefined) style = result;\n      }\n\n      if (style == \"def\" && parserConfig.styleDefs === false) style = \"variable\";\n\n      state.startOfLine = false;\n      state.prevToken = isDefKeyword ? \"def\" : style || curPunc;\n      maybeEOL(stream, state);\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine) return CodeMirror.Pass;\n      var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);\n      var closing = firstChar == ctx.type;\n      if (ctx.type == \"statement\" && firstChar == \"}\") ctx = ctx.prev;\n      if (parserConfig.dontIndentStatements)\n        while (ctx.type == \"statement\" && parserConfig.dontIndentStatements.test(ctx.info))\n          ctx = ctx.prev\n      if (hooks.indent) {\n        var hook = hooks.indent(state, ctx, textAfter, indentUnit);\n        if (typeof hook == \"number\") return hook\n      }\n      var switchBlock = ctx.prev && ctx.prev.info == \"switch\";\n      if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) {\n        while (ctx.type != \"top\" && ctx.type != \"}\") ctx = ctx.prev\n        return ctx.indented\n      }\n      if (ctx.type == \"statement\")\n        return ctx.indented + (firstChar == \"{\" ? 0 : statementIndentUnit);\n      if (ctx.align && (!dontAlignCalls || ctx.type != \")\"))\n        return ctx.column + (closing ? 0 : 1);\n      if (ctx.type == \")\" && !closing)\n        return ctx.indented + statementIndentUnit;\n\n      return ctx.indented + (closing ? 0 : indentUnit) +\n        (!closing && switchBlock && !/^(?:case|default)\\b/.test(textAfter) ? indentUnit : 0);\n    },\n\n    electricInput: indentSwitch ? /^\\s*(?:case .*?:|default:|\\{\\}?|\\})$/ : /^\\s*[{}]$/,\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\",\n    blockCommentContinue: \" * \",\n    lineComment: \"//\",\n    fold: \"brace\"\n  };\n});\n\n  function words(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n  function contains(words, word) {\n    if (typeof words === \"function\") {\n      return words(word);\n    } else {\n      return words.propertyIsEnumerable(word);\n    }\n  }\n  var cKeywords = \"auto if break case register continue return default do sizeof \" +\n    \"static else struct switch extern typedef union for goto while enum const \" +\n    \"volatile inline restrict asm fortran\";\n\n  // Do not use this. Use the cTypes function below. This is global just to avoid\n  // excessive calls when cTypes is being called multiple times during a parse.\n  var basicCTypes = words(\"int long char short double float unsigned signed \" +\n    \"void bool\");\n\n  // Do not use this. Use the objCTypes function below. This is global just to avoid\n  // excessive calls when objCTypes is being called multiple times during a parse.\n  var basicObjCTypes = words(\"SEL instancetype id Class Protocol BOOL\");\n\n  // Returns true if identifier is a \"C\" type.\n  // C type is defined as those that are reserved by the compiler (basicTypes),\n  // and those that end in _t (Reserved by POSIX for types)\n  // http://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html\n  function cTypes(identifier) {\n    return contains(basicCTypes, identifier) || /.+_t/.test(identifier);\n  }\n\n  // Returns true if identifier is a \"Objective C\" type.\n  function objCTypes(identifier) {\n    return cTypes(identifier) || contains(basicObjCTypes, identifier);\n  }\n\n  var cBlockKeywords = \"case do else for if switch while struct enum union\";\n  var cDefKeywords = \"struct enum union\";\n\n  function cppHook(stream, state) {\n    if (!state.startOfLine) return false\n    for (var ch, next = null; ch = stream.peek();) {\n      if (ch == \"\\\\\" && stream.match(/^.$/)) {\n        next = cppHook\n        break\n      } else if (ch == \"/\" && stream.match(/^\\/[\\/\\*]/, false)) {\n        break\n      }\n      stream.next()\n    }\n    state.tokenize = next\n    return \"meta\"\n  }\n\n  function pointerHook(_stream, state) {\n    if (state.prevToken == \"type\") return \"type\";\n    return false;\n  }\n\n  // For C and C++ (and ObjC): identifiers starting with __\n  // or _ followed by a capital letter are reserved for the compiler.\n  function cIsReservedIdentifier(token) {\n    if (!token || token.length < 2) return false;\n    if (token[0] != '_') return false;\n    return (token[1] == '_') || (token[1] !== token[1].toLowerCase());\n  }\n\n  function cpp14Literal(stream) {\n    stream.eatWhile(/[\\w\\.']/);\n    return \"number\";\n  }\n\n  function cpp11StringHook(stream, state) {\n    stream.backUp(1);\n    // Raw strings.\n    if (stream.match(/(R|u8R|uR|UR|LR)/)) {\n      var match = stream.match(/\"([^\\s\\\\()]{0,16})\\(/);\n      if (!match) {\n        return false;\n      }\n      state.cpp11RawStringDelim = match[1];\n      state.tokenize = tokenRawString;\n      return tokenRawString(stream, state);\n    }\n    // Unicode strings/chars.\n    if (stream.match(/(u8|u|U|L)/)) {\n      if (stream.match(/[\"']/, /* eat */ false)) {\n        return \"string\";\n      }\n      return false;\n    }\n    // Ignore this hook.\n    stream.next();\n    return false;\n  }\n\n  function cppLooksLikeConstructor(word) {\n    var lastTwo = /(\\w+)::~?(\\w+)$/.exec(word);\n    return lastTwo && lastTwo[1] == lastTwo[2];\n  }\n\n  // C#-style strings where \"\" escapes a quote.\n  function tokenAtString(stream, state) {\n    var next;\n    while ((next = stream.next()) != null) {\n      if (next == '\"' && !stream.eat('\"')) {\n        state.tokenize = null;\n        break;\n      }\n    }\n    return \"string\";\n  }\n\n  // C++11 raw string literal is <prefix>\"<delim>( anything )<delim>\", where\n  // <delim> can be a string up to 16 characters long.\n  function tokenRawString(stream, state) {\n    // Escape characters that have special regex meanings.\n    var delim = state.cpp11RawStringDelim.replace(/[^\\w\\s]/g, '\\\\$&');\n    var match = stream.match(new RegExp(\".*?\\\\)\" + delim + '\"'));\n    if (match)\n      state.tokenize = null;\n    else\n      stream.skipToEnd();\n    return \"string\";\n  }\n\n  function def(mimes, mode) {\n    if (typeof mimes == \"string\") mimes = [mimes];\n    var words = [];\n    function add(obj) {\n      if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))\n        words.push(prop);\n    }\n    add(mode.keywords);\n    add(mode.types);\n    add(mode.builtin);\n    add(mode.atoms);\n    if (words.length) {\n      mode.helperType = mimes[0];\n      CodeMirror.registerHelper(\"hintWords\", mimes[0], words);\n    }\n\n    for (var i = 0; i < mimes.length; ++i)\n      CodeMirror.defineMIME(mimes[i], mode);\n  }\n\n  def([\"text/x-csrc\", \"text/x-c\", \"text/x-chdr\"], {\n    name: \"clike\",\n    keywords: words(cKeywords),\n    types: cTypes,\n    blockKeywords: words(cBlockKeywords),\n    defKeywords: words(cDefKeywords),\n    typeFirstDefinitions: true,\n    atoms: words(\"NULL true false\"),\n    isReservedIdentifier: cIsReservedIdentifier,\n    hooks: {\n      \"#\": cppHook,\n      \"*\": pointerHook,\n    },\n    modeProps: {fold: [\"brace\", \"include\"]}\n  });\n\n  def([\"text/x-c++src\", \"text/x-c++hdr\"], {\n    name: \"clike\",\n    keywords: words(cKeywords + \" dynamic_cast namespace reinterpret_cast try explicit new \" +\n                    \"static_cast typeid catch operator template typename class friend private \" +\n                    \"this using const_cast public throw virtual delete mutable protected \" +\n                    \"alignas alignof constexpr decltype nullptr noexcept thread_local final \" +\n                    \"static_assert override\"),\n    types: cTypes,\n    blockKeywords: words(cBlockKeywords +\" class try catch finally\"),\n    defKeywords: words(cDefKeywords + \" class namespace\"),\n    typeFirstDefinitions: true,\n    atoms: words(\"true false NULL\"),\n    dontIndentStatements: /^template$/,\n    isIdentifierChar: /[\\w\\$_~\\xa1-\\uffff]/,\n    isReservedIdentifier: cIsReservedIdentifier,\n    hooks: {\n      \"#\": cppHook,\n      \"*\": pointerHook,\n      \"u\": cpp11StringHook,\n      \"U\": cpp11StringHook,\n      \"L\": cpp11StringHook,\n      \"R\": cpp11StringHook,\n      \"0\": cpp14Literal,\n      \"1\": cpp14Literal,\n      \"2\": cpp14Literal,\n      \"3\": cpp14Literal,\n      \"4\": cpp14Literal,\n      \"5\": cpp14Literal,\n      \"6\": cpp14Literal,\n      \"7\": cpp14Literal,\n      \"8\": cpp14Literal,\n      \"9\": cpp14Literal,\n      token: function(stream, state, style) {\n        if (style == \"variable\" && stream.peek() == \"(\" &&\n            (state.prevToken == \";\" || state.prevToken == null ||\n             state.prevToken == \"}\") &&\n            cppLooksLikeConstructor(stream.current()))\n          return \"def\";\n      }\n    },\n    namespaceSeparator: \"::\",\n    modeProps: {fold: [\"brace\", \"include\"]}\n  });\n\n  def(\"text/x-java\", {\n    name: \"clike\",\n    keywords: words(\"abstract assert break case catch class const continue default \" +\n                    \"do else enum extends final finally float for goto if implements import \" +\n                    \"instanceof interface native new package private protected public \" +\n                    \"return static strictfp super switch synchronized this throw throws transient \" +\n                    \"try volatile while @interface\"),\n    types: words(\"byte short int long float double boolean char void Boolean Byte Character Double Float \" +\n                 \"Integer Long Number Object Short String StringBuffer StringBuilder Void\"),\n    blockKeywords: words(\"catch class do else finally for if switch try while\"),\n    defKeywords: words(\"class interface enum @interface\"),\n    typeFirstDefinitions: true,\n    atoms: words(\"true false null\"),\n    number: /^(?:0x[a-f\\d_]+|0b[01_]+|(?:[\\d_]+\\.?\\d*|\\.\\d+)(?:e[-+]?[\\d_]+)?)(u|ll?|l|f)?/i,\n    hooks: {\n      \"@\": function(stream) {\n        // Don't match the @interface keyword.\n        if (stream.match('interface', false)) return false;\n\n        stream.eatWhile(/[\\w\\$_]/);\n        return \"meta\";\n      }\n    },\n    modeProps: {fold: [\"brace\", \"import\"]}\n  });\n\n  def(\"text/x-csharp\", {\n    name: \"clike\",\n    keywords: words(\"abstract as async await base break case catch checked class const continue\" +\n                    \" default delegate do else enum event explicit extern finally fixed for\" +\n                    \" foreach goto if implicit in interface internal is lock namespace new\" +\n                    \" operator out override params private protected public readonly ref return sealed\" +\n                    \" sizeof stackalloc static struct switch this throw try typeof unchecked\" +\n                    \" unsafe using virtual void volatile while add alias ascending descending dynamic from get\" +\n                    \" global group into join let orderby partial remove select set value var yield\"),\n    types: words(\"Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func\" +\n                 \" Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32\" +\n                 \" UInt64 bool byte char decimal double short int long object\"  +\n                 \" sbyte float string ushort uint ulong\"),\n    blockKeywords: words(\"catch class do else finally for foreach if struct switch try while\"),\n    defKeywords: words(\"class interface namespace struct var\"),\n    typeFirstDefinitions: true,\n    atoms: words(\"true false null\"),\n    hooks: {\n      \"@\": function(stream, state) {\n        if (stream.eat('\"')) {\n          state.tokenize = tokenAtString;\n          return tokenAtString(stream, state);\n        }\n        stream.eatWhile(/[\\w\\$_]/);\n        return \"meta\";\n      }\n    }\n  });\n\n  function tokenTripleString(stream, state) {\n    var escaped = false;\n    while (!stream.eol()) {\n      if (!escaped && stream.match('\"\"\"')) {\n        state.tokenize = null;\n        break;\n      }\n      escaped = stream.next() == \"\\\\\" && !escaped;\n    }\n    return \"string\";\n  }\n\n  function tokenNestedComment(depth) {\n    return function (stream, state) {\n      var ch\n      while (ch = stream.next()) {\n        if (ch == \"*\" && stream.eat(\"/\")) {\n          if (depth == 1) {\n            state.tokenize = null\n            break\n          } else {\n            state.tokenize = tokenNestedComment(depth - 1)\n            return state.tokenize(stream, state)\n          }\n        } else if (ch == \"/\" && stream.eat(\"*\")) {\n          state.tokenize = tokenNestedComment(depth + 1)\n          return state.tokenize(stream, state)\n        }\n      }\n      return \"comment\"\n    }\n  }\n\n  def(\"text/x-scala\", {\n    name: \"clike\",\n    keywords: words(\n      /* scala */\n      \"abstract case catch class def do else extends final finally for forSome if \" +\n      \"implicit import lazy match new null object override package private protected return \" +\n      \"sealed super this throw trait try type val var while with yield _ \" +\n\n      /* package scala */\n      \"assert assume require print println printf readLine readBoolean readByte readShort \" +\n      \"readChar readInt readLong readFloat readDouble\"\n    ),\n    types: words(\n      \"AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either \" +\n      \"Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable \" +\n      \"Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering \" +\n      \"Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder \" +\n      \"StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector \" +\n\n      /* package java.lang */\n      \"Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable \" +\n      \"Compiler Double Exception Float Integer Long Math Number Object Package Pair Process \" +\n      \"Runtime Runnable SecurityManager Short StackTraceElement StrictMath String \" +\n      \"StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void\"\n    ),\n    multiLineStrings: true,\n    blockKeywords: words(\"catch class enum do else finally for forSome if match switch try while\"),\n    defKeywords: words(\"class enum def object package trait type val var\"),\n    atoms: words(\"true false null\"),\n    indentStatements: false,\n    indentSwitch: false,\n    isOperatorChar: /[+\\-*&%=<>!?|\\/#:@]/,\n    hooks: {\n      \"@\": function(stream) {\n        stream.eatWhile(/[\\w\\$_]/);\n        return \"meta\";\n      },\n      '\"': function(stream, state) {\n        if (!stream.match('\"\"')) return false;\n        state.tokenize = tokenTripleString;\n        return state.tokenize(stream, state);\n      },\n      \"'\": function(stream) {\n        stream.eatWhile(/[\\w\\$_\\xa1-\\uffff]/);\n        return \"atom\";\n      },\n      \"=\": function(stream, state) {\n        var cx = state.context\n        if (cx.type == \"}\" && cx.align && stream.eat(\">\")) {\n          state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev)\n          return \"operator\"\n        } else {\n          return false\n        }\n      },\n\n      \"/\": function(stream, state) {\n        if (!stream.eat(\"*\")) return false\n        state.tokenize = tokenNestedComment(1)\n        return state.tokenize(stream, state)\n      }\n    },\n    modeProps: {closeBrackets: {pairs: '()[]{}\"\"', triples: '\"'}}\n  });\n\n  function tokenKotlinString(tripleString){\n    return function (stream, state) {\n      var escaped = false, next, end = false;\n      while (!stream.eol()) {\n        if (!tripleString && !escaped && stream.match('\"') ) {end = true; break;}\n        if (tripleString && stream.match('\"\"\"')) {end = true; break;}\n        next = stream.next();\n        if(!escaped && next == \"$\" && stream.match('{'))\n          stream.skipTo(\"}\");\n        escaped = !escaped && next == \"\\\\\" && !tripleString;\n      }\n      if (end || !tripleString)\n        state.tokenize = null;\n      return \"string\";\n    }\n  }\n\n  def(\"text/x-kotlin\", {\n    name: \"clike\",\n    keywords: words(\n      /*keywords*/\n      \"package as typealias class interface this super val operator \" +\n      \"var fun for is in This throw return annotation \" +\n      \"break continue object if else while do try when !in !is as? \" +\n\n      /*soft keywords*/\n      \"file import where by get set abstract enum open inner override private public internal \" +\n      \"protected catch finally out final vararg reified dynamic companion constructor init \" +\n      \"sealed field property receiver param sparam lateinit data inline noinline tailrec \" +\n      \"external annotation crossinline const operator infix suspend actual expect setparam\"\n    ),\n    types: words(\n      /* package java.lang */\n      \"Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable \" +\n      \"Compiler Double Exception Float Integer Long Math Number Object Package Pair Process \" +\n      \"Runtime Runnable SecurityManager Short StackTraceElement StrictMath String \" +\n      \"StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void Annotation Any BooleanArray \" +\n      \"ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy \" +\n      \"LazyThreadSafetyMode LongArray Nothing ShortArray Unit\"\n    ),\n    intendSwitch: false,\n    indentStatements: false,\n    multiLineStrings: true,\n    number: /^(?:0x[a-f\\d_]+|0b[01_]+|(?:[\\d_]+(\\.\\d+)?|\\.\\d+)(?:e[-+]?[\\d_]+)?)(u|ll?|l|f)?/i,\n    blockKeywords: words(\"catch class do else finally for if where try while enum\"),\n    defKeywords: words(\"class val var object interface fun\"),\n    atoms: words(\"true false null this\"),\n    hooks: {\n      \"@\": function(stream) {\n        stream.eatWhile(/[\\w\\$_]/);\n        return \"meta\";\n      },\n      '\"': function(stream, state) {\n        state.tokenize = tokenKotlinString(stream.match('\"\"'));\n        return state.tokenize(stream, state);\n      },\n      indent: function(state, ctx, textAfter, indentUnit) {\n        var firstChar = textAfter && textAfter.charAt(0);\n        if ((state.prevToken == \"}\" || state.prevToken == \")\") && textAfter == \"\")\n          return state.indented;\n        if (state.prevToken == \"operator\" && textAfter != \"}\" ||\n          state.prevToken == \"variable\" && firstChar == \".\" ||\n          (state.prevToken == \"}\" || state.prevToken == \")\") && firstChar == \".\")\n          return indentUnit * 2 + ctx.indented;\n        if (ctx.align && ctx.type == \"}\")\n          return ctx.indented + (state.context.type == (textAfter || \"\").charAt(0) ? 0 : indentUnit);\n      }\n    },\n    modeProps: {closeBrackets: {triples: '\"'}}\n  });\n\n  def([\"x-shader/x-vertex\", \"x-shader/x-fragment\"], {\n    name: \"clike\",\n    keywords: words(\"sampler1D sampler2D sampler3D samplerCube \" +\n                    \"sampler1DShadow sampler2DShadow \" +\n                    \"const attribute uniform varying \" +\n                    \"break continue discard return \" +\n                    \"for while do if else struct \" +\n                    \"in out inout\"),\n    types: words(\"float int bool void \" +\n                 \"vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 \" +\n                 \"mat2 mat3 mat4\"),\n    blockKeywords: words(\"for while do if else struct\"),\n    builtin: words(\"radians degrees sin cos tan asin acos atan \" +\n                    \"pow exp log exp2 sqrt inversesqrt \" +\n                    \"abs sign floor ceil fract mod min max clamp mix step smoothstep \" +\n                    \"length distance dot cross normalize ftransform faceforward \" +\n                    \"reflect refract matrixCompMult \" +\n                    \"lessThan lessThanEqual greaterThan greaterThanEqual \" +\n                    \"equal notEqual any all not \" +\n                    \"texture1D texture1DProj texture1DLod texture1DProjLod \" +\n                    \"texture2D texture2DProj texture2DLod texture2DProjLod \" +\n                    \"texture3D texture3DProj texture3DLod texture3DProjLod \" +\n                    \"textureCube textureCubeLod \" +\n                    \"shadow1D shadow2D shadow1DProj shadow2DProj \" +\n                    \"shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod \" +\n                    \"dFdx dFdy fwidth \" +\n                    \"noise1 noise2 noise3 noise4\"),\n    atoms: words(\"true false \" +\n                \"gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex \" +\n                \"gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 \" +\n                \"gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 \" +\n                \"gl_FogCoord gl_PointCoord \" +\n                \"gl_Position gl_PointSize gl_ClipVertex \" +\n                \"gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor \" +\n                \"gl_TexCoord gl_FogFragCoord \" +\n                \"gl_FragCoord gl_FrontFacing \" +\n                \"gl_FragData gl_FragDepth \" +\n                \"gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix \" +\n                \"gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse \" +\n                \"gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse \" +\n                \"gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose \" +\n                \"gl_ProjectionMatrixInverseTranspose \" +\n                \"gl_ModelViewProjectionMatrixInverseTranspose \" +\n                \"gl_TextureMatrixInverseTranspose \" +\n                \"gl_NormalScale gl_DepthRange gl_ClipPlane \" +\n                \"gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel \" +\n                \"gl_FrontLightModelProduct gl_BackLightModelProduct \" +\n                \"gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ \" +\n                \"gl_FogParameters \" +\n                \"gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords \" +\n                \"gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats \" +\n                \"gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits \" +\n                \"gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits \" +\n                \"gl_MaxDrawBuffers\"),\n    indentSwitch: false,\n    hooks: {\"#\": cppHook},\n    modeProps: {fold: [\"brace\", \"include\"]}\n  });\n\n  def(\"text/x-nesc\", {\n    name: \"clike\",\n    keywords: words(cKeywords + \" as atomic async call command component components configuration event generic \" +\n                    \"implementation includes interface module new norace nx_struct nx_union post provides \" +\n                    \"signal task uses abstract extends\"),\n    types: cTypes,\n    blockKeywords: words(cBlockKeywords),\n    atoms: words(\"null true false\"),\n    hooks: {\"#\": cppHook},\n    modeProps: {fold: [\"brace\", \"include\"]}\n  });\n\n  def(\"text/x-objectivec\", {\n    name: \"clike\",\n    keywords: words(cKeywords + \" bycopy byref in inout oneway out self super atomic nonatomic retain copy \" +\n                    \"readwrite readonly strong weak assign typeof nullable nonnull null_resettable _cmd \" +\n                    \"@interface @implementation @end @protocol @encode @property @synthesize @dynamic @class \" +\n                    \"@public @package @private @protected @required @optional @try @catch @finally @import \" +\n                    \"@selector @encode @defs @synchronized @autoreleasepool @compatibility_alias @available\"),\n    types: objCTypes,\n    builtin: words(\"FOUNDATION_EXPORT FOUNDATION_EXTERN NS_INLINE NS_FORMAT_FUNCTION NS_RETURNS_RETAINED \" +\n                   \"NS_ERROR_ENUM NS_RETURNS_NOT_RETAINED NS_RETURNS_INNER_POINTER NS_DESIGNATED_INITIALIZER \" +\n                   \"NS_ENUM NS_OPTIONS NS_REQUIRES_NIL_TERMINATION NS_ASSUME_NONNULL_BEGIN \" +\n                   \"NS_ASSUME_NONNULL_END NS_SWIFT_NAME NS_REFINED_FOR_SWIFT\"),\n    blockKeywords: words(cBlockKeywords + \" @synthesize @try @catch @finally @autoreleasepool @synchronized\"),\n    defKeywords: words(cDefKeywords + \" @interface @implementation @protocol @class\"),\n    dontIndentStatements: /^@.*$/,\n    typeFirstDefinitions: true,\n    atoms: words(\"YES NO NULL Nil nil true false nullptr\"),\n    isReservedIdentifier: cIsReservedIdentifier,\n    hooks: {\n      \"#\": cppHook,\n      \"*\": pointerHook,\n    },\n    modeProps: {fold: [\"brace\", \"include\"]}\n  });\n\n  def(\"text/x-squirrel\", {\n    name: \"clike\",\n    keywords: words(\"base break clone continue const default delete enum extends function in class\" +\n                    \" foreach local resume return this throw typeof yield constructor instanceof static\"),\n    types: cTypes,\n    blockKeywords: words(\"case catch class else for foreach if switch try while\"),\n    defKeywords: words(\"function local class\"),\n    typeFirstDefinitions: true,\n    atoms: words(\"true false null\"),\n    hooks: {\"#\": cppHook},\n    modeProps: {fold: [\"brace\", \"include\"]}\n  });\n\n  // Ceylon Strings need to deal with interpolation\n  var stringTokenizer = null;\n  function tokenCeylonString(type) {\n    return function(stream, state) {\n      var escaped = false, next, end = false;\n      while (!stream.eol()) {\n        if (!escaped && stream.match('\"') &&\n              (type == \"single\" || stream.match('\"\"'))) {\n          end = true;\n          break;\n        }\n        if (!escaped && stream.match('``')) {\n          stringTokenizer = tokenCeylonString(type);\n          end = true;\n          break;\n        }\n        next = stream.next();\n        escaped = type == \"single\" && !escaped && next == \"\\\\\";\n      }\n      if (end)\n          state.tokenize = null;\n      return \"string\";\n    }\n  }\n\n  def(\"text/x-ceylon\", {\n    name: \"clike\",\n    keywords: words(\"abstracts alias assembly assert assign break case catch class continue dynamic else\" +\n                    \" exists extends finally for function given if import in interface is let module new\" +\n                    \" nonempty object of out outer package return satisfies super switch then this throw\" +\n                    \" try value void while\"),\n    types: function(word) {\n        // In Ceylon all identifiers that start with an uppercase are types\n        var first = word.charAt(0);\n        return (first === first.toUpperCase() && first !== first.toLowerCase());\n    },\n    blockKeywords: words(\"case catch class dynamic else finally for function if interface module new object switch try while\"),\n    defKeywords: words(\"class dynamic function interface module object package value\"),\n    builtin: words(\"abstract actual aliased annotation by default deprecated doc final formal late license\" +\n                   \" native optional sealed see serializable shared suppressWarnings tagged throws variable\"),\n    isPunctuationChar: /[\\[\\]{}\\(\\),;\\:\\.`]/,\n    isOperatorChar: /[+\\-*&%=<>!?|^~:\\/]/,\n    numberStart: /[\\d#$]/,\n    number: /^(?:#[\\da-fA-F_]+|\\$[01_]+|[\\d_]+[kMGTPmunpf]?|[\\d_]+\\.[\\d_]+(?:[eE][-+]?\\d+|[kMGTPmunpf]|)|)/i,\n    multiLineStrings: true,\n    typeFirstDefinitions: true,\n    atoms: words(\"true false null larger smaller equal empty finished\"),\n    indentSwitch: false,\n    styleDefs: false,\n    hooks: {\n      \"@\": function(stream) {\n        stream.eatWhile(/[\\w\\$_]/);\n        return \"meta\";\n      },\n      '\"': function(stream, state) {\n          state.tokenize = tokenCeylonString(stream.match('\"\"') ? \"triple\" : \"single\");\n          return state.tokenize(stream, state);\n        },\n      '`': function(stream, state) {\n          if (!stringTokenizer || !stream.match('`')) return false;\n          state.tokenize = stringTokenizer;\n          stringTokenizer = null;\n          return state.tokenize(stream, state);\n        },\n      \"'\": function(stream) {\n        stream.eatWhile(/[\\w\\$_\\xa1-\\uffff]/);\n        return \"atom\";\n      },\n      token: function(_stream, state, style) {\n          if ((style == \"variable\" || style == \"type\") &&\n              state.prevToken == \".\") {\n            return \"variable-2\";\n          }\n        }\n    },\n    modeProps: {\n        fold: [\"brace\", \"import\"],\n        closeBrackets: {triples: '\"'}\n    }\n  });\n\n});\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/mode/javascript/javascript.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"javascript\", function(config, parserConfig) {\n  var indentUnit = config.indentUnit;\n  var statementIndent = parserConfig.statementIndent;\n  var jsonldMode = parserConfig.jsonld;\n  var jsonMode = parserConfig.json || jsonldMode;\n  var isTS = parserConfig.typescript;\n  var wordRE = parserConfig.wordCharacters || /[\\w$\\xa1-\\uffff]/;\n\n  // Tokenizer\n\n  var keywords = function(){\n    function kw(type) {return {type: type, style: \"keyword\"};}\n    var A = kw(\"keyword a\"), B = kw(\"keyword b\"), C = kw(\"keyword c\"), D = kw(\"keyword d\");\n    var operator = kw(\"operator\"), atom = {type: \"atom\", style: \"atom\"};\n\n    return {\n      \"if\": kw(\"if\"), \"while\": A, \"with\": A, \"else\": B, \"do\": B, \"try\": B, \"finally\": B,\n      \"return\": D, \"break\": D, \"continue\": D, \"new\": kw(\"new\"), \"delete\": C, \"void\": C, \"throw\": C,\n      \"debugger\": kw(\"debugger\"), \"var\": kw(\"var\"), \"const\": kw(\"var\"), \"let\": kw(\"var\"),\n      \"function\": kw(\"function\"), \"catch\": kw(\"catch\"),\n      \"for\": kw(\"for\"), \"switch\": kw(\"switch\"), \"case\": kw(\"case\"), \"default\": kw(\"default\"),\n      \"in\": operator, \"typeof\": operator, \"instanceof\": operator,\n      \"true\": atom, \"false\": atom, \"null\": atom, \"undefined\": atom, \"NaN\": atom, \"Infinity\": atom,\n      \"this\": kw(\"this\"), \"class\": kw(\"class\"), \"super\": kw(\"atom\"),\n      \"yield\": C, \"export\": kw(\"export\"), \"import\": kw(\"import\"), \"extends\": C,\n      \"await\": C\n    };\n  }();\n\n  var isOperatorChar = /[+\\-*&%=<>!?|~^@]/;\n  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)\"/;\n\n  function readRegexp(stream) {\n    var escaped = false, next, inSet = false;\n    while ((next = stream.next()) != null) {\n      if (!escaped) {\n        if (next == \"/\" && !inSet) return;\n        if (next == \"[\") inSet = true;\n        else if (inSet && next == \"]\") inSet = false;\n      }\n      escaped = !escaped && next == \"\\\\\";\n    }\n  }\n\n  // Used as scratch variables to communicate multiple values without\n  // consing up tons of objects.\n  var type, content;\n  function ret(tp, style, cont) {\n    type = tp; content = cont;\n    return style;\n  }\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n    if (ch == '\"' || ch == \"'\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    } else if (ch == \".\" && stream.match(/^\\d+(?:[eE][+\\-]?\\d+)?/)) {\n      return ret(\"number\", \"number\");\n    } else if (ch == \".\" && stream.match(\"..\")) {\n      return ret(\"spread\", \"meta\");\n    } else if (/[\\[\\]{}\\(\\),;\\:\\.]/.test(ch)) {\n      return ret(ch);\n    } else if (ch == \"=\" && stream.eat(\">\")) {\n      return ret(\"=>\", \"operator\");\n    } else if (ch == \"0\" && stream.match(/^(?:x[\\da-f]+|o[0-7]+|b[01]+)n?/i)) {\n      return ret(\"number\", \"number\");\n    } else if (/\\d/.test(ch)) {\n      stream.match(/^\\d*(?:n|(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)?/);\n      return ret(\"number\", \"number\");\n    } else if (ch == \"/\") {\n      if (stream.eat(\"*\")) {\n        state.tokenize = tokenComment;\n        return tokenComment(stream, state);\n      } else if (stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return ret(\"comment\", \"comment\");\n      } else if (expressionAllowed(stream, state, 1)) {\n        readRegexp(stream);\n        stream.match(/^\\b(([gimyus])(?![gimyus]*\\2))+\\b/);\n        return ret(\"regexp\", \"string-2\");\n      } else {\n        stream.eat(\"=\");\n        return ret(\"operator\", \"operator\", stream.current());\n      }\n    } else if (ch == \"`\") {\n      state.tokenize = tokenQuasi;\n      return tokenQuasi(stream, state);\n    } else if (ch == \"#\") {\n      stream.skipToEnd();\n      return ret(\"error\", \"error\");\n    } else if (isOperatorChar.test(ch)) {\n      if (ch != \">\" || !state.lexical || state.lexical.type != \">\") {\n        if (stream.eat(\"=\")) {\n          if (ch == \"!\" || ch == \"=\") stream.eat(\"=\")\n        } else if (/[<>*+\\-]/.test(ch)) {\n          stream.eat(ch)\n          if (ch == \">\") stream.eat(ch)\n        }\n      }\n      return ret(\"operator\", \"operator\", stream.current());\n    } else if (wordRE.test(ch)) {\n      stream.eatWhile(wordRE);\n      var word = stream.current()\n      if (state.lastType != \".\") {\n        if (keywords.propertyIsEnumerable(word)) {\n          var kw = keywords[word]\n          return ret(kw.type, kw.style, word)\n        }\n        if (word == \"async\" && stream.match(/^(\\s|\\/\\*.*?\\*\\/)*[\\[\\(\\w]/, false))\n          return ret(\"async\", \"keyword\", word)\n      }\n      return ret(\"variable\", \"variable\", word)\n    }\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, next;\n      if (jsonldMode && stream.peek() == \"@\" && stream.match(isJsonldKeyword)){\n        state.tokenize = tokenBase;\n        return ret(\"jsonld-keyword\", \"meta\");\n      }\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) break;\n        escaped = !escaped && next == \"\\\\\";\n      }\n      if (!escaped) state.tokenize = tokenBase;\n      return ret(\"string\", \"string\");\n    };\n  }\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return ret(\"comment\", \"comment\");\n  }\n\n  function tokenQuasi(stream, state) {\n    var escaped = false, next;\n    while ((next = stream.next()) != null) {\n      if (!escaped && (next == \"`\" || next == \"$\" && stream.eat(\"{\"))) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      escaped = !escaped && next == \"\\\\\";\n    }\n    return ret(\"quasi\", \"string-2\", stream.current());\n  }\n\n  var brackets = \"([{}])\";\n  // This is a crude lookahead trick to try and notice that we're\n  // parsing the argument patterns for a fat-arrow function before we\n  // actually hit the arrow token. It only works if the arrow is on\n  // the same line as the arguments and there's no strange noise\n  // (comments) in between. Fallback is to only notice when we hit the\n  // arrow, and not declare the arguments as locals for the arrow\n  // body.\n  function findFatArrow(stream, state) {\n    if (state.fatArrowAt) state.fatArrowAt = null;\n    var arrow = stream.string.indexOf(\"=>\", stream.start);\n    if (arrow < 0) return;\n\n    if (isTS) { // Try to skip TypeScript return type declarations after the arguments\n      var m = /:\\s*(?:\\w+(?:<[^>]*>|\\[\\])?|\\{[^}]*\\})\\s*$/.exec(stream.string.slice(stream.start, arrow))\n      if (m) arrow = m.index\n    }\n\n    var depth = 0, sawSomething = false;\n    for (var pos = arrow - 1; pos >= 0; --pos) {\n      var ch = stream.string.charAt(pos);\n      var bracket = brackets.indexOf(ch);\n      if (bracket >= 0 && bracket < 3) {\n        if (!depth) { ++pos; break; }\n        if (--depth == 0) { if (ch == \"(\") sawSomething = true; break; }\n      } else if (bracket >= 3 && bracket < 6) {\n        ++depth;\n      } else if (wordRE.test(ch)) {\n        sawSomething = true;\n      } else if (/[\"'\\/]/.test(ch)) {\n        return;\n      } else if (sawSomething && !depth) {\n        ++pos;\n        break;\n      }\n    }\n    if (sawSomething && !depth) state.fatArrowAt = pos;\n  }\n\n  // Parser\n\n  var atomicTypes = {\"atom\": true, \"number\": true, \"variable\": true, \"string\": true, \"regexp\": true, \"this\": true, \"jsonld-keyword\": true};\n\n  function JSLexical(indented, column, type, align, prev, info) {\n    this.indented = indented;\n    this.column = column;\n    this.type = type;\n    this.prev = prev;\n    this.info = info;\n    if (align != null) this.align = align;\n  }\n\n  function inScope(state, varname) {\n    for (var v = state.localVars; v; v = v.next)\n      if (v.name == varname) return true;\n    for (var cx = state.context; cx; cx = cx.prev) {\n      for (var v = cx.vars; v; v = v.next)\n        if (v.name == varname) return true;\n    }\n  }\n\n  function parseJS(state, style, type, content, stream) {\n    var cc = state.cc;\n    // Communicate our context to the combinators.\n    // (Less wasteful than consing up a hundred closures on every call.)\n    cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;\n\n    if (!state.lexical.hasOwnProperty(\"align\"))\n      state.lexical.align = true;\n\n    while(true) {\n      var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;\n      if (combinator(type, content)) {\n        while(cc.length && cc[cc.length - 1].lex)\n          cc.pop()();\n        if (cx.marked) return cx.marked;\n        if (type == \"variable\" && inScope(state, content)) return \"variable-2\";\n        return style;\n      }\n    }\n  }\n\n  // Combinator utils\n\n  var cx = {state: null, column: null, marked: null, cc: null};\n  function pass() {\n    for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);\n  }\n  function cont() {\n    pass.apply(null, arguments);\n    return true;\n  }\n  function inList(name, list) {\n    for (var v = list; v; v = v.next) if (v.name == name) return true\n    return false;\n  }\n  function register(varname) {\n    var state = cx.state;\n    cx.marked = \"def\";\n    if (state.context) {\n      if (state.lexical.info == \"var\" && state.context && state.context.block) {\n        // FIXME function decls are also not block scoped\n        var newContext = registerVarScoped(varname, state.context)\n        if (newContext != null) {\n          state.context = newContext\n          return\n        }\n      } else if (!inList(varname, state.localVars)) {\n        state.localVars = new Var(varname, state.localVars)\n        return\n      }\n    }\n    // Fall through means this is global\n    if (parserConfig.globalVars && !inList(varname, state.globalVars))\n      state.globalVars = new Var(varname, state.globalVars)\n  }\n  function registerVarScoped(varname, context) {\n    if (!context) {\n      return null\n    } else if (context.block) {\n      var inner = registerVarScoped(varname, context.prev)\n      if (!inner) return null\n      if (inner == context.prev) return context\n      return new Context(inner, context.vars, true)\n    } else if (inList(varname, context.vars)) {\n      return context\n    } else {\n      return new Context(context.prev, new Var(varname, context.vars), false)\n    }\n  }\n\n  function isModifier(name) {\n    return name == \"public\" || name == \"private\" || name == \"protected\" || name == \"abstract\" || name == \"readonly\"\n  }\n\n  // Combinators\n\n  function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }\n  function Var(name, next) { this.name = name; this.next = next }\n\n  var defaultVars = new Var(\"this\", new Var(\"arguments\", null))\n  function pushcontext() {\n    cx.state.context = new Context(cx.state.context, cx.state.localVars, false)\n    cx.state.localVars = defaultVars\n  }\n  function pushblockcontext() {\n    cx.state.context = new Context(cx.state.context, cx.state.localVars, true)\n    cx.state.localVars = null\n  }\n  function popcontext() {\n    cx.state.localVars = cx.state.context.vars\n    cx.state.context = cx.state.context.prev\n  }\n  popcontext.lex = true\n  function pushlex(type, info) {\n    var result = function() {\n      var state = cx.state, indent = state.indented;\n      if (state.lexical.type == \"stat\") indent = state.lexical.indented;\n      else for (var outer = state.lexical; outer && outer.type == \")\" && outer.align; outer = outer.prev)\n        indent = outer.indented;\n      state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);\n    };\n    result.lex = true;\n    return result;\n  }\n  function poplex() {\n    var state = cx.state;\n    if (state.lexical.prev) {\n      if (state.lexical.type == \")\")\n        state.indented = state.lexical.indented;\n      state.lexical = state.lexical.prev;\n    }\n  }\n  poplex.lex = true;\n\n  function expect(wanted) {\n    function exp(type) {\n      if (type == wanted) return cont();\n      else if (wanted == \";\" || type == \"}\" || type == \")\" || type == \"]\") return pass();\n      else return cont(exp);\n    };\n    return exp;\n  }\n\n  function statement(type, value) {\n    if (type == \"var\") return cont(pushlex(\"vardef\", value), vardef, expect(\";\"), poplex);\n    if (type == \"keyword a\") return cont(pushlex(\"form\"), parenExpr, statement, poplex);\n    if (type == \"keyword b\") return cont(pushlex(\"form\"), statement, poplex);\n    if (type == \"keyword d\") return cx.stream.match(/^\\s*$/, false) ? cont() : cont(pushlex(\"stat\"), maybeexpression, expect(\";\"), poplex);\n    if (type == \"debugger\") return cont(expect(\";\"));\n    if (type == \"{\") return cont(pushlex(\"}\"), pushblockcontext, block, poplex, popcontext);\n    if (type == \";\") return cont();\n    if (type == \"if\") {\n      if (cx.state.lexical.info == \"else\" && cx.state.cc[cx.state.cc.length - 1] == poplex)\n        cx.state.cc.pop()();\n      return cont(pushlex(\"form\"), parenExpr, statement, poplex, maybeelse);\n    }\n    if (type == \"function\") return cont(functiondef);\n    if (type == \"for\") return cont(pushlex(\"form\"), forspec, statement, poplex);\n    if (type == \"class\" || (isTS && value == \"interface\")) { cx.marked = \"keyword\"; return cont(pushlex(\"form\"), className, poplex); }\n    if (type == \"variable\") {\n      if (isTS && value == \"declare\") {\n        cx.marked = \"keyword\"\n        return cont(statement)\n      } else if (isTS && (value == \"module\" || value == \"enum\" || value == \"type\") && cx.stream.match(/^\\s*\\w/, false)) {\n        cx.marked = \"keyword\"\n        if (value == \"enum\") return cont(enumdef);\n        else if (value == \"type\") return cont(typeexpr, expect(\"operator\"), typeexpr, expect(\";\"));\n        else return cont(pushlex(\"form\"), pattern, expect(\"{\"), pushlex(\"}\"), block, poplex, poplex)\n      } else if (isTS && value == \"namespace\") {\n        cx.marked = \"keyword\"\n        return cont(pushlex(\"form\"), expression, block, poplex)\n      } else if (isTS && value == \"abstract\") {\n        cx.marked = \"keyword\"\n        return cont(statement)\n      } else {\n        return cont(pushlex(\"stat\"), maybelabel);\n      }\n    }\n    if (type == \"switch\") return cont(pushlex(\"form\"), parenExpr, expect(\"{\"), pushlex(\"}\", \"switch\"), pushblockcontext,\n                                      block, poplex, poplex, popcontext);\n    if (type == \"case\") return cont(expression, expect(\":\"));\n    if (type == \"default\") return cont(expect(\":\"));\n    if (type == \"catch\") return cont(pushlex(\"form\"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);\n    if (type == \"export\") return cont(pushlex(\"stat\"), afterExport, poplex);\n    if (type == \"import\") return cont(pushlex(\"stat\"), afterImport, poplex);\n    if (type == \"async\") return cont(statement)\n    if (value == \"@\") return cont(expression, statement)\n    return pass(pushlex(\"stat\"), expression, expect(\";\"), poplex);\n  }\n  function maybeCatchBinding(type) {\n    if (type == \"(\") return cont(funarg, expect(\")\"))\n  }\n  function expression(type, value) {\n    return expressionInner(type, value, false);\n  }\n  function expressionNoComma(type, value) {\n    return expressionInner(type, value, true);\n  }\n  function parenExpr(type) {\n    if (type != \"(\") return pass()\n    return cont(pushlex(\")\"), expression, expect(\")\"), poplex)\n  }\n  function expressionInner(type, value, noComma) {\n    if (cx.state.fatArrowAt == cx.stream.start) {\n      var body = noComma ? arrowBodyNoComma : arrowBody;\n      if (type == \"(\") return cont(pushcontext, pushlex(\")\"), commasep(funarg, \")\"), poplex, expect(\"=>\"), body, popcontext);\n      else if (type == \"variable\") return pass(pushcontext, pattern, expect(\"=>\"), body, popcontext);\n    }\n\n    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;\n    if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);\n    if (type == \"function\") return cont(functiondef, maybeop);\n    if (type == \"class\" || (isTS && value == \"interface\")) { cx.marked = \"keyword\"; return cont(pushlex(\"form\"), classExpression, poplex); }\n    if (type == \"keyword c\" || type == \"async\") return cont(noComma ? expressionNoComma : expression);\n    if (type == \"(\") return cont(pushlex(\")\"), maybeexpression, expect(\")\"), poplex, maybeop);\n    if (type == \"operator\" || type == \"spread\") return cont(noComma ? expressionNoComma : expression);\n    if (type == \"[\") return cont(pushlex(\"]\"), arrayLiteral, poplex, maybeop);\n    if (type == \"{\") return contCommasep(objprop, \"}\", null, maybeop);\n    if (type == \"quasi\") return pass(quasi, maybeop);\n    if (type == \"new\") return cont(maybeTarget(noComma));\n    if (type == \"import\") return cont(expression);\n    return cont();\n  }\n  function maybeexpression(type) {\n    if (type.match(/[;\\}\\)\\],]/)) return pass();\n    return pass(expression);\n  }\n\n  function maybeoperatorComma(type, value) {\n    if (type == \",\") return cont(expression);\n    return maybeoperatorNoComma(type, value, false);\n  }\n  function maybeoperatorNoComma(type, value, noComma) {\n    var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;\n    var expr = noComma == false ? expression : expressionNoComma;\n    if (type == \"=>\") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);\n    if (type == \"operator\") {\n      if (/\\+\\+|--/.test(value) || isTS && value == \"!\") return cont(me);\n      if (isTS && value == \"<\" && cx.stream.match(/^([^>]|<.*?>)*>\\s*\\(/, false))\n        return cont(pushlex(\">\"), commasep(typeexpr, \">\"), poplex, me);\n      if (value == \"?\") return cont(expression, expect(\":\"), expr);\n      return cont(expr);\n    }\n    if (type == \"quasi\") { return pass(quasi, me); }\n    if (type == \";\") return;\n    if (type == \"(\") return contCommasep(expressionNoComma, \")\", \"call\", me);\n    if (type == \".\") return cont(property, me);\n    if (type == \"[\") return cont(pushlex(\"]\"), maybeexpression, expect(\"]\"), poplex, me);\n    if (isTS && value == \"as\") { cx.marked = \"keyword\"; return cont(typeexpr, me) }\n    if (type == \"regexp\") {\n      cx.state.lastType = cx.marked = \"operator\"\n      cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)\n      return cont(expr)\n    }\n  }\n  function quasi(type, value) {\n    if (type != \"quasi\") return pass();\n    if (value.slice(value.length - 2) != \"${\") return cont(quasi);\n    return cont(expression, continueQuasi);\n  }\n  function continueQuasi(type) {\n    if (type == \"}\") {\n      cx.marked = \"string-2\";\n      cx.state.tokenize = tokenQuasi;\n      return cont(quasi);\n    }\n  }\n  function arrowBody(type) {\n    findFatArrow(cx.stream, cx.state);\n    return pass(type == \"{\" ? statement : expression);\n  }\n  function arrowBodyNoComma(type) {\n    findFatArrow(cx.stream, cx.state);\n    return pass(type == \"{\" ? statement : expressionNoComma);\n  }\n  function maybeTarget(noComma) {\n    return function(type) {\n      if (type == \".\") return cont(noComma ? targetNoComma : target);\n      else if (type == \"variable\" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)\n      else return pass(noComma ? expressionNoComma : expression);\n    };\n  }\n  function target(_, value) {\n    if (value == \"target\") { cx.marked = \"keyword\"; return cont(maybeoperatorComma); }\n  }\n  function targetNoComma(_, value) {\n    if (value == \"target\") { cx.marked = \"keyword\"; return cont(maybeoperatorNoComma); }\n  }\n  function maybelabel(type) {\n    if (type == \":\") return cont(poplex, statement);\n    return pass(maybeoperatorComma, expect(\";\"), poplex);\n  }\n  function property(type) {\n    if (type == \"variable\") {cx.marked = \"property\"; return cont();}\n  }\n  function objprop(type, value) {\n    if (type == \"async\") {\n      cx.marked = \"property\";\n      return cont(objprop);\n    } else if (type == \"variable\" || cx.style == \"keyword\") {\n      cx.marked = \"property\";\n      if (value == \"get\" || value == \"set\") return cont(getterSetter);\n      var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params\n      if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\\s*:\\s*/, false)))\n        cx.state.fatArrowAt = cx.stream.pos + m[0].length\n      return cont(afterprop);\n    } else if (type == \"number\" || type == \"string\") {\n      cx.marked = jsonldMode ? \"property\" : (cx.style + \" property\");\n      return cont(afterprop);\n    } else if (type == \"jsonld-keyword\") {\n      return cont(afterprop);\n    } else if (isTS && isModifier(value)) {\n      cx.marked = \"keyword\"\n      return cont(objprop)\n    } else if (type == \"[\") {\n      return cont(expression, maybetype, expect(\"]\"), afterprop);\n    } else if (type == \"spread\") {\n      return cont(expressionNoComma, afterprop);\n    } else if (value == \"*\") {\n      cx.marked = \"keyword\";\n      return cont(objprop);\n    } else if (type == \":\") {\n      return pass(afterprop)\n    }\n  }\n  function getterSetter(type) {\n    if (type != \"variable\") return pass(afterprop);\n    cx.marked = \"property\";\n    return cont(functiondef);\n  }\n  function afterprop(type) {\n    if (type == \":\") return cont(expressionNoComma);\n    if (type == \"(\") return pass(functiondef);\n  }\n  function commasep(what, end, sep) {\n    function proceed(type, value) {\n      if (sep ? sep.indexOf(type) > -1 : type == \",\") {\n        var lex = cx.state.lexical;\n        if (lex.info == \"call\") lex.pos = (lex.pos || 0) + 1;\n        return cont(function(type, value) {\n          if (type == end || value == end) return pass()\n          return pass(what)\n        }, proceed);\n      }\n      if (type == end || value == end) return cont();\n      return cont(expect(end));\n    }\n    return function(type, value) {\n      if (type == end || value == end) return cont();\n      return pass(what, proceed);\n    };\n  }\n  function contCommasep(what, end, info) {\n    for (var i = 3; i < arguments.length; i++)\n      cx.cc.push(arguments[i]);\n    return cont(pushlex(end, info), commasep(what, end), poplex);\n  }\n  function block(type) {\n    if (type == \"}\") return cont();\n    return pass(statement, block);\n  }\n  function maybetype(type, value) {\n    if (isTS) {\n      if (type == \":\") return cont(typeexpr);\n      if (value == \"?\") return cont(maybetype);\n    }\n  }\n  function mayberettype(type) {\n    if (isTS && type == \":\") {\n      if (cx.stream.match(/^\\s*\\w+\\s+is\\b/, false)) return cont(expression, isKW, typeexpr)\n      else return cont(typeexpr)\n    }\n  }\n  function isKW(_, value) {\n    if (value == \"is\") {\n      cx.marked = \"keyword\"\n      return cont()\n    }\n  }\n  function typeexpr(type, value) {\n    if (value == \"keyof\" || value == \"typeof\") {\n      cx.marked = \"keyword\"\n      return cont(value == \"keyof\" ? typeexpr : expressionNoComma)\n    }\n    if (type == \"variable\" || value == \"void\") {\n      cx.marked = \"type\"\n      return cont(afterType)\n    }\n    if (type == \"string\" || type == \"number\" || type == \"atom\") return cont(afterType);\n    if (type == \"[\") return cont(pushlex(\"]\"), commasep(typeexpr, \"]\", \",\"), poplex, afterType)\n    if (type == \"{\") return cont(pushlex(\"}\"), commasep(typeprop, \"}\", \",;\"), poplex, afterType)\n    if (type == \"(\") return cont(commasep(typearg, \")\"), maybeReturnType)\n    if (type == \"<\") return cont(commasep(typeexpr, \">\"), typeexpr)\n  }\n  function maybeReturnType(type) {\n    if (type == \"=>\") return cont(typeexpr)\n  }\n  function typeprop(type, value) {\n    if (type == \"variable\" || cx.style == \"keyword\") {\n      cx.marked = \"property\"\n      return cont(typeprop)\n    } else if (value == \"?\") {\n      return cont(typeprop)\n    } else if (type == \":\") {\n      return cont(typeexpr)\n    } else if (type == \"[\") {\n      return cont(expression, maybetype, expect(\"]\"), typeprop)\n    }\n  }\n  function typearg(type, value) {\n    if (type == \"variable\" && cx.stream.match(/^\\s*[?:]/, false) || value == \"?\") return cont(typearg)\n    if (type == \":\") return cont(typeexpr)\n    return pass(typeexpr)\n  }\n  function afterType(type, value) {\n    if (value == \"<\") return cont(pushlex(\">\"), commasep(typeexpr, \">\"), poplex, afterType)\n    if (value == \"|\" || type == \".\" || value == \"&\") return cont(typeexpr)\n    if (type == \"[\") return cont(expect(\"]\"), afterType)\n    if (value == \"extends\" || value == \"implements\") { cx.marked = \"keyword\"; return cont(typeexpr) }\n  }\n  function maybeTypeArgs(_, value) {\n    if (value == \"<\") return cont(pushlex(\">\"), commasep(typeexpr, \">\"), poplex, afterType)\n  }\n  function typeparam() {\n    return pass(typeexpr, maybeTypeDefault)\n  }\n  function maybeTypeDefault(_, value) {\n    if (value == \"=\") return cont(typeexpr)\n  }\n  function vardef(_, value) {\n    if (value == \"enum\") {cx.marked = \"keyword\"; return cont(enumdef)}\n    return pass(pattern, maybetype, maybeAssign, vardefCont);\n  }\n  function pattern(type, value) {\n    if (isTS && isModifier(value)) { cx.marked = \"keyword\"; return cont(pattern) }\n    if (type == \"variable\") { register(value); return cont(); }\n    if (type == \"spread\") return cont(pattern);\n    if (type == \"[\") return contCommasep(eltpattern, \"]\");\n    if (type == \"{\") return contCommasep(proppattern, \"}\");\n  }\n  function proppattern(type, value) {\n    if (type == \"variable\" && !cx.stream.match(/^\\s*:/, false)) {\n      register(value);\n      return cont(maybeAssign);\n    }\n    if (type == \"variable\") cx.marked = \"property\";\n    if (type == \"spread\") return cont(pattern);\n    if (type == \"}\") return pass();\n    return cont(expect(\":\"), pattern, maybeAssign);\n  }\n  function eltpattern() {\n    return pass(pattern, maybeAssign)\n  }\n  function maybeAssign(_type, value) {\n    if (value == \"=\") return cont(expressionNoComma);\n  }\n  function vardefCont(type) {\n    if (type == \",\") return cont(vardef);\n  }\n  function maybeelse(type, value) {\n    if (type == \"keyword b\" && value == \"else\") return cont(pushlex(\"form\", \"else\"), statement, poplex);\n  }\n  function forspec(type, value) {\n    if (value == \"await\") return cont(forspec);\n    if (type == \"(\") return cont(pushlex(\")\"), forspec1, expect(\")\"), poplex);\n  }\n  function forspec1(type) {\n    if (type == \"var\") return cont(vardef, expect(\";\"), forspec2);\n    if (type == \";\") return cont(forspec2);\n    if (type == \"variable\") return cont(formaybeinof);\n    return pass(expression, expect(\";\"), forspec2);\n  }\n  function formaybeinof(_type, value) {\n    if (value == \"in\" || value == \"of\") { cx.marked = \"keyword\"; return cont(expression); }\n    return cont(maybeoperatorComma, forspec2);\n  }\n  function forspec2(type, value) {\n    if (type == \";\") return cont(forspec3);\n    if (value == \"in\" || value == \"of\") { cx.marked = \"keyword\"; return cont(expression); }\n    return pass(expression, expect(\";\"), forspec3);\n  }\n  function forspec3(type) {\n    if (type != \")\") cont(expression);\n  }\n  function functiondef(type, value) {\n    if (value == \"*\") {cx.marked = \"keyword\"; return cont(functiondef);}\n    if (type == \"variable\") {register(value); return cont(functiondef);}\n    if (type == \"(\") return cont(pushcontext, pushlex(\")\"), commasep(funarg, \")\"), poplex, mayberettype, statement, popcontext);\n    if (isTS && value == \"<\") return cont(pushlex(\">\"), commasep(typeparam, \">\"), poplex, functiondef)\n  }\n  function funarg(type, value) {\n    if (value == \"@\") cont(expression, funarg)\n    if (type == \"spread\") return cont(funarg);\n    if (isTS && isModifier(value)) { cx.marked = \"keyword\"; return cont(funarg); }\n    return pass(pattern, maybetype, maybeAssign);\n  }\n  function classExpression(type, value) {\n    // Class expressions may have an optional name.\n    if (type == \"variable\") return className(type, value);\n    return classNameAfter(type, value);\n  }\n  function className(type, value) {\n    if (type == \"variable\") {register(value); return cont(classNameAfter);}\n  }\n  function classNameAfter(type, value) {\n    if (value == \"<\") return cont(pushlex(\">\"), commasep(typeparam, \">\"), poplex, classNameAfter)\n    if (value == \"extends\" || value == \"implements\" || (isTS && type == \",\")) {\n      if (value == \"implements\") cx.marked = \"keyword\";\n      return cont(isTS ? typeexpr : expression, classNameAfter);\n    }\n    if (type == \"{\") return cont(pushlex(\"}\"), classBody, poplex);\n  }\n  function classBody(type, value) {\n    if (type == \"async\" ||\n        (type == \"variable\" &&\n         (value == \"static\" || value == \"get\" || value == \"set\" || (isTS && isModifier(value))) &&\n         cx.stream.match(/^\\s+[\\w$\\xa1-\\uffff]/, false))) {\n      cx.marked = \"keyword\";\n      return cont(classBody);\n    }\n    if (type == \"variable\" || cx.style == \"keyword\") {\n      cx.marked = \"property\";\n      return cont(isTS ? classfield : functiondef, classBody);\n    }\n    if (type == \"[\")\n      return cont(expression, maybetype, expect(\"]\"), isTS ? classfield : functiondef, classBody)\n    if (value == \"*\") {\n      cx.marked = \"keyword\";\n      return cont(classBody);\n    }\n    if (type == \";\") return cont(classBody);\n    if (type == \"}\") return cont();\n    if (value == \"@\") return cont(expression, classBody)\n  }\n  function classfield(type, value) {\n    if (value == \"?\") return cont(classfield)\n    if (type == \":\") return cont(typeexpr, maybeAssign)\n    if (value == \"=\") return cont(expressionNoComma)\n    return pass(functiondef)\n  }\n  function afterExport(type, value) {\n    if (value == \"*\") { cx.marked = \"keyword\"; return cont(maybeFrom, expect(\";\")); }\n    if (value == \"default\") { cx.marked = \"keyword\"; return cont(expression, expect(\";\")); }\n    if (type == \"{\") return cont(commasep(exportField, \"}\"), maybeFrom, expect(\";\"));\n    return pass(statement);\n  }\n  function exportField(type, value) {\n    if (value == \"as\") { cx.marked = \"keyword\"; return cont(expect(\"variable\")); }\n    if (type == \"variable\") return pass(expressionNoComma, exportField);\n  }\n  function afterImport(type) {\n    if (type == \"string\") return cont();\n    if (type == \"(\") return pass(expression);\n    return pass(importSpec, maybeMoreImports, maybeFrom);\n  }\n  function importSpec(type, value) {\n    if (type == \"{\") return contCommasep(importSpec, \"}\");\n    if (type == \"variable\") register(value);\n    if (value == \"*\") cx.marked = \"keyword\";\n    return cont(maybeAs);\n  }\n  function maybeMoreImports(type) {\n    if (type == \",\") return cont(importSpec, maybeMoreImports)\n  }\n  function maybeAs(_type, value) {\n    if (value == \"as\") { cx.marked = \"keyword\"; return cont(importSpec); }\n  }\n  function maybeFrom(_type, value) {\n    if (value == \"from\") { cx.marked = \"keyword\"; return cont(expression); }\n  }\n  function arrayLiteral(type) {\n    if (type == \"]\") return cont();\n    return pass(commasep(expressionNoComma, \"]\"));\n  }\n  function enumdef() {\n    return pass(pushlex(\"form\"), pattern, expect(\"{\"), pushlex(\"}\"), commasep(enummember, \"}\"), poplex, poplex)\n  }\n  function enummember() {\n    return pass(pattern, maybeAssign);\n  }\n\n  function isContinuedStatement(state, textAfter) {\n    return state.lastType == \"operator\" || state.lastType == \",\" ||\n      isOperatorChar.test(textAfter.charAt(0)) ||\n      /[,.]/.test(textAfter.charAt(0));\n  }\n\n  function expressionAllowed(stream, state, backUp) {\n    return state.tokenize == tokenBase &&\n      /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\\[{}\\(,;:]|=>)$/.test(state.lastType) ||\n      (state.lastType == \"quasi\" && /\\{\\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))\n  }\n\n  // Interface\n\n  return {\n    startState: function(basecolumn) {\n      var state = {\n        tokenize: tokenBase,\n        lastType: \"sof\",\n        cc: [],\n        lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, \"block\", false),\n        localVars: parserConfig.localVars,\n        context: parserConfig.localVars && new Context(null, null, false),\n        indented: basecolumn || 0\n      };\n      if (parserConfig.globalVars && typeof parserConfig.globalVars == \"object\")\n        state.globalVars = parserConfig.globalVars;\n      return state;\n    },\n\n    token: function(stream, state) {\n      if (stream.sol()) {\n        if (!state.lexical.hasOwnProperty(\"align\"))\n          state.lexical.align = false;\n        state.indented = stream.indentation();\n        findFatArrow(stream, state);\n      }\n      if (state.tokenize != tokenComment && stream.eatSpace()) return null;\n      var style = state.tokenize(stream, state);\n      if (type == \"comment\") return style;\n      state.lastType = type == \"operator\" && (content == \"++\" || content == \"--\") ? \"incdec\" : type;\n      return parseJS(state, style, type, content, stream);\n    },\n\n    indent: function(state, textAfter) {\n      if (state.tokenize == tokenComment) return CodeMirror.Pass;\n      if (state.tokenize != tokenBase) return 0;\n      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top\n      // Kludge to prevent 'maybelse' from blocking lexical scope pops\n      if (!/^\\s*else\\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {\n        var c = state.cc[i];\n        if (c == poplex) lexical = lexical.prev;\n        else if (c != maybeelse) break;\n      }\n      while ((lexical.type == \"stat\" || lexical.type == \"form\") &&\n             (firstChar == \"}\" || ((top = state.cc[state.cc.length - 1]) &&\n                                   (top == maybeoperatorComma || top == maybeoperatorNoComma) &&\n                                   !/^[,\\.=+\\-*:?[\\(]/.test(textAfter))))\n        lexical = lexical.prev;\n      if (statementIndent && lexical.type == \")\" && lexical.prev.type == \"stat\")\n        lexical = lexical.prev;\n      var type = lexical.type, closing = firstChar == type;\n\n      if (type == \"vardef\") return lexical.indented + (state.lastType == \"operator\" || state.lastType == \",\" ? lexical.info.length + 1 : 0);\n      else if (type == \"form\" && firstChar == \"{\") return lexical.indented;\n      else if (type == \"form\") return lexical.indented + indentUnit;\n      else if (type == \"stat\")\n        return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);\n      else if (lexical.info == \"switch\" && !closing && parserConfig.doubleIndentSwitch != false)\n        return lexical.indented + (/^(?:case|default)\\b/.test(textAfter) ? indentUnit : 2 * indentUnit);\n      else if (lexical.align) return lexical.column + (closing ? 0 : 1);\n      else return lexical.indented + (closing ? 0 : indentUnit);\n    },\n\n    electricInput: /^\\s*(?:case .*?:|default:|\\{|\\})$/,\n    blockCommentStart: jsonMode ? null : \"/*\",\n    blockCommentEnd: jsonMode ? null : \"*/\",\n    blockCommentContinue: jsonMode ? null : \" * \",\n    lineComment: jsonMode ? null : \"//\",\n    fold: \"brace\",\n    closeBrackets: \"()[]{}''\\\"\\\"``\",\n\n    helperType: jsonMode ? \"json\" : \"javascript\",\n    jsonldMode: jsonldMode,\n    jsonMode: jsonMode,\n\n    expressionAllowed: expressionAllowed,\n\n    skipExpression: function(state) {\n      var top = state.cc[state.cc.length - 1]\n      if (top == expression || top == expressionNoComma) state.cc.pop()\n    }\n  };\n});\n\nCodeMirror.registerHelper(\"wordChars\", \"javascript\", /[\\w$]/);\n\nCodeMirror.defineMIME(\"text/javascript\", \"javascript\");\nCodeMirror.defineMIME(\"text/ecmascript\", \"javascript\");\nCodeMirror.defineMIME(\"application/javascript\", \"javascript\");\nCodeMirror.defineMIME(\"application/x-javascript\", \"javascript\");\nCodeMirror.defineMIME(\"application/ecmascript\", \"javascript\");\nCodeMirror.defineMIME(\"application/json\", {name: \"javascript\", json: true});\nCodeMirror.defineMIME(\"application/x-json\", {name: \"javascript\", json: true});\nCodeMirror.defineMIME(\"application/ld+json\", {name: \"javascript\", jsonld: true});\nCodeMirror.defineMIME(\"text/typescript\", { name: \"javascript\", typescript: true });\nCodeMirror.defineMIME(\"application/typescript\", { name: \"javascript\", typescript: true });\n\n});\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/mode/php/php.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../htmlmixed/htmlmixed\"), require(\"../clike/clike\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../htmlmixed/htmlmixed\", \"../clike/clike\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  function keywords(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n\n  // Helper for phpString\n  function matchSequence(list, end, escapes) {\n    if (list.length == 0) return phpString(end);\n    return function (stream, state) {\n      var patterns = list[0];\n      for (var i = 0; i < patterns.length; i++) if (stream.match(patterns[i][0])) {\n        state.tokenize = matchSequence(list.slice(1), end);\n        return patterns[i][1];\n      }\n      state.tokenize = phpString(end, escapes);\n      return \"string\";\n    };\n  }\n  function phpString(closing, escapes) {\n    return function(stream, state) { return phpString_(stream, state, closing, escapes); };\n  }\n  function phpString_(stream, state, closing, escapes) {\n    // \"Complex\" syntax\n    if (escapes !== false && stream.match(\"${\", false) || stream.match(\"{$\", false)) {\n      state.tokenize = null;\n      return \"string\";\n    }\n\n    // Simple syntax\n    if (escapes !== false && stream.match(/^\\$[a-zA-Z_][a-zA-Z0-9_]*/)) {\n      // After the variable name there may appear array or object operator.\n      if (stream.match(\"[\", false)) {\n        // Match array operator\n        state.tokenize = matchSequence([\n          [[\"[\", null]],\n          [[/\\d[\\w\\.]*/, \"number\"],\n           [/\\$[a-zA-Z_][a-zA-Z0-9_]*/, \"variable-2\"],\n           [/[\\w\\$]+/, \"variable\"]],\n          [[\"]\", null]]\n        ], closing, escapes);\n      }\n      if (stream.match(/\\-\\>\\w/, false)) {\n        // Match object operator\n        state.tokenize = matchSequence([\n          [[\"->\", null]],\n          [[/[\\w]+/, \"variable\"]]\n        ], closing, escapes);\n      }\n      return \"variable-2\";\n    }\n\n    var escaped = false;\n    // Normal string\n    while (!stream.eol() &&\n           (escaped || escapes === false ||\n            (!stream.match(\"{$\", false) &&\n             !stream.match(/^(\\$[a-zA-Z_][a-zA-Z0-9_]*|\\$\\{)/, false)))) {\n      if (!escaped && stream.match(closing)) {\n        state.tokenize = null;\n        state.tokStack.pop(); state.tokStack.pop();\n        break;\n      }\n      escaped = stream.next() == \"\\\\\" && !escaped;\n    }\n    return \"string\";\n  }\n\n  var phpKeywords = \"abstract and array as break case catch class clone const continue declare default \" +\n    \"do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final \" +\n    \"for foreach function global goto if implements interface instanceof namespace \" +\n    \"new or private protected public static switch throw trait try use var while xor \" +\n    \"die echo empty exit eval include include_once isset list require require_once return \" +\n    \"print unset __halt_compiler self static parent yield insteadof finally\";\n  var phpAtoms = \"true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__\";\n  var phpBuiltin = \"func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents file_put_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists array_intersect_key array_combine array_column pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count\";\n  CodeMirror.registerHelper(\"hintWords\", \"php\", [phpKeywords, phpAtoms, phpBuiltin].join(\" \").split(\" \"));\n  CodeMirror.registerHelper(\"wordChars\", \"php\", /[\\w$]/);\n\n  var phpConfig = {\n    name: \"clike\",\n    helperType: \"php\",\n    keywords: keywords(phpKeywords),\n    blockKeywords: keywords(\"catch do else elseif for foreach if switch try while finally\"),\n    defKeywords: keywords(\"class function interface namespace trait\"),\n    atoms: keywords(phpAtoms),\n    builtin: keywords(phpBuiltin),\n    multiLineStrings: true,\n    hooks: {\n      \"$\": function(stream) {\n        stream.eatWhile(/[\\w\\$_]/);\n        return \"variable-2\";\n      },\n      \"<\": function(stream, state) {\n        var before;\n        if (before = stream.match(/<<\\s*/)) {\n          var quoted = stream.eat(/['\"]/);\n          stream.eatWhile(/[\\w\\.]/);\n          var delim = stream.current().slice(before[0].length + (quoted ? 2 : 1));\n          if (quoted) stream.eat(quoted);\n          if (delim) {\n            (state.tokStack || (state.tokStack = [])).push(delim, 0);\n            state.tokenize = phpString(delim, quoted != \"'\");\n            return \"string\";\n          }\n        }\n        return false;\n      },\n      \"#\": function(stream) {\n        while (!stream.eol() && !stream.match(\"?>\", false)) stream.next();\n        return \"comment\";\n      },\n      \"/\": function(stream) {\n        if (stream.eat(\"/\")) {\n          while (!stream.eol() && !stream.match(\"?>\", false)) stream.next();\n          return \"comment\";\n        }\n        return false;\n      },\n      '\"': function(_stream, state) {\n        (state.tokStack || (state.tokStack = [])).push('\"', 0);\n        state.tokenize = phpString('\"');\n        return \"string\";\n      },\n      \"{\": function(_stream, state) {\n        if (state.tokStack && state.tokStack.length)\n          state.tokStack[state.tokStack.length - 1]++;\n        return false;\n      },\n      \"}\": function(_stream, state) {\n        if (state.tokStack && state.tokStack.length > 0 &&\n            !--state.tokStack[state.tokStack.length - 1]) {\n          state.tokenize = phpString(state.tokStack[state.tokStack.length - 2]);\n        }\n        return false;\n      }\n    }\n  };\n\n  CodeMirror.defineMode(\"php\", function(config, parserConfig) {\n    var htmlMode = CodeMirror.getMode(config, (parserConfig && parserConfig.htmlMode) || \"text/html\");\n    var phpMode = CodeMirror.getMode(config, phpConfig);\n\n    function dispatch(stream, state) {\n      var isPHP = state.curMode == phpMode;\n      if (stream.sol() && state.pending && state.pending != '\"' && state.pending != \"'\") state.pending = null;\n      if (!isPHP) {\n        if (stream.match(/^<\\?\\w*/)) {\n          state.curMode = phpMode;\n          if (!state.php) state.php = CodeMirror.startState(phpMode, htmlMode.indent(state.html, \"\"))\n          state.curState = state.php;\n          return \"meta\";\n        }\n        if (state.pending == '\"' || state.pending == \"'\") {\n          while (!stream.eol() && stream.next() != state.pending) {}\n          var style = \"string\";\n        } else if (state.pending && stream.pos < state.pending.end) {\n          stream.pos = state.pending.end;\n          var style = state.pending.style;\n        } else {\n          var style = htmlMode.token(stream, state.curState);\n        }\n        if (state.pending) state.pending = null;\n        var cur = stream.current(), openPHP = cur.search(/<\\?/), m;\n        if (openPHP != -1) {\n          if (style == \"string\" && (m = cur.match(/[\\'\\\"]$/)) && !/\\?>/.test(cur)) state.pending = m[0];\n          else state.pending = {end: stream.pos, style: style};\n          stream.backUp(cur.length - openPHP);\n        }\n        return style;\n      } else if (isPHP && state.php.tokenize == null && stream.match(\"?>\")) {\n        state.curMode = htmlMode;\n        state.curState = state.html;\n        if (!state.php.context.prev) state.php = null;\n        return \"meta\";\n      } else {\n        return phpMode.token(stream, state.curState);\n      }\n    }\n\n    return {\n      startState: function() {\n        var html = CodeMirror.startState(htmlMode)\n        var php = parserConfig.startOpen ? CodeMirror.startState(phpMode) : null\n        return {html: html,\n                php: php,\n                curMode: parserConfig.startOpen ? phpMode : htmlMode,\n                curState: parserConfig.startOpen ? php : html,\n                pending: null};\n      },\n\n      copyState: function(state) {\n        var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html),\n            php = state.php, phpNew = php && CodeMirror.copyState(phpMode, php), cur;\n        if (state.curMode == htmlMode) cur = htmlNew;\n        else cur = phpNew;\n        return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur,\n                pending: state.pending};\n      },\n\n      token: dispatch,\n\n      indent: function(state, textAfter) {\n        if ((state.curMode != phpMode && /^\\s*<\\//.test(textAfter)) ||\n            (state.curMode == phpMode && /^\\?>/.test(textAfter)))\n          return htmlMode.indent(state.html, textAfter);\n        return state.curMode.indent(state.curState, textAfter);\n      },\n\n      blockCommentStart: \"/*\",\n      blockCommentEnd: \"*/\",\n      lineComment: \"//\",\n\n      innerMode: function(state) { return {state: state.curState, mode: state.curMode}; }\n    };\n  }, \"htmlmixed\", \"clike\");\n\n  CodeMirror.defineMIME(\"application/x-httpd-php\", \"php\");\n  CodeMirror.defineMIME(\"application/x-httpd-php-open\", {name: \"php\", startOpen: true});\n  CodeMirror.defineMIME(\"text/x-php\", phpConfig);\n});\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/mode/powershell/powershell.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n  'use strict';\n  if (typeof exports == 'object' && typeof module == 'object') // CommonJS\n    mod(require('../../lib/codemirror'));\n  else if (typeof define == 'function' && define.amd) // AMD\n    define(['../../lib/codemirror'], mod);\n  else // Plain browser env\n    mod(window.CodeMirror);\n})(function(CodeMirror) {\n'use strict';\n\nCodeMirror.defineMode('powershell', function() {\n  function buildRegexp(patterns, options) {\n    options = options || {};\n    var prefix = options.prefix !== undefined ? options.prefix : '^';\n    var suffix = options.suffix !== undefined ? options.suffix : '\\\\b';\n\n    for (var i = 0; i < patterns.length; i++) {\n      if (patterns[i] instanceof RegExp) {\n        patterns[i] = patterns[i].source;\n      }\n      else {\n        patterns[i] = patterns[i].replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\n      }\n    }\n\n    return new RegExp(prefix + '(' + patterns.join('|') + ')' + suffix, 'i');\n  }\n\n  var notCharacterOrDash = '(?=[^A-Za-z\\\\d\\\\-_]|$)';\n  var varNames = /[\\w\\-:]/\n  var keywords = buildRegexp([\n    /begin|break|catch|continue|data|default|do|dynamicparam/,\n    /else|elseif|end|exit|filter|finally|for|foreach|from|function|if|in/,\n    /param|process|return|switch|throw|trap|try|until|where|while/\n  ], { suffix: notCharacterOrDash });\n\n  var punctuation = /[\\[\\]{},;`\\.]|@[({]/;\n  var wordOperators = buildRegexp([\n    'f',\n    /b?not/,\n    /[ic]?split/, 'join',\n    /is(not)?/, 'as',\n    /[ic]?(eq|ne|[gl][te])/,\n    /[ic]?(not)?(like|match|contains)/,\n    /[ic]?replace/,\n    /b?(and|or|xor)/\n  ], { prefix: '-' });\n  var symbolOperators = /[+\\-*\\/%]=|\\+\\+|--|\\.\\.|[+\\-*&^%:=!|\\/]|<(?!#)|(?!#)>/;\n  var operators = buildRegexp([wordOperators, symbolOperators], { suffix: '' });\n\n  var numbers = /^((0x[\\da-f]+)|((\\d+\\.\\d+|\\d\\.|\\.\\d+|\\d+)(e[\\+\\-]?\\d+)?))[ld]?([kmgtp]b)?/i;\n\n  var identifiers = /^[A-Za-z\\_][A-Za-z\\-\\_\\d]*\\b/;\n\n  var symbolBuiltins = /[A-Z]:|%|\\?/i;\n  var namedBuiltins = buildRegexp([\n    /Add-(Computer|Content|History|Member|PSSnapin|Type)/,\n    /Checkpoint-Computer/,\n    /Clear-(Content|EventLog|History|Host|Item(Property)?|Variable)/,\n    /Compare-Object/,\n    /Complete-Transaction/,\n    /Connect-PSSession/,\n    /ConvertFrom-(Csv|Json|SecureString|StringData)/,\n    /Convert-Path/,\n    /ConvertTo-(Csv|Html|Json|SecureString|Xml)/,\n    /Copy-Item(Property)?/,\n    /Debug-Process/,\n    /Disable-(ComputerRestore|PSBreakpoint|PSRemoting|PSSessionConfiguration)/,\n    /Disconnect-PSSession/,\n    /Enable-(ComputerRestore|PSBreakpoint|PSRemoting|PSSessionConfiguration)/,\n    /(Enter|Exit)-PSSession/,\n    /Export-(Alias|Clixml|Console|Counter|Csv|FormatData|ModuleMember|PSSession)/,\n    /ForEach-Object/,\n    /Format-(Custom|List|Table|Wide)/,\n    new RegExp('Get-(Acl|Alias|AuthenticodeSignature|ChildItem|Command|ComputerRestorePoint|Content|ControlPanelItem|Counter|Credential'\n      + '|Culture|Date|Event|EventLog|EventSubscriber|ExecutionPolicy|FormatData|Help|History|Host|HotFix|Item|ItemProperty|Job'\n      + '|Location|Member|Module|PfxCertificate|Process|PSBreakpoint|PSCallStack|PSDrive|PSProvider|PSSession|PSSessionConfiguration'\n      + '|PSSnapin|Random|Service|TraceSource|Transaction|TypeData|UICulture|Unique|Variable|Verb|WinEvent|WmiObject)'),\n    /Group-Object/,\n    /Import-(Alias|Clixml|Counter|Csv|LocalizedData|Module|PSSession)/,\n    /ImportSystemModules/,\n    /Invoke-(Command|Expression|History|Item|RestMethod|WebRequest|WmiMethod)/,\n    /Join-Path/,\n    /Limit-EventLog/,\n    /Measure-(Command|Object)/,\n    /Move-Item(Property)?/,\n    new RegExp('New-(Alias|Event|EventLog|Item(Property)?|Module|ModuleManifest|Object|PSDrive|PSSession|PSSessionConfigurationFile'\n      + '|PSSessionOption|PSTransportOption|Service|TimeSpan|Variable|WebServiceProxy|WinEvent)'),\n    /Out-(Default|File|GridView|Host|Null|Printer|String)/,\n    /Pause/,\n    /(Pop|Push)-Location/,\n    /Read-Host/,\n    /Receive-(Job|PSSession)/,\n    /Register-(EngineEvent|ObjectEvent|PSSessionConfiguration|WmiEvent)/,\n    /Remove-(Computer|Event|EventLog|Item(Property)?|Job|Module|PSBreakpoint|PSDrive|PSSession|PSSnapin|TypeData|Variable|WmiObject)/,\n    /Rename-(Computer|Item(Property)?)/,\n    /Reset-ComputerMachinePassword/,\n    /Resolve-Path/,\n    /Restart-(Computer|Service)/,\n    /Restore-Computer/,\n    /Resume-(Job|Service)/,\n    /Save-Help/,\n    /Select-(Object|String|Xml)/,\n    /Send-MailMessage/,\n    new RegExp('Set-(Acl|Alias|AuthenticodeSignature|Content|Date|ExecutionPolicy|Item(Property)?|Location|PSBreakpoint|PSDebug' +\n               '|PSSessionConfiguration|Service|StrictMode|TraceSource|Variable|WmiInstance)'),\n    /Show-(Command|ControlPanelItem|EventLog)/,\n    /Sort-Object/,\n    /Split-Path/,\n    /Start-(Job|Process|Service|Sleep|Transaction|Transcript)/,\n    /Stop-(Computer|Job|Process|Service|Transcript)/,\n    /Suspend-(Job|Service)/,\n    /TabExpansion2/,\n    /Tee-Object/,\n    /Test-(ComputerSecureChannel|Connection|ModuleManifest|Path|PSSessionConfigurationFile)/,\n    /Trace-Command/,\n    /Unblock-File/,\n    /Undo-Transaction/,\n    /Unregister-(Event|PSSessionConfiguration)/,\n    /Update-(FormatData|Help|List|TypeData)/,\n    /Use-Transaction/,\n    /Wait-(Event|Job|Process)/,\n    /Where-Object/,\n    /Write-(Debug|Error|EventLog|Host|Output|Progress|Verbose|Warning)/,\n    /cd|help|mkdir|more|oss|prompt/,\n    /ac|asnp|cat|cd|chdir|clc|clear|clhy|cli|clp|cls|clv|cnsn|compare|copy|cp|cpi|cpp|cvpa|dbp|del|diff|dir|dnsn|ebp/,\n    /echo|epal|epcsv|epsn|erase|etsn|exsn|fc|fl|foreach|ft|fw|gal|gbp|gc|gci|gcm|gcs|gdr|ghy|gi|gjb|gl|gm|gmo|gp|gps/,\n    /group|gsn|gsnp|gsv|gu|gv|gwmi|h|history|icm|iex|ihy|ii|ipal|ipcsv|ipmo|ipsn|irm|ise|iwmi|iwr|kill|lp|ls|man|md/,\n    /measure|mi|mount|move|mp|mv|nal|ndr|ni|nmo|npssc|nsn|nv|ogv|oh|popd|ps|pushd|pwd|r|rbp|rcjb|rcsn|rd|rdr|ren|ri/,\n    /rjb|rm|rmdir|rmo|rni|rnp|rp|rsn|rsnp|rujb|rv|rvpa|rwmi|sajb|sal|saps|sasv|sbp|sc|select|set|shcm|si|sl|sleep|sls/,\n    /sort|sp|spjb|spps|spsv|start|sujb|sv|swmi|tee|trcm|type|where|wjb|write/\n  ], { prefix: '', suffix: '' });\n  var variableBuiltins = buildRegexp([\n    /[$?^_]|Args|ConfirmPreference|ConsoleFileName|DebugPreference|Error|ErrorActionPreference|ErrorView|ExecutionContext/,\n    /FormatEnumerationLimit|Home|Host|Input|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount/,\n    /MaximumHistoryCount|MaximumVariableCount|MyInvocation|NestedPromptLevel|OutputEncoding|Pid|Profile|ProgressPreference/,\n    /PSBoundParameters|PSCommandPath|PSCulture|PSDefaultParameterValues|PSEmailServer|PSHome|PSScriptRoot|PSSessionApplicationName/,\n    /PSSessionConfigurationName|PSSessionOption|PSUICulture|PSVersionTable|Pwd|ShellId|StackTrace|VerbosePreference/,\n    /WarningPreference|WhatIfPreference/,\n\n    /Event|EventArgs|EventSubscriber|Sender/,\n    /Matches|Ofs|ForEach|LastExitCode|PSCmdlet|PSItem|PSSenderInfo|This/,\n    /true|false|null/\n  ], { prefix: '\\\\$', suffix: '' });\n\n  var builtins = buildRegexp([symbolBuiltins, namedBuiltins, variableBuiltins], { suffix: notCharacterOrDash });\n\n  var grammar = {\n    keyword: keywords,\n    number: numbers,\n    operator: operators,\n    builtin: builtins,\n    punctuation: punctuation,\n    identifier: identifiers\n  };\n\n  // tokenizers\n  function tokenBase(stream, state) {\n    // Handle Comments\n    //var ch = stream.peek();\n\n    var parent = state.returnStack[state.returnStack.length - 1];\n    if (parent && parent.shouldReturnFrom(state)) {\n      state.tokenize = parent.tokenize;\n      state.returnStack.pop();\n      return state.tokenize(stream, state);\n    }\n\n    if (stream.eatSpace()) {\n      return null;\n    }\n\n    if (stream.eat('(')) {\n      state.bracketNesting += 1;\n      return 'punctuation';\n    }\n\n    if (stream.eat(')')) {\n      state.bracketNesting -= 1;\n      return 'punctuation';\n    }\n\n    for (var key in grammar) {\n      if (stream.match(grammar[key])) {\n        return key;\n      }\n    }\n\n    var ch = stream.next();\n\n    // single-quote string\n    if (ch === \"'\") {\n      return tokenSingleQuoteString(stream, state);\n    }\n\n    if (ch === '$') {\n      return tokenVariable(stream, state);\n    }\n\n    // double-quote string\n    if (ch === '\"') {\n      return tokenDoubleQuoteString(stream, state);\n    }\n\n    if (ch === '<' && stream.eat('#')) {\n      state.tokenize = tokenComment;\n      return tokenComment(stream, state);\n    }\n\n    if (ch === '#') {\n      stream.skipToEnd();\n      return 'comment';\n    }\n\n    if (ch === '@') {\n      var quoteMatch = stream.eat(/[\"']/);\n      if (quoteMatch && stream.eol()) {\n        state.tokenize = tokenMultiString;\n        state.startQuote = quoteMatch[0];\n        return tokenMultiString(stream, state);\n      } else if (stream.eol()) {\n        return 'error';\n      } else if (stream.peek().match(/[({]/)) {\n        return 'punctuation';\n      } else if (stream.peek().match(varNames)) {\n        // splatted variable\n        return tokenVariable(stream, state);\n      }\n    }\n    return 'error';\n  }\n\n  function tokenSingleQuoteString(stream, state) {\n    var ch;\n    while ((ch = stream.peek()) != null) {\n      stream.next();\n\n      if (ch === \"'\" && !stream.eat(\"'\")) {\n        state.tokenize = tokenBase;\n        return 'string';\n      }\n    }\n\n    return 'error';\n  }\n\n  function tokenDoubleQuoteString(stream, state) {\n    var ch;\n    while ((ch = stream.peek()) != null) {\n      if (ch === '$') {\n        state.tokenize = tokenStringInterpolation;\n        return 'string';\n      }\n\n      stream.next();\n      if (ch === '`') {\n        stream.next();\n        continue;\n      }\n\n      if (ch === '\"' && !stream.eat('\"')) {\n        state.tokenize = tokenBase;\n        return 'string';\n      }\n    }\n\n    return 'error';\n  }\n\n  function tokenStringInterpolation(stream, state) {\n    return tokenInterpolation(stream, state, tokenDoubleQuoteString);\n  }\n\n  function tokenMultiStringReturn(stream, state) {\n    state.tokenize = tokenMultiString;\n    state.startQuote = '\"'\n    return tokenMultiString(stream, state);\n  }\n\n  function tokenHereStringInterpolation(stream, state) {\n    return tokenInterpolation(stream, state, tokenMultiStringReturn);\n  }\n\n  function tokenInterpolation(stream, state, parentTokenize) {\n    if (stream.match('$(')) {\n      var savedBracketNesting = state.bracketNesting;\n      state.returnStack.push({\n        /*jshint loopfunc:true */\n        shouldReturnFrom: function(state) {\n          return state.bracketNesting === savedBracketNesting;\n        },\n        tokenize: parentTokenize\n      });\n      state.tokenize = tokenBase;\n      state.bracketNesting += 1;\n      return 'punctuation';\n    } else {\n      stream.next();\n      state.returnStack.push({\n        shouldReturnFrom: function() { return true; },\n        tokenize: parentTokenize\n      });\n      state.tokenize = tokenVariable;\n      return state.tokenize(stream, state);\n    }\n  }\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while ((ch = stream.next()) != null) {\n      if (maybeEnd && ch == '>') {\n          state.tokenize = tokenBase;\n          break;\n      }\n      maybeEnd = (ch === '#');\n    }\n    return 'comment';\n  }\n\n  function tokenVariable(stream, state) {\n    var ch = stream.peek();\n    if (stream.eat('{')) {\n      state.tokenize = tokenVariableWithBraces;\n      return tokenVariableWithBraces(stream, state);\n    } else if (ch != undefined && ch.match(varNames)) {\n      stream.eatWhile(varNames);\n      state.tokenize = tokenBase;\n      return 'variable-2';\n    } else {\n      state.tokenize = tokenBase;\n      return 'error';\n    }\n  }\n\n  function tokenVariableWithBraces(stream, state) {\n    var ch;\n    while ((ch = stream.next()) != null) {\n      if (ch === '}') {\n        state.tokenize = tokenBase;\n        break;\n      }\n    }\n    return 'variable-2';\n  }\n\n  function tokenMultiString(stream, state) {\n    var quote = state.startQuote;\n    if (stream.sol() && stream.match(new RegExp(quote + '@'))) {\n      state.tokenize = tokenBase;\n    }\n    else if (quote === '\"') {\n      while (!stream.eol()) {\n        var ch = stream.peek();\n        if (ch === '$') {\n          state.tokenize = tokenHereStringInterpolation;\n          return 'string';\n        }\n\n        stream.next();\n        if (ch === '`') {\n          stream.next();\n        }\n      }\n    }\n    else {\n      stream.skipToEnd();\n    }\n\n    return 'string';\n  }\n\n  var external = {\n    startState: function() {\n      return {\n        returnStack: [],\n        bracketNesting: 0,\n        tokenize: tokenBase\n      };\n    },\n\n    token: function(stream, state) {\n      return state.tokenize(stream, state);\n    },\n\n    blockCommentStart: '<#',\n    blockCommentEnd: '#>',\n    lineComment: '#',\n    fold: 'brace'\n  };\n  return external;\n});\n\nCodeMirror.defineMIME('application/x-powershell', 'powershell');\n});\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/mode/python/python.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  function wordRegexp(words) {\n    return new RegExp(\"^((\" + words.join(\")|(\") + \"))\\\\b\");\n  }\n\n  var wordOperators = wordRegexp([\"and\", \"or\", \"not\", \"is\"]);\n  var commonKeywords = [\"as\", \"assert\", \"break\", \"class\", \"continue\",\n                        \"def\", \"del\", \"elif\", \"else\", \"except\", \"finally\",\n                        \"for\", \"from\", \"global\", \"if\", \"import\",\n                        \"lambda\", \"pass\", \"raise\", \"return\",\n                        \"try\", \"while\", \"with\", \"yield\", \"in\"];\n  var commonBuiltins = [\"abs\", \"all\", \"any\", \"bin\", \"bool\", \"bytearray\", \"callable\", \"chr\",\n                        \"classmethod\", \"compile\", \"complex\", \"delattr\", \"dict\", \"dir\", \"divmod\",\n                        \"enumerate\", \"eval\", \"filter\", \"float\", \"format\", \"frozenset\",\n                        \"getattr\", \"globals\", \"hasattr\", \"hash\", \"help\", \"hex\", \"id\",\n                        \"input\", \"int\", \"isinstance\", \"issubclass\", \"iter\", \"len\",\n                        \"list\", \"locals\", \"map\", \"max\", \"memoryview\", \"min\", \"next\",\n                        \"object\", \"oct\", \"open\", \"ord\", \"pow\", \"property\", \"range\",\n                        \"repr\", \"reversed\", \"round\", \"set\", \"setattr\", \"slice\",\n                        \"sorted\", \"staticmethod\", \"str\", \"sum\", \"super\", \"tuple\",\n                        \"type\", \"vars\", \"zip\", \"__import__\", \"NotImplemented\",\n                        \"Ellipsis\", \"__debug__\"];\n  CodeMirror.registerHelper(\"hintWords\", \"python\", commonKeywords.concat(commonBuiltins));\n\n  function top(state) {\n    return state.scopes[state.scopes.length - 1];\n  }\n\n  CodeMirror.defineMode(\"python\", function(conf, parserConf) {\n    var ERRORCLASS = \"error\";\n\n    var delimiters = parserConf.delimiters || parserConf.singleDelimiters || /^[\\(\\)\\[\\]\\{\\}@,:`=;\\.\\\\]/;\n    //               (Backwards-compatiblity with old, cumbersome config system)\n    var operators = [parserConf.singleOperators, parserConf.doubleOperators, parserConf.doubleDelimiters, parserConf.tripleDelimiters,\n                     parserConf.operators || /^([-+*/%\\/&|^]=?|[<>=]+|\\/\\/=?|\\*\\*=?|!=|[~!@])/]\n    for (var i = 0; i < operators.length; i++) if (!operators[i]) operators.splice(i--, 1)\n\n    var hangingIndent = parserConf.hangingIndent || conf.indentUnit;\n\n    var myKeywords = commonKeywords, myBuiltins = commonBuiltins;\n    if (parserConf.extra_keywords != undefined)\n      myKeywords = myKeywords.concat(parserConf.extra_keywords);\n\n    if (parserConf.extra_builtins != undefined)\n      myBuiltins = myBuiltins.concat(parserConf.extra_builtins);\n\n    var py3 = !(parserConf.version && Number(parserConf.version) < 3)\n    if (py3) {\n      // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator\n      var identifiers = parserConf.identifiers|| /^[_A-Za-z\\u00A1-\\uFFFF][_A-Za-z0-9\\u00A1-\\uFFFF]*/;\n      myKeywords = myKeywords.concat([\"nonlocal\", \"False\", \"True\", \"None\", \"async\", \"await\"]);\n      myBuiltins = myBuiltins.concat([\"ascii\", \"bytes\", \"exec\", \"print\"]);\n      var stringPrefixes = new RegExp(\"^(([rbuf]|(br)|(fr))?('{3}|\\\"{3}|['\\\"]))\", \"i\");\n    } else {\n      var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/;\n      myKeywords = myKeywords.concat([\"exec\", \"print\"]);\n      myBuiltins = myBuiltins.concat([\"apply\", \"basestring\", \"buffer\", \"cmp\", \"coerce\", \"execfile\",\n                                      \"file\", \"intern\", \"long\", \"raw_input\", \"reduce\", \"reload\",\n                                      \"unichr\", \"unicode\", \"xrange\", \"False\", \"True\", \"None\"]);\n      var stringPrefixes = new RegExp(\"^(([rubf]|(ur)|(br))?('{3}|\\\"{3}|['\\\"]))\", \"i\");\n    }\n    var keywords = wordRegexp(myKeywords);\n    var builtins = wordRegexp(myBuiltins);\n\n    // tokenizers\n    function tokenBase(stream, state) {\n      var sol = stream.sol() && state.lastToken != \"\\\\\"\n      if (sol) state.indent = stream.indentation()\n      // Handle scope changes\n      if (sol && top(state).type == \"py\") {\n        var scopeOffset = top(state).offset;\n        if (stream.eatSpace()) {\n          var lineOffset = stream.indentation();\n          if (lineOffset > scopeOffset)\n            pushPyScope(state);\n          else if (lineOffset < scopeOffset && dedent(stream, state) && stream.peek() != \"#\")\n            state.errorToken = true;\n          return null;\n        } else {\n          var style = tokenBaseInner(stream, state);\n          if (scopeOffset > 0 && dedent(stream, state))\n            style += \" \" + ERRORCLASS;\n          return style;\n        }\n      }\n      return tokenBaseInner(stream, state);\n    }\n\n    function tokenBaseInner(stream, state) {\n      if (stream.eatSpace()) return null;\n\n      // Handle Comments\n      if (stream.match(/^#.*/)) return \"comment\";\n\n      // Handle Number Literals\n      if (stream.match(/^[0-9\\.]/, false)) {\n        var floatLiteral = false;\n        // Floats\n        if (stream.match(/^[\\d_]*\\.\\d+(e[\\+\\-]?\\d+)?/i)) { floatLiteral = true; }\n        if (stream.match(/^[\\d_]+\\.\\d*/)) { floatLiteral = true; }\n        if (stream.match(/^\\.\\d+/)) { floatLiteral = true; }\n        if (floatLiteral) {\n          // Float literals may be \"imaginary\"\n          stream.eat(/J/i);\n          return \"number\";\n        }\n        // Integers\n        var intLiteral = false;\n        // Hex\n        if (stream.match(/^0x[0-9a-f_]+/i)) intLiteral = true;\n        // Binary\n        if (stream.match(/^0b[01_]+/i)) intLiteral = true;\n        // Octal\n        if (stream.match(/^0o[0-7_]+/i)) intLiteral = true;\n        // Decimal\n        if (stream.match(/^[1-9][\\d_]*(e[\\+\\-]?[\\d_]+)?/)) {\n          // Decimal literals may be \"imaginary\"\n          stream.eat(/J/i);\n          // TODO - Can you have imaginary longs?\n          intLiteral = true;\n        }\n        // Zero by itself with no other piece of number.\n        if (stream.match(/^0(?![\\dx])/i)) intLiteral = true;\n        if (intLiteral) {\n          // Integer literals may be \"long\"\n          stream.eat(/L/i);\n          return \"number\";\n        }\n      }\n\n      // Handle Strings\n      if (stream.match(stringPrefixes)) {\n        var isFmtString = stream.current().toLowerCase().indexOf('f') !== -1;\n        if (!isFmtString) {\n          state.tokenize = tokenStringFactory(stream.current());\n          return state.tokenize(stream, state);\n        } else {\n          state.tokenize = formatStringFactory(stream.current(), state.tokenize);\n          return state.tokenize(stream, state);\n        }\n      }\n\n      for (var i = 0; i < operators.length; i++)\n        if (stream.match(operators[i])) return \"operator\"\n\n      if (stream.match(delimiters)) return \"punctuation\";\n\n      if (state.lastToken == \".\" && stream.match(identifiers))\n        return \"property\";\n\n      if (stream.match(keywords) || stream.match(wordOperators))\n        return \"keyword\";\n\n      if (stream.match(builtins))\n        return \"builtin\";\n\n      if (stream.match(/^(self|cls)\\b/))\n        return \"variable-2\";\n\n      if (stream.match(identifiers)) {\n        if (state.lastToken == \"def\" || state.lastToken == \"class\")\n          return \"def\";\n        return \"variable\";\n      }\n\n      // Handle non-detected items\n      stream.next();\n      return ERRORCLASS;\n    }\n\n    function formatStringFactory(delimiter, tokenOuter) {\n      while (\"rubf\".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)\n        delimiter = delimiter.substr(1);\n\n      var singleline = delimiter.length == 1;\n      var OUTCLASS = \"string\";\n\n      function tokenFString(stream, state) {\n        // inside f-str Expression\n        if (stream.match(delimiter)) {\n          // expression ends pre-maturally, but very common in editing\n          // Could show error to remind users to close brace here\n          state.tokenize = tokenString\n          return OUTCLASS;\n        } else if (stream.match('{')) {\n          // starting brace, if not eaten below\n          return \"punctuation\";\n        } else if (stream.match('}')) {\n          // return to regular inside string state\n          state.tokenize = tokenString\n          return \"punctuation\";\n        } else {\n          // use tokenBaseInner to parse the expression\n          return tokenBaseInner(stream, state);\n        }\n      }\n\n      function tokenString(stream, state) {\n        while (!stream.eol()) {\n          stream.eatWhile(/[^'\"\\{\\}\\\\]/);\n          if (stream.eat(\"\\\\\")) {\n            stream.next();\n            if (singleline && stream.eol())\n              return OUTCLASS;\n          } else if (stream.match(delimiter)) {\n            state.tokenize = tokenOuter;\n            return OUTCLASS;\n          } else if (stream.match('{{')) {\n            // ignore {{ in f-str\n            return OUTCLASS;\n          } else if (stream.match('{', false)) {\n            // switch to nested mode\n            state.tokenize = tokenFString\n            if (stream.current()) {\n              return OUTCLASS;\n            } else {\n              // need to return something, so eat the starting {\n              stream.next();\n              return \"punctuation\";\n            }\n          } else if (stream.match('}}')) {\n            return OUTCLASS;\n          } else if (stream.match('}')) {\n            // single } in f-string is an error\n            return ERRORCLASS;\n          } else {\n            stream.eat(/['\"]/);\n          }\n        }\n        if (singleline) {\n          if (parserConf.singleLineStringErrors)\n            return ERRORCLASS;\n          else\n            state.tokenize = tokenOuter;\n        }\n        return OUTCLASS;\n      }\n      tokenString.isString = true;\n      return tokenString;\n    }\n\n    function tokenStringFactory(delimiter) {\n      while (\"rubf\".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)\n        delimiter = delimiter.substr(1);\n\n      var singleline = delimiter.length == 1;\n      var OUTCLASS = \"string\";\n\n      function tokenString(stream, state) {\n        while (!stream.eol()) {\n          stream.eatWhile(/[^'\"\\\\]/);\n          if (stream.eat(\"\\\\\")) {\n            stream.next();\n            if (singleline && stream.eol())\n              return OUTCLASS;\n          } else if (stream.match(delimiter)) {\n            state.tokenize = tokenBase;\n            return OUTCLASS;\n          } else {\n            stream.eat(/['\"]/);\n          }\n        }\n        if (singleline) {\n          if (parserConf.singleLineStringErrors)\n            return ERRORCLASS;\n          else\n            state.tokenize = tokenBase;\n        }\n        return OUTCLASS;\n      }\n      tokenString.isString = true;\n      return tokenString;\n    }\n\n    function pushPyScope(state) {\n      while (top(state).type != \"py\") state.scopes.pop()\n      state.scopes.push({offset: top(state).offset + conf.indentUnit,\n                         type: \"py\",\n                         align: null})\n    }\n\n    function pushBracketScope(stream, state, type) {\n      var align = stream.match(/^([\\s\\[\\{\\(]|#.*)*$/, false) ? null : stream.column() + 1\n      state.scopes.push({offset: state.indent + hangingIndent,\n                         type: type,\n                         align: align})\n    }\n\n    function dedent(stream, state) {\n      var indented = stream.indentation();\n      while (state.scopes.length > 1 && top(state).offset > indented) {\n        if (top(state).type != \"py\") return true;\n        state.scopes.pop();\n      }\n      return top(state).offset != indented;\n    }\n\n    function tokenLexer(stream, state) {\n      if (stream.sol()) state.beginningOfLine = true;\n\n      var style = state.tokenize(stream, state);\n      var current = stream.current();\n\n      // Handle decorators\n      if (state.beginningOfLine && current == \"@\")\n        return stream.match(identifiers, false) ? \"meta\" : py3 ? \"operator\" : ERRORCLASS;\n\n      if (/\\S/.test(current)) state.beginningOfLine = false;\n\n      if ((style == \"variable\" || style == \"builtin\")\n          && state.lastToken == \"meta\")\n        style = \"meta\";\n\n      // Handle scope changes.\n      if (current == \"pass\" || current == \"return\")\n        state.dedent += 1;\n\n      if (current == \"lambda\") state.lambda = true;\n      if (current == \":\" && !state.lambda && top(state).type == \"py\")\n        pushPyScope(state);\n\n      if (current.length == 1 && !/string|comment/.test(style)) {\n        var delimiter_index = \"[({\".indexOf(current);\n        if (delimiter_index != -1)\n          pushBracketScope(stream, state, \"])}\".slice(delimiter_index, delimiter_index+1));\n\n        delimiter_index = \"])}\".indexOf(current);\n        if (delimiter_index != -1) {\n          if (top(state).type == current) state.indent = state.scopes.pop().offset - hangingIndent\n          else return ERRORCLASS;\n        }\n      }\n      if (state.dedent > 0 && stream.eol() && top(state).type == \"py\") {\n        if (state.scopes.length > 1) state.scopes.pop();\n        state.dedent -= 1;\n      }\n\n      return style;\n    }\n\n    var external = {\n      startState: function(basecolumn) {\n        return {\n          tokenize: tokenBase,\n          scopes: [{offset: basecolumn || 0, type: \"py\", align: null}],\n          indent: basecolumn || 0,\n          lastToken: null,\n          lambda: false,\n          dedent: 0\n        };\n      },\n\n      token: function(stream, state) {\n        var addErr = state.errorToken;\n        if (addErr) state.errorToken = false;\n        var style = tokenLexer(stream, state);\n\n        if (style && style != \"comment\")\n          state.lastToken = (style == \"keyword\" || style == \"punctuation\") ? stream.current() : style;\n        if (style == \"punctuation\") style = null;\n\n        if (stream.eol() && state.lambda)\n          state.lambda = false;\n        return addErr ? style + \" \" + ERRORCLASS : style;\n      },\n\n      indent: function(state, textAfter) {\n        if (state.tokenize != tokenBase)\n          return state.tokenize.isString ? CodeMirror.Pass : 0;\n\n        var scope = top(state), closing = scope.type == textAfter.charAt(0)\n        if (scope.align != null)\n          return scope.align - (closing ? 1 : 0)\n        else\n          return scope.offset - (closing ? hangingIndent : 0)\n      },\n\n      electricInput: /^\\s*[\\}\\]\\)]$/,\n      closeBrackets: {triples: \"'\\\"\"},\n      lineComment: \"#\",\n      fold: \"indent\"\n    };\n    return external;\n  });\n\n  CodeMirror.defineMIME(\"text/x-python\", \"python\");\n\n  var words = function(str) { return str.split(\" \"); };\n\n  CodeMirror.defineMIME(\"text/x-cython\", {\n    name: \"python\",\n    extra_keywords: words(\"by cdef cimport cpdef ctypedef enum except \"+\n                          \"extern gil include nogil property public \"+\n                          \"readonly struct union DEF IF ELIF ELSE\")\n  });\n\n});\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/mode/shell/shell.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode('shell', function() {\n\n  var words = {};\n  function define(style, dict) {\n    for(var i = 0; i < dict.length; i++) {\n      words[dict[i]] = style;\n    }\n  };\n\n  var commonAtoms = [\"true\", \"false\"];\n  var commonKeywords = [\"if\", \"then\", \"do\", \"else\", \"elif\", \"while\", \"until\", \"for\", \"in\", \"esac\", \"fi\",\n    \"fin\", \"fil\", \"done\", \"exit\", \"set\", \"unset\", \"export\", \"function\"];\n  var commonCommands = [\"ab\", \"awk\", \"bash\", \"beep\", \"cat\", \"cc\", \"cd\", \"chown\", \"chmod\", \"chroot\", \"clear\",\n    \"cp\", \"curl\", \"cut\", \"diff\", \"echo\", \"find\", \"gawk\", \"gcc\", \"get\", \"git\", \"grep\", \"hg\", \"kill\", \"killall\",\n    \"ln\", \"ls\", \"make\", \"mkdir\", \"openssl\", \"mv\", \"nc\", \"nl\", \"node\", \"npm\", \"ping\", \"ps\", \"restart\", \"rm\",\n    \"rmdir\", \"sed\", \"service\", \"sh\", \"shopt\", \"shred\", \"source\", \"sort\", \"sleep\", \"ssh\", \"start\", \"stop\",\n    \"su\", \"sudo\", \"svn\", \"tee\", \"telnet\", \"top\", \"touch\", \"vi\", \"vim\", \"wall\", \"wc\", \"wget\", \"who\", \"write\",\n    \"yes\", \"zsh\"];\n\n  CodeMirror.registerHelper(\"hintWords\", \"shell\", commonAtoms.concat(commonKeywords, commonCommands));\n\n  define('atom', commonAtoms);\n  define('keyword', commonKeywords);\n  define('builtin', commonCommands);\n\n  function tokenBase(stream, state) {\n    if (stream.eatSpace()) return null;\n\n    var sol = stream.sol();\n    var ch = stream.next();\n\n    if (ch === '\\\\') {\n      stream.next();\n      return null;\n    }\n    if (ch === '\\'' || ch === '\"' || ch === '`') {\n      state.tokens.unshift(tokenString(ch, ch === \"`\" ? \"quote\" : \"string\"));\n      return tokenize(stream, state);\n    }\n    if (ch === '#') {\n      if (sol && stream.eat('!')) {\n        stream.skipToEnd();\n        return 'meta'; // 'comment'?\n      }\n      stream.skipToEnd();\n      return 'comment';\n    }\n    if (ch === '$') {\n      state.tokens.unshift(tokenDollar);\n      return tokenize(stream, state);\n    }\n    if (ch === '+' || ch === '=') {\n      return 'operator';\n    }\n    if (ch === '-') {\n      stream.eat('-');\n      stream.eatWhile(/\\w/);\n      return 'attribute';\n    }\n    if (/\\d/.test(ch)) {\n      stream.eatWhile(/\\d/);\n      if(stream.eol() || !/\\w/.test(stream.peek())) {\n        return 'number';\n      }\n    }\n    stream.eatWhile(/[\\w-]/);\n    var cur = stream.current();\n    if (stream.peek() === '=' && /\\w+/.test(cur)) return 'def';\n    return words.hasOwnProperty(cur) ? words[cur] : null;\n  }\n\n  function tokenString(quote, style) {\n    var close = quote == \"(\" ? \")\" : quote == \"{\" ? \"}\" : quote\n    return function(stream, state) {\n      var next, escaped = false;\n      while ((next = stream.next()) != null) {\n        if (next === close && !escaped) {\n          state.tokens.shift();\n          break;\n        } else if (next === '$' && !escaped && quote !== \"'\" && stream.peek() != close) {\n          escaped = true;\n          stream.backUp(1);\n          state.tokens.unshift(tokenDollar);\n          break;\n        } else if (!escaped && quote !== close && next === quote) {\n          state.tokens.unshift(tokenString(quote, style))\n          return tokenize(stream, state)\n        } else if (!escaped && /['\"]/.test(next) && !/['\"]/.test(quote)) {\n          state.tokens.unshift(tokenStringStart(next, \"string\"));\n          stream.backUp(1);\n          break;\n        }\n        escaped = !escaped && next === '\\\\';\n      }\n      return style;\n    };\n  };\n\n  function tokenStringStart(quote, style) {\n    return function(stream, state) {\n      state.tokens[0] = tokenString(quote, style)\n      stream.next()\n      return tokenize(stream, state)\n    }\n  }\n\n  var tokenDollar = function(stream, state) {\n    if (state.tokens.length > 1) stream.eat('$');\n    var ch = stream.next()\n    if (/['\"({]/.test(ch)) {\n      state.tokens[0] = tokenString(ch, ch == \"(\" ? \"quote\" : ch == \"{\" ? \"def\" : \"string\");\n      return tokenize(stream, state);\n    }\n    if (!/\\d/.test(ch)) stream.eatWhile(/\\w/);\n    state.tokens.shift();\n    return 'def';\n  };\n\n  function tokenize(stream, state) {\n    return (state.tokens[0] || tokenBase) (stream, state);\n  };\n\n  return {\n    startState: function() {return {tokens:[]};},\n    token: function(stream, state) {\n      return tokenize(stream, state);\n    },\n    closeBrackets: \"()[]{}''\\\"\\\"``\",\n    lineComment: '#',\n    fold: \"brace\"\n  };\n});\n\nCodeMirror.defineMIME('text/x-sh', 'shell');\n// Apache uses a slightly different Media Type for Shell scripts\n// http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types\nCodeMirror.defineMIME('application/x-sh', 'shell');\n\n});\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/cronGen/cronGen.js",
    "content": "﻿(function ($) {\n    // var resultsName = \"\";\n    var inputElement;\n    var displayElement;\n    $.fn.extend({\n        cronGen: function (options) {\n            if (options == null) {\n              options = {};\n            }\n            options = $.extend({}, $.fn.cronGen.defaultOptions, options);\n            //create top menu\n            var cronContainer = $(\"<div/>\", { id: \"CronContainer\", style: \"display:none;width:300px;height:300px;\" });\n            var mainDiv = $(\"<div/>\", { id: \"CronGenMainDiv\", style: \"width:410px;height:420px;\" });\n            var topMenu = $(\"<ul/>\", { \"class\": \"nav nav-tabs\", id: \"CronGenTabs\" });\n            $('<li/>', { 'class': 'active' }).html($('<a id=\"SecondlyTab\" href=\"#Secondly\">秒</a>')).appendTo(topMenu);\n            $('<li/>').html($('<a id=\"MinutesTab\" href=\"#Minutes\">分钟</a>')).appendTo(topMenu);\n            $('<li/>').html($('<a id=\"HourlyTab\" href=\"#Hourly\">小时</a>')).appendTo(topMenu);\n            $('<li/>').html($('<a id=\"DailyTab\" href=\"#Daily\">日</a>')).appendTo(topMenu);\n            $('<li/>').html($('<a id=\"MonthlyTab\" href=\"#Monthly\">月</a>')).appendTo(topMenu);\n            $('<li/>').html($('<a id=\"WeeklyTab\" href=\"#Weekly\">周</a>')).appendTo(topMenu);\n            $('<li/>').html($('<a id=\"YearlyTab\" href=\"#Yearly\">年</a>')).appendTo(topMenu);\n            $(topMenu).appendTo(mainDiv);\n\n            //create what's inside the tabs\n            var container = $(\"<div/>\", { \"class\": \"container-fluid\", \"style\": \"margin-top: 30px;margin-left: -14px;\" });\n            var row = $(\"<div/>\", { \"class\": \"row-fluid\" });\n            var span12 = $(\"<div/>\", { \"class\": \"span12\" });\n            var tabContent = $(\"<div/>\", { \"class\": \"tab-content\", \"style\": \"border:0px; margin-top:-20px;\" });\n\n\n            //creating the secondsTab\n            var secondsTab = $(\"<div/>\", { \"class\": \"tab-pane active\", id: \"Secondly\" });\n            var seconds1 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"1\", name : \"second\"}).appendTo(seconds1);\n            $(seconds1).append(\"每秒 允许的通配符[, - * /]\");\n            $(seconds1).appendTo(secondsTab);\n\n            var seconds2 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"2\", name : \"second\"}).appendTo(seconds2);\n            $(seconds2).append(\"周期 从\");\n            $(\"<input/>\",{type : \"text\", id : \"secondStart_0\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(seconds2);\n            $(seconds2).append(\"-\");\n            $(\"<input/>\",{type : \"text\", id : \"secondEnd_0\", value : \"2\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(seconds2);\n            $(seconds2).append(\"秒\");\n            $(seconds2).appendTo(secondsTab);\n\n            var seconds3 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"3\", name : \"second\"}).appendTo(seconds3);\n            $(seconds3).append(\"从\");\n            $(\"<input/>\",{type : \"text\", id : \"secondStart_1\", value : \"0\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(seconds3);\n            $(seconds3).append(\"秒开始,每\");\n            $(\"<input/>\",{type : \"text\", id : \"secondEnd_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(seconds3);\n            $(seconds3).append(\"秒执行一次\");\n            $(seconds3).appendTo(secondsTab);\n\n            var seconds4 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"4\", name : \"second\", id: \"sencond_appoint\"}).appendTo(seconds4);\n            $(seconds4).append(\"指定\");\n            $(seconds4).appendTo(secondsTab);\n\n            $(secondsTab).append('<div class=\"imp secondList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"0\">00<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"1\">01<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"2\">02<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"3\">03<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"4\">04<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"5\">05<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"6\">06<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"7\">07<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"8\">08<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"9\">09</div>');\n            $(secondsTab).append('<div class=\"imp secondList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"10\">10<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"11\">11<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"12\">12<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"13\">13<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"14\">14<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"15\">15<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"16\">16<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"17\">17<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"18\">18<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"19\">19</div>');\n            $(secondsTab).append('<div class=\"imp secondList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"20\">20<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"21\">21<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"22\">22<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"23\">23<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"24\">24<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"25\">25<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"26\">26<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"27\">27<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"28\">28<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"29\">29</div>');\n            $(secondsTab).append('<div class=\"imp secondList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"30\">30<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"31\">31<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"32\">32<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"33\">33<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"34\">34<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"35\">35<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"36\">36<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"37\">37<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"38\">38<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"39\">39</div>');\n            $(secondsTab).append('<div class=\"imp secondList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"40\">40<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"41\">41<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"42\">42<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"43\">43<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"44\">44<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"45\">45<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"46\">46<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"47\">47<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"48\">48<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"49\">49</div>');\n            $(secondsTab).append('<div class=\"imp secondList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"50\">50<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"51\">51<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"52\">52<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"53\">53<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"54\">54<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"55\">55<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"56\">56<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"57\">57<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"58\">58<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"59\">59</div>');\n            $(\"<input/>\",{type : \"hidden\", id : \"secondHidden\"}).appendTo(secondsTab);\n            $(secondsTab).appendTo(tabContent);\n\n            //creating the minutesTab\n            var minutesTab = $(\"<div/>\", { \"class\": \"tab-pane\", id: \"Minutes\" });\n\n            var minutes1 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"1\", name : \"min\"}).appendTo(minutes1);\n            $(minutes1).append(\"每分钟 允许的通配符[, - * /]\");\n            $(minutes1).appendTo(minutesTab);\n\n            var minutes2 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"2\", name : \"min\"}).appendTo(minutes2);\n            $(minutes2).append(\"周期 从\");\n            $(\"<input/>\",{type : \"text\", id : \"minStart_0\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(minutes2);\n            $(minutes2).append(\"-\");\n            $(\"<input/>\",{type : \"text\", id : \"minEnd_0\", value : \"2\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(minutes2);\n            $(minutes2).append(\"分钟\");\n            $(minutes2).appendTo(minutesTab);\n\n            var minutes3 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"3\", name : \"min\"}).appendTo(minutes3);\n            $(minutes3).append(\"从\");\n            $(\"<input/>\",{type : \"text\", id : \"minStart_1\", value : \"0\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(minutes3);\n            $(minutes3).append(\"分钟开始,每\");\n            $(\"<input/>\",{type : \"text\", id : \"minEnd_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(minutes3);\n            $(minutes3).append(\"分钟执行一次\");\n            $(minutes3).appendTo(minutesTab);\n\n            var minutes4 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"4\", name : \"min\", id: \"min_appoint\"}).appendTo(minutes4);\n            $(minutes4).append(\"指定\");\n            $(minutes4).appendTo(minutesTab);\n\n            $(minutesTab).append('<div class=\"imp minList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"0\">00<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"1\">01<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"2\">02<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"3\">03<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"4\">04<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"5\">05<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"6\">06<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"7\">07<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"8\">08<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"9\">09</div>');\n            $(minutesTab).append('<div class=\"imp minList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"10\">10<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"11\">11<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"12\">12<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"13\">13<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"14\">14<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"15\">15<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"16\">16<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"17\">17<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"18\">18<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"19\">19</div>');\n            $(minutesTab).append('<div class=\"imp minList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"20\">20<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"21\">21<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"22\">22<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"23\">23<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"24\">24<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"25\">25<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"26\">26<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"27\">27<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"28\">28<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"29\">29</div>');\n            $(minutesTab).append('<div class=\"imp minList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"30\">30<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"31\">31<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"32\">32<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"33\">33<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"34\">34<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"35\">35<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"36\">36<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"37\">37<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"38\">38<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"39\">39</div>');\n            $(minutesTab).append('<div class=\"imp minList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"40\">40<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"41\">41<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"42\">42<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"43\">43<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"44\">44<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"45\">45<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"46\">46<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"47\">47<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"48\">48<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"49\">49</div>');\n            $(minutesTab).append('<div class=\"imp minList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"50\">50<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"51\">51<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"52\">52<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"53\">53<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"54\">54<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"55\">55<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"56\">56<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"57\">57<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"58\">58<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"59\">59</div>');\n            $(\"<input/>\",{type : \"hidden\", id : \"minHidden\"}).appendTo(minutesTab);\n            $(minutesTab).appendTo(tabContent);\n\n            //creating the hourlyTab\n            var hourlyTab = $(\"<div/>\", { \"class\": \"tab-pane\", id: \"Hourly\" });\n\n            var hourly1 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"1\", name : \"hour\"}).appendTo(hourly1);\n            $(hourly1).append(\"每小时 允许的通配符[, - * /]\");\n            $(hourly1).appendTo(hourlyTab);\n\n            var hourly2 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"2\", name : \"hour\"}).appendTo(hourly2);\n            $(hourly2).append(\"周期 从\");\n            $(\"<input/>\",{type : \"text\", id : \"hourStart_0\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(hourly2);\n            $(hourly2).append(\"-\");\n            $(\"<input/>\",{type : \"text\", id : \"hourEnd_0\", value : \"2\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(hourly2);\n            $(hourly2).append(\"小时\");\n            $(hourly2).appendTo(hourlyTab);\n\n            var hourly3 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"3\", name : \"hour\"}).appendTo(hourly3);\n            $(hourly3).append(\"从\");\n            $(\"<input/>\",{type : \"text\", id : \"hourStart_1\", value : \"0\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(hourly3);\n            $(hourly3).append(\"小时开始,每\");\n            $(\"<input/>\",{type : \"text\", id : \"hourEnd_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(hourly3);\n            $(hourly3).append(\"小时执行一次\");\n            $(hourly3).appendTo(hourlyTab);\n\n            var hourly4 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"4\", name : \"hour\", id: \"hour_appoint\"}).appendTo(hourly4);\n            $(hourly4).append(\"指定\");\n            $(hourly4).appendTo(hourlyTab);\n\n            $(hourlyTab).append('<div class=\"imp hourList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"0\">00<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"1\">01<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"2\">02<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"3\">03<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"4\">04<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"5\">05</div>');\n            $(hourlyTab).append('<div class=\"imp hourList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"6\">06<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"7\">07<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"8\">08<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"9\">09<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"10\">10<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"11\">11</div>');\n            $(hourlyTab).append('<div class=\"imp hourList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"12\">12<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"13\">13<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"14\">14<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"15\">15<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"16\">16<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"17\">17</div>');\n            $(hourlyTab).append('<div class=\"imp hourList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"18\">18<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"19\">19<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"20\">20<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"21\">21<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"22\">22<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"23\">23</div>');\n            $(\"<input/>\",{type : \"hidden\", id : \"hourHidden\"}).appendTo(hourlyTab);\n            $(hourlyTab).appendTo(tabContent);\n\n\n            //creating the dailyTab\n            var dailyTab = $(\"<div/>\", { \"class\": \"tab-pane\", id: \"Daily\" });\n\n            var daily1 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"1\", name : \"day\"}).appendTo(daily1);\n            $(daily1).append(\"每天 允许的通配符[, - * / L W]\");\n            $(daily1).appendTo(dailyTab);\n\n            var daily5 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"2\", name : \"day\"}).appendTo(daily5);\n            $(daily5).append(\"不指定\");\n            $(daily5).appendTo(dailyTab);\n\n            var daily2 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"3\", name : \"day\"}).appendTo(daily2);\n            $(daily2).append(\"周期 从\");\n            $(\"<input/>\",{type : \"text\", id : \"dayStart_0\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(daily2);\n            $(daily2).append(\"-\");\n            $(\"<input/>\",{type : \"text\", id : \"dayEnd_0\", value : \"2\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(daily2);\n            $(daily2).append(\"日\");\n            $(daily2).appendTo(dailyTab);\n\n            var daily3 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"4\", name : \"day\"}).appendTo(daily3);\n            $(daily3).append(\"从\");\n            $(\"<input/>\",{type : \"text\", id : \"dayStart_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(daily3);\n            $(daily3).append(\"日开始,每\");\n            $(\"<input/>\",{type : \"text\", id : \"dayEnd_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(daily3);\n            $(daily3).append(\"天执行一次\");\n            $(daily3).appendTo(dailyTab);\n\n            var daily6 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"5\", name : \"day\"}).appendTo(daily6);\n            $(daily6).append(\"每月\");\n            $(\"<input/>\",{type : \"text\", id : \"dayStart_2\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(daily6);\n            $(daily6).append(\"号最近的那个工作日\");\n            $(daily6).appendTo(dailyTab);\n\n            var daily7 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"6\", name : \"day\"}).appendTo(daily7);\n            $(daily7).append(\"本月最后一天\");\n            $(daily7).appendTo(dailyTab);\n\n            var daily4 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"7\", name : \"day\", id: \"day_appoint\"}).appendTo(daily4);\n            $(daily4).append(\"指定\");\n            $(daily4).appendTo(dailyTab);\n\n            $(dailyTab).append('<div class=\"imp dayList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"1\">01<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"2\">02<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"3\">03<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"4\">04<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"5\">05<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"6\">06<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"7\">07<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"8\">08<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"9\">09<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"10\">10</div>');\n            $(dailyTab).append('<div class=\"imp dayList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"11\">11<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"12\">12<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"13\">13<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"14\">14<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"15\">15<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"16\">16<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"17\">17<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"18\">18<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"19\">19<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"20\">20</div>');\n            $(dailyTab).append('<div class=\"imp dayList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"21\">21<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"22\">22<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"23\">23<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"24\">24<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"25\">25<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"26\">26<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"27\">27<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"28\">28<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"29\">29<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"30\">30</div>');\n            $(dailyTab).append('<div class=\"imp dayList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"31\">31</div>');\n            $(\"<input/>\",{type : \"hidden\", id : \"dayHidden\"}).appendTo(dailyTab);\n            $(dailyTab).appendTo(tabContent);\n\n\n            //creating the monthlyTab\n            var monthlyTab = $(\"<div/>\", { \"class\": \"tab-pane\", id: \"Monthly\" });\n\n            var monthly1 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"1\", name : \"month\"}).appendTo(monthly1);\n            $(monthly1).append(\"每月 允许的通配符[, - * /]\");\n            $(monthly1).appendTo(monthlyTab);\n\n            var monthly2 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"2\", name : \"month\"}).appendTo(monthly2);\n            $(monthly2).append(\"不指定\");\n            $(monthly2).appendTo(monthlyTab);\n\n            var monthly3 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"3\", name : \"month\"}).appendTo(monthly3);\n            $(monthly3).append(\"周期 从\");\n            $(\"<input/>\",{type : \"text\", id : \"monthStart_0\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(monthly3);\n            $(monthly3).append(\"-\");\n            $(\"<input/>\",{type : \"text\", id : \"monthEnd_0\", value : \"2\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(monthly3);\n            $(monthly3).append(\"月\");\n            $(monthly3).appendTo(monthlyTab);\n\n            var monthly4 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"4\", name : \"month\"}).appendTo(monthly4);\n            $(monthly4).append(\"从\");\n            $(\"<input/>\",{type : \"text\", id : \"monthStart_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(monthly4);\n            $(monthly4).append(\"月开始,每\");\n            $(\"<input/>\",{type : \"text\", id : \"monthEnd_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(monthly4);\n            $(monthly4).append(\"月执行一次\");\n            $(monthly4).appendTo(monthlyTab);\n\n            var monthly5 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"5\", name : \"month\", id: \"month_appoint\"}).appendTo(monthly5);\n            $(monthly5).append(\"指定\");\n            $(monthly5).appendTo(monthlyTab);\n\n            $(monthlyTab).append('<div class=\"imp monthList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"1\">01<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"2\">02<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"3\">03<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"4\">04<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"5\">05<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"6\">06</div>');\n            $(monthlyTab).append('<div class=\"imp monthList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"7\">07<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"8\">08<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"9\">09<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"10\">10<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"11\">11<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"12\">12</div>');\n            $(\"<input/>\",{type : \"hidden\", id : \"monthHidden\"}).appendTo(monthlyTab);\n            $(monthlyTab).appendTo(tabContent);\n\n            //creating the weeklyTab\n            var weeklyTab = $(\"<div/>\", { \"class\": \"tab-pane\", id: \"Weekly\" });\n\n            var weekly1 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"1\", name : \"week\"}).appendTo(weekly1);\n            $(weekly1).append(\"每周 允许的通配符[, - * / L #]\");\n            $(weekly1).appendTo(weeklyTab);\n\n            var weekly2 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"2\", name : \"week\"}).appendTo(weekly2);\n            $(weekly2).append(\"不指定\");\n            $(weekly2).appendTo(weeklyTab);\n\n            var weekly3 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"3\", name : \"week\"}).appendTo(weekly3);\n            $(weekly3).append(\"周期 从星期\");\n            $(\"<input/>\",{type : \"text\", id : \"weekStart_0\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(weekly3);\n            $(weekly3).append(\"-\");\n            $(\"<input/>\",{type : \"text\", id : \"weekEnd_0\", value : \"2\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(weekly3);\n            $(weekly3).appendTo(weeklyTab);\n\n            var weekly4 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"4\", name : \"week\"}).appendTo(weekly4);\n            $(weekly4).append(\"第\");\n            $(\"<input/>\",{type : \"text\", id : \"weekStart_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(weekly4);\n            $(weekly4).append(\"周的星期\");\n            $(\"<input/>\",{type : \"text\", id : \"weekEnd_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(weekly4);\n            $(weekly4).appendTo(weeklyTab);\n\n            var weekly5 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"5\", name : \"week\"}).appendTo(weekly5);\n            $(weekly5).append(\"本月最后一个星期\");\n            $(\"<input/>\",{type : \"text\", id : \"weekStart_2\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(weekly5);\n            $(weekly5).appendTo(weeklyTab);\n\n            var weekly6 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"6\", name : \"week\", id: \"week_appoint\"}).appendTo(weekly6);\n            $(weekly6).append(\"指定\");\n            $(weekly6).appendTo(weeklyTab);\n\n            $(weeklyTab).append('<div class=\"imp weekList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"1\">1<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"2\">2<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"3\">3<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"4\">4<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"5\">5<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"6\">6<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"7\">7</div>');\n\n            $(\"<input/>\",{type : \"hidden\", id : \"weekHidden\"}).appendTo(weeklyTab);\n            $(weeklyTab).appendTo(tabContent);\n\n            //creating the yearlyTab\n            var yearlyTab = $(\"<div/>\", { \"class\": \"tab-pane\", id: \"Yearly\" });\n\n            var yearly1 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"1\", name : \"year\"}).appendTo(yearly1);\n            $(yearly1).append(\"不指定 允许的通配符[, - * /] 非必填\");\n            $(yearly1).appendTo(yearlyTab);\n\n            var yearly3 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"2\", name : \"year\"}).appendTo(yearly3);\n            $(yearly3).append(\"每年\");\n            $(yearly3).appendTo(yearlyTab);\n\n            var yearly2 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"3\", name : \"year\"}).appendTo(yearly2);\n            $(yearly2).append(\"周期从\");\n            $(\"<input/>\",{type : \"text\", id : \"yearStart_0\", value : \"2016\", style:\"width:45px; height:20px;\"}).appendTo(yearly2);\n            $(yearly2).append(\"-\");\n            $(\"<input/>\",{type : \"text\", id : \"yearEnd_0\", value : \"2017\", style:\"width:45px; height:20px;\"}).appendTo(yearly2);\n            $(yearly2).append(\"年\");\n            $(yearly2).appendTo(yearlyTab);\n            $(\"<input/>\",{type : \"hidden\", id : \"yearHidden\"}).appendTo(yearlyTab);\n            $(yearlyTab).appendTo(tabContent);\n\n            $(tabContent).appendTo(span12);\n\n            //creating the button and results input\n            // resultsName = $(this).prop(\"id\");\n            // $(this).prop(\"name\", resultsName);\n\n            var runTime = '<br style=\"padding-top: 10px\"><label>最近运行时间: </label></br><textarea id=\"runTime\" rows=\"6\" style=\"width: 90%;resize: none;background: none;border: none;outline: none;\" readonly = readonly></textarea></div>';\n\n            $(span12).appendTo(row);\n            $(row).appendTo(container);\n            $(container).appendTo(mainDiv);\n            $(runTime).appendTo(mainDiv);\n            $(cronContainer).append(mainDiv);\n\n            var that = $(this);\n\n            // Hide the original input\n            that.hide();\n\n            // Replace the input with an input group\n            var $g = $(\"<div>\").addClass(\"input-group\");\n            // Add an input\n            var $i = $(\"<input>\", { type: 'text', placeholder: 'cron表达式...', name: 'cronGen_display' }).addClass(\"form-control\").val($(that).val());\n            $i.appendTo($g);\n            // Add the button\n            var $b = $(\"<button class=\\\"btn btn-default\\\"><i class=\\\"fa fa-edit\\\"></i></button>\");\n            // Put button inside span\n            var $s = $(\"<span>\").addClass(\"input-group-btn\");\n            $b.appendTo($s);\n            $s.appendTo($g);\n\n            $(this).before($g);\n\n            inputElement = that;\n            displayElement = $i;\n\n            $b.popover({\n                html: true,\n                content: function () {\n                    return $(cronContainer).html();\n                },\n                template: '<div class=\"popover\" style=\"max-width:500px !important; width:425px;left:-341.656px;\"><div class=\"arrow\"></div><div class=\"popover-inner\"><h3 class=\"popover-title\"></h3><div class=\"popover-content\"><p></p></div></div></div>',\n                sanitize:false,\n                placement: options.direction\n\n            }).on('click', function (e) {\n                if (inputElement.val().trim() !== '') {\n                    refreshRunTime();\n                }\n                e.preventDefault();\n\n                //fillDataOfMinutesAndHoursSelectOptions();\n                //fillDayWeekInMonth();\n                //fillInWeekDays();\n                //fillInMonths();\n\n                $.fn.cronGen.tools.cronParse(inputElement.val());\n\n                //绑定指定事件\n                $.fn.cronGen.tools.initChangeEvent();\n\n\n                $('#CronGenTabs a').click(function (e) {\n                    e.preventDefault();\n                    $(this).tab('show');\n                    //generate();\n                });\n                $(\"#CronGenMainDiv select,input\").change(function (e) {\n                    generate();\n                    refreshRunTime();\n                });\n                $(\"#CronGenMainDiv input\").focus(function (e) {\n                    generate();\n                });\n                //generate();\n            });\n            return;\n        }\n    });\n\n\n    var fillInMonths = function () {\n        var days = [\n            { text: \"一月\", val: \"1\" },\n            { text: \"二月\", val: \"2\" },\n            { text: \"三月\", val: \"3\" },\n            { text: \"四月\", val: \"4\" },\n            { text: \"五月\", val: \"5\" },\n            { text: \"六月\", val: \"6\" },\n            { text: \"七月\", val: \"7\" },\n            { text: \"八月\", val: \"8\" },\n            { text: \"九月\", val: \"9\" },\n            { text: \"十月\", val: \"10\" },\n            { text: \"十一月\", val: \"11\" },\n            { text: \"十二月\", val: \"12\" }\n        ];\n        $(\".months\").each(function () {\n            fillOptions(this, days);\n        });\n    };\n\n    var fillOptions = function (elements, options) {\n        for (var i = 0; i < options.length; i++)\n            $(elements).append(\"<option value='\" + options[i].val + \"'>\" + options[i].text + \"</option>\");\n    };\n    var fillDataOfMinutesAndHoursSelectOptions = function () {\n        for (var i = 0; i < 60; i++) {\n            if (i < 24) {\n                $(\".hours\").each(function () { $(this).append(timeSelectOption(i)); });\n            }\n            $(\".minutes\").each(function () { $(this).append(timeSelectOption(i)); });\n        }\n    };\n    var fillInWeekDays = function () {\n        var days = [\n            { text: \"周一\", val: \"2\" },\n            { text: \"周二\", val: \"3\" },\n            { text: \"周三\", val: \"4\" },\n            { text: \"周四\", val: \"5\" },\n            { text: \"周五\", val: \"6\" },\n            { text: \"周六\", val: \"7\" },\n            { text: \"周天\", val: \"1\" }\n        ];\n        $(\".week-days\").each(function () {\n            fillOptions(this, days);\n        });\n\n    };\n    var fillDayWeekInMonth = function () {\n        var days = [\n            { text: \"第一个\", val: \"1\" },\n            { text: \"第二个\", val: \"2\" },\n            { text: \"第三个\", val: \"3\" },\n            { text: \"第四个\", val: \"4\" }\n        ];\n        $(\".day-order-in-month\").each(function () {\n            fillOptions(this, days);\n        });\n    };\n    var displayTimeUnit = function (unit) {\n        if (unit.toString().length == 1)\n            return \"0\" + unit;\n        return unit;\n    };\n    var timeSelectOption = function (i) {\n        return \"<option id='\" + i + \"'>\" + displayTimeUnit(i) + \"</option>\";\n    };\n\n    var generate = function () {\n\n        var activeTab = $(\"ul#CronGenTabs li.active a\").prop(\"id\");\n        if (activeTab == undefined) {\n            return;\n        }\n        var results = \"\";\n        switch (activeTab) {\n            case \"SecondlyTab\":\n                switch ($(\"input:radio[name=second]:checked\").val()) {\n                    case \"1\":\n                        $.fn.cronGen.tools.everyTime(\"second\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"2\":\n                        $.fn.cronGen.tools.cycle(\"second\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"3\":\n                        $.fn.cronGen.tools.startOn(\"second\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"4\":\n                    \t$.fn.cronGen.tools.initCheckBox(\"second\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                }\n                break;\n            case \"MinutesTab\":\n                switch ($(\"input:radio[name=min]:checked\").val()) {\n                    case \"1\":\n                        $.fn.cronGen.tools.everyTime(\"min\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"2\":\n                        $.fn.cronGen.tools.cycle(\"min\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"3\":\n                        $.fn.cronGen.tools.startOn(\"min\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"4\":\n                    \t$.fn.cronGen.tools.initCheckBox(\"min\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                }\n                break;\n            case \"HourlyTab\":\n                switch ($(\"input:radio[name=hour]:checked\").val()) {\n                    case \"1\":\n                       $.fn.cronGen.tools.everyTime(\"hour\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"2\":\n                       $.fn.cronGen.tools.cycle(\"hour\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"3\":\n                        $.fn.cronGen.tools.startOn(\"hour\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"4\":\n                    \t$.fn.cronGen.tools.initCheckBox(\"hour\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                }\n                break;\n            case \"DailyTab\":\n                switch ($(\"input:radio[name=day]:checked\").val()) {\n                    case \"1\":\n                        $.fn.cronGen.tools.everyTime(\"day\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"2\":\n                        $.fn.cronGen.tools.unAppoint(\"day\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"3\":\n                        $.fn.cronGen.tools.cycle(\"day\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"4\":\n                        $.fn.cronGen.tools.startOn(\"day\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"5\":\n                        $.fn.cronGen.tools.workDay(\"day\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"6\":\n                        $.fn.cronGen.tools.lastDay(\"day\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"7\":\n                    \t$.fn.cronGen.tools.initCheckBox(\"day\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                }\n                break;\n            case \"WeeklyTab\":\n                switch ($(\"input:radio[name=week]:checked\").val()) {\n                    case \"1\":\n                        $.fn.cronGen.tools.everyTime(\"week\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"2\":\n                        $.fn.cronGen.tools.unAppoint(\"week\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"3\":\n                        $.fn.cronGen.tools.cycle(\"week\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"4\":\n                        $.fn.cronGen.tools.startOn(\"week\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"5\":\n                        $.fn.cronGen.tools.lastWeek(\"week\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"6\":\n                    \t$.fn.cronGen.tools.initCheckBox(\"week\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                }\n                break;\n            case \"MonthlyTab\":\n                switch ($(\"input:radio[name=month]:checked\").val()) {\n                    case \"1\":\n                        $.fn.cronGen.tools.everyTime(\"month\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"2\":\n                        $.fn.cronGen.tools.unAppoint(\"month\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"3\":\n                        $.fn.cronGen.tools.cycle(\"month\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"4\":\n                        $.fn.cronGen.tools.startOn(\"month\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"5\":\n                    \t$.fn.cronGen.tools.initCheckBox(\"month\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                }\n                break;\n            case \"YearlyTab\":\n                switch ($(\"input:radio[name=year]:checked\").val()) {\n                    case \"1\":\n                        $.fn.cronGen.tools.unAppoint(\"year\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"2\":\n                        $.fn.cronGen.tools.everyTime(\"year\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"3\":\n                        $.fn.cronGen.tools.cycle(\"year\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                }\n                break;\n        }\n\n        // Update original control\n        inputElement.val(results);\n        // Update display\n        displayElement.val(results);\n    };\n\n    var refreshRunTime = function () {\n        $.ajax({\n            type : 'GET',\n            url : base_url + \"/jobinfo/nextTriggerTime\",\n            data : {\n                \"scheduleType\" : 'CRON',\n                \"scheduleConf\" : inputElement.val()\n            },\n            dataType : \"json\",\n            success : function(data){\n                if (data.code === 200) {\n                    $('#runTime').val(data.content.join(\"\\n\"));\n                } else {\n                    $('#runTime').val(data.msg);\n                }\n            }\n        });\n    };\n\n})(jQuery);\n\n(function($) {\n    $.fn.cronGen.defaultOptions = {\n        direction : 'bottom'\n    };\n    $.fn.cronGen.tools = {\n        /**\n         * 每周期\n         */\n        everyTime : function(dom){\n            $(\"#\"+dom+\"Hidden\").val(\"*\");\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        /**\n         * 不指定\n         */\n        unAppoint : function(dom){\n            var val = \"?\";\n            if (dom == \"year\")\n            {\n                val = \"\";\n            }\n            $(\"#\"+dom+\"Hidden\").val(val);\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        /**\n         * 周期\n         */\n        cycle : function(dom){\n            var start = $(\"#\"+dom+\"Start_0\").val();\n            var end = $(\"#\"+dom+\"End_0\").val();\n            $(\"#\"+dom+\"Hidden\").val(start + \"-\" + end);\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        /**\n         * 从开始\n         */\n        startOn : function(dom) {\n            var start = $(\"#\"+dom+\"Start_1\").val();\n            var end = $(\"#\"+dom+\"End_1\").val();\n            $(\"#\"+dom+\"Hidden\").val(start + \"/\" + end);\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        /**\n         * 最后一天\n         */\n        lastDay : function(dom){\n            $(\"#\"+dom+\"Hidden\").val(\"L\");\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        /**\n         * 每周的某一天\n         */\n        weekOfDay : function(dom){\n            var start = $(\"#\"+dom+\"Start_0\").val();\n            var end = $(\"#\"+dom+\"End_0\").val();\n            $(\"#\"+dom+\"Hidden\").val(start + \"#\" + end);\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        /**\n         * 最后一周\n         */\n        lastWeek : function(dom){\n            var start = $(\"#\"+dom+\"Start_2\").val();\n            $(\"#\"+dom+\"Hidden\").val(start+\"L\");\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        /**\n         * 工作日\n         */\n        workDay : function(dom) {\n            var start = $(\"#\"+dom+\"Start_2\").val();\n            $(\"#\"+dom+\"Hidden\").val(start + \"W\");\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        initChangeEvent : function(){\n            var secondList = $(\".secondList\").children();\n            $(\"#sencond_appoint\").click(function(){\n                if (this.checked) {\n                    if ($(secondList).filter(\":checked\").length == 0) {\n                        $(secondList.eq(0)).attr(\"checked\", true);\n                    }\n                    secondList.eq(0).change();\n                }\n            });\n\n            secondList.change(function() {\n                var sencond_appoint = $(\"#sencond_appoint\").prop(\"checked\");\n                if (sencond_appoint) {\n                    var vals = [];\n                    secondList.each(function() {\n                        if (this.checked) {\n                            vals.push(this.value);\n                        }\n                    });\n                    var val = \"?\";\n                    if (vals.length > 0 && vals.length < 59) {\n                        val = vals.join(\",\");\n                    }else if(vals.length == 59){\n                        val = \"*\";\n                    }\n                    $(\"#secondHidden\").val(val);\n                }\n            });\n\n            var minList = $(\".minList\").children();\n            $(\"#min_appoint\").click(function(){\n                if (this.checked) {\n                    if ($(minList).filter(\":checked\").length == 0) {\n                        $(minList.eq(0)).attr(\"checked\", true);\n                    }\n                    minList.eq(0).change();\n                }\n            });\n\n            minList.change(function() {\n                var min_appoint = $(\"#min_appoint\").prop(\"checked\");\n                if (min_appoint) {\n                    var vals = [];\n                    minList.each(function() {\n                        if (this.checked) {\n                            vals.push(this.value);\n                        }\n                    });\n                    var val = \"?\";\n                    if (vals.length > 0 && vals.length < 59) {\n                        val = vals.join(\",\");\n                    }else if(vals.length == 59){\n                        val = \"*\";\n                    }\n                    $(\"#minHidden\").val(val);\n                }\n            });\n\n            var hourList = $(\".hourList\").children();\n            $(\"#hour_appoint\").click(function(){\n                if (this.checked) {\n                    if ($(hourList).filter(\":checked\").length == 0) {\n                        $(hourList.eq(0)).attr(\"checked\", true);\n                    }\n                    hourList.eq(0).change();\n                }\n            });\n\n            hourList.change(function() {\n                var hour_appoint = $(\"#hour_appoint\").prop(\"checked\");\n                if (hour_appoint) {\n                    var vals = [];\n                    hourList.each(function() {\n                        if (this.checked) {\n                            vals.push(this.value);\n                        }\n                    });\n                    var val = \"?\";\n                    if (vals.length > 0 && vals.length < 24) {\n                        val = vals.join(\",\");\n                    }else if(vals.length == 24){\n                        val = \"*\";\n                    }\n                    $(\"#hourHidden\").val(val);\n                }\n            });\n\n            var dayList = $(\".dayList\").children();\n            $(\"#day_appoint\").click(function(){\n                if (this.checked) {\n                    if ($(dayList).filter(\":checked\").length == 0) {\n                        $(dayList.eq(0)).attr(\"checked\", true);\n                    }\n                    dayList.eq(0).change();\n                }\n            });\n\n            dayList.change(function() {\n                var day_appoint = $(\"#day_appoint\").prop(\"checked\");\n                if (day_appoint) {\n                    var vals = [];\n                    dayList.each(function() {\n                        if (this.checked) {\n                            vals.push(this.value);\n                        }\n                    });\n                    var val = \"?\";\n                    if (vals.length > 0 && vals.length < 31) {\n                        val = vals.join(\",\");\n                    }else if(vals.length == 31){\n                        val = \"*\";\n                    }\n                   $(\"#dayHidden\").val(val);\n                }\n            });\n\n            var monthList = $(\".monthList\").children();\n            $(\"#month_appoint\").click(function(){\n                if (this.checked) {\n                    if ($(monthList).filter(\":checked\").length == 0) {\n                        $(monthList.eq(0)).attr(\"checked\", true);\n                    }\n                    monthList.eq(0).change();\n                }\n            });\n\n            monthList.change(function() {\n                var month_appoint = $(\"#month_appoint\").prop(\"checked\");\n                if (month_appoint) {\n                    var vals = [];\n                    monthList.each(function() {\n                        if (this.checked) {\n                            vals.push(this.value);\n                        }\n                    });\n                    var val = \"?\";\n                    if (vals.length > 0 && vals.length < 12) {\n                        val = vals.join(\",\");\n                    }else if(vals.length == 12){\n                        val = \"*\";\n                    }\n                    $(\"#monthHidden\").val(val);\n                }\n            });\n\n            var weekList = $(\".weekList\").children();\n            $(\"#week_appoint\").click(function(){\n                if (this.checked) {\n                    if ($(weekList).filter(\":checked\").length == 0) {\n                        $(weekList.eq(0)).attr(\"checked\", true);\n                    }\n                    weekList.eq(0).change();\n                }\n            });\n\n            weekList.change(function() {\n                var week_appoint = $(\"#week_appoint\").prop(\"checked\");\n                if (week_appoint) {\n                    var vals = [];\n                    weekList.each(function() {\n                        if (this.checked) {\n                            vals.push(this.value);\n                        }\n                    });\n                    var val = \"?\";\n                    if (vals.length > 0 && vals.length < 7) {\n                        val = vals.join(\",\");\n                    }else if(vals.length == 7){\n                        val = \"*\";\n                    }\n                   $(\"#weekHidden\").val(val);\n                }\n            });\n        },\n        initObj : function(strVal, strid){\n            var ary = null;\n            var objRadio = $(\"input[name='\" + strid + \"'\");\n            if (strVal == \"*\") {\n                objRadio.eq(0).attr(\"checked\", \"checked\");\n            } else if (strVal.split('-').length > 1) {\n                ary = strVal.split('-');\n                objRadio.eq(1).attr(\"checked\", \"checked\");\n                $(\"#\" + strid + \"Start_0\").val(ary[0]);\n                $(\"#\" + strid + \"End_0\").val(ary[1]);\n            } else if (strVal.split('/').length > 1) {\n                ary = strVal.split('/');\n                objRadio.eq(2).attr(\"checked\", \"checked\");\n                $(\"#\" + strid + \"Start_1\").val(ary[0]);\n                $(\"#\" + strid + \"End_1\").val(ary[1]);\n            } else {\n                objRadio.eq(3).attr(\"checked\", \"checked\");\n                if (strVal != \"?\") {\n                    ary = strVal.split(\",\");\n                    for (var i = 0; i < ary.length; i++) {\n                        $(\".\" + strid + \"List input[value='\" + ary[i] + \"']\").attr(\"checked\", \"checked\");\n                    }\n                    $.fn.cronGen.tools.initCheckBox(strid);\n                }\n            }\n        },\n        initDay : function(strVal) {\n            var ary = null;\n            var objRadio = $(\"input[name='day'\");\n            if (strVal == \"*\") {\n                objRadio.eq(0).attr(\"checked\", \"checked\");\n            } else if (strVal == \"?\") {\n                objRadio.eq(1).attr(\"checked\", \"checked\");\n            } else if (strVal.split('-').length > 1) {\n                ary = strVal.split('-');\n                objRadio.eq(2).attr(\"checked\", \"checked\");\n                $(\"#dayStart_0\").val(ary[0]);\n                $(\"#dayEnd_0\").val(ary[1]);\n            } else if (strVal.split('/').length > 1) {\n                ary = strVal.split('/');\n                objRadio.eq(3).attr(\"checked\", \"checked\");\n                $(\"#dayStart_1\").val(ary[0]);\n                $(\"#dayEnd_1\").val(ary[1]);\n            } else if (strVal.split('W').length > 1) {\n                ary = strVal.split('W');\n                objRadio.eq(4).attr(\"checked\", \"checked\");\n                $(\"#dayStart_2\").val(ary[0]);\n            } else if (strVal == \"L\") {\n                objRadio.eq(5).attr(\"checked\", \"checked\");\n            } else {\n                objRadio.eq(6).attr(\"checked\", \"checked\");\n                ary = strVal.split(\",\");\n                for (var i = 0; i < ary.length; i++) {\n                    $(\".dayList input[value='\" + ary[i] + \"']\").attr(\"checked\", \"checked\");\n                }\n                $.fn.cronGen.tools.initCheckBox(\"day\");\n            }\n        },\n        initMonth : function(strVal) {\n            var ary = null;\n            var objRadio = $(\"input[name='month'\");\n            if (strVal == \"*\") {\n                objRadio.eq(0).attr(\"checked\", \"checked\");\n            } else if (strVal == \"?\") {\n                objRadio.eq(1).attr(\"checked\", \"checked\");\n            } else if (strVal.split('-').length > 1) {\n                ary = strVal.split('-');\n                objRadio.eq(2).attr(\"checked\", \"checked\");\n                $(\"#monthStart_0\").val(ary[0]);\n                $(\"#monthEnd_0\").val(ary[1]);\n            } else if (strVal.split('/').length > 1) {\n                ary = strVal.split('/');\n                objRadio.eq(3).attr(\"checked\", \"checked\");\n                $(\"#monthStart_1\").val(ary[0]);\n                $(\"#monthEnd_1\").val(ary[1]);\n\n            } else {\n                objRadio.eq(4).attr(\"checked\", \"checked\");\n\n                ary = strVal.split(\",\");\n                for (var i = 0; i < ary.length; i++) {\n                    $(\".monthList input[value='\" + ary[i] + \"']\").attr(\"checked\", \"checked\");\n                }\n                $.fn.cronGen.tools.initCheckBox(\"month\");\n            }\n        },\n        initWeek : function(strVal) {\n            var ary = null;\n            var objRadio = $(\"input[name='week'\");\n            if (strVal == \"*\") {\n                objRadio.eq(0).attr(\"checked\", \"checked\");\n            } else if (strVal == \"?\") {\n                objRadio.eq(1).attr(\"checked\", \"checked\");\n            } else if (strVal.split('/').length > 1) {\n                ary = strVal.split('/');\n                objRadio.eq(2).attr(\"checked\", \"checked\");\n                $(\"#weekStart_0\").val(ary[0]);\n                $(\"#weekEnd_0\").val(ary[1]);\n            } else if (strVal.split('-').length > 1) {\n                ary = strVal.split('-');\n                objRadio.eq(3).attr(\"checked\", \"checked\");\n                $(\"#weekStart_1\").val(ary[0]);\n                $(\"#weekEnd_1\").val(ary[1]);\n            } else if (strVal.split('L').length > 1) {\n                ary = strVal.split('L');\n                objRadio.eq(4).attr(\"checked\", \"checked\");\n                $(\"#weekStart_2\").val(ary[0]);\n            } else {\n                objRadio.eq(5).attr(\"checked\", \"checked\");\n                ary = strVal.split(\",\");\n                for (var i = 0; i < ary.length; i++) {\n                    $(\".weekList input[value='\" + ary[i] + \"']\").attr(\"checked\", \"checked\");\n                }\n                $.fn.cronGen.tools.initCheckBox(\"week\");\n            }\n        },\n        initYear : function(strVal) {\n            var ary = null;\n            var objRadio = $(\"input[name='year'\");\n            if (strVal == \"*\") {\n                objRadio.eq(1).attr(\"checked\", \"checked\");\n            } else if (strVal.split('-').length > 1) {\n                ary = strVal.split('-');\n                objRadio.eq(2).attr(\"checked\", \"checked\");\n                $(\"#yearStart_0\").val(ary[0]);\n                $(\"#yearEnd_0\").val(ary[1]);\n            }\n        },\n        cronParse : function(cronExpress) {\n            //获取参数中表达式的值\n            if (cronExpress) {\n                var regs = cronExpress.split(' ');\n                $(\"#secondHidden\").val(regs[0]);\n                $(\"#minHidden\").val(regs[1]);\n                $(\"#hourHidden\").val(regs[2]);\n                $(\"#dayHidden\").val(regs[3]);\n                $(\"#monthHidden\").val(regs[4]);\n                $(\"#weekHidden\").val(regs[5]);\n\n                $.fn.cronGen.tools.initObj(regs[0], \"second\");\n                $.fn.cronGen.tools.initObj(regs[1], \"min\");\n                $.fn.cronGen.tools.initObj(regs[2], \"hour\");\n                $.fn.cronGen.tools.initDay(regs[3]);\n                $.fn.cronGen.tools.initMonth(regs[4]);\n                $.fn.cronGen.tools.initWeek(regs[5]);\n\n                if (regs.length > 6) {\n                    $(\"input[name=yearHidden]\").val(regs[6]);\n                    $.fn.cronGen.tools.initYear(regs[6]);\n                }\n            }\n    \t},\n        cronResult : function() {\n            var result;\n            var second = $(\"#secondHidden\").val();\n            second = second== \"\" ? \"*\":second;\n            var minute = $(\"#minHidden\").val();\n            minute = minute== \"\" ? \"*\":minute;\n            var hour = $(\"#hourHidden\").val();\n            hour = hour== \"\" ? \"*\":hour;\n            var day = $(\"#dayHidden\").val();\n            day = day== \"\" ? \"*\":day;\n            var month = $(\"#monthHidden\").val();\n            month = month== \"\" ? \"*\":month;\n            var week = $(\"#weekHidden\").val();\n            week = week== \"\" ? \"?\":week;\n            var year = $(\"#yearHidden\").val();\n            if(year!=\"\")\n            {\n                result = second+\" \"+minute+\" \"+hour+\" \"+day+\" \"+month+\" \"+week+\" \"+year;\n            }else\n            {\n                result = second+\" \"+minute+\" \"+hour+\" \"+day+\" \"+month+\" \"+week;\n            }\n            return result;\n        },\n        clearCheckbox : function(dom){\n        \t//清除选中的checkbox\n            var list = $(\".\"+dom+\"List\").children().filter(\":checked\");\n            if ($(list).length > 0) {\n            \t$.each(list, function(index){\n            \t\t$(this).attr(\"checked\", false);\n            \t\t$(this).attr(\"disabled\", \"disabled\");\n            \t\t$(this).change();\n            \t});\n            }\n        },\n        initCheckBox : function(dom) {\n        \t//移除checkbox禁用\n            var list = $(\".\"+dom+\"List\").children();\n            if ($(list).length > 0) {\n            \t$.each(list, function(index){\n            \t\t$(this).removeAttr(\"disabled\");\n            \t});\n            }\n        }\n    };\n})(jQuery);\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/cronGen/cronGen_en.js",
    "content": "﻿(function ($) {\n    // var resultsName = \"\";\n    var inputElement;\n    var displayElement;\n    $.fn.extend({\n        cronGen: function (options) {\n            if (options == null) {\n              options = {};\n            }\n            options = $.extend({}, $.fn.cronGen.defaultOptions, options);\n            //create top menu\n            var cronContainer = $(\"<div/>\", { id: \"CronContainer\", style: \"display:none;width:300px;height:300px;\" });\n            var mainDiv = $(\"<div/>\", { id: \"CronGenMainDiv\", style: \"width:410px;height:420px;\" });\n            var topMenu = $(\"<ul/>\", { \"class\": \"nav nav-tabs\", id: \"CronGenTabs\" });\n            $('<li/>', { 'class': 'active' }).html($('<a id=\"SecondlyTab\" href=\"#Secondly\">秒</a>')).appendTo(topMenu);\n            $('<li/>').html($('<a id=\"MinutesTab\" href=\"#Minutes\">Minute</a>')).appendTo(topMenu);\n            $('<li/>').html($('<a id=\"HourlyTab\" href=\"#Hourly\">Hour</a>')).appendTo(topMenu);\n            $('<li/>').html($('<a id=\"DailyTab\" href=\"#Daily\">Day</a>')).appendTo(topMenu);\n            $('<li/>').html($('<a id=\"MonthlyTab\" href=\"#Monthly\">Month</a>')).appendTo(topMenu);\n            $('<li/>').html($('<a id=\"WeeklyTab\" href=\"#Weekly\">Week</a>')).appendTo(topMenu);\n            $('<li/>').html($('<a id=\"YearlyTab\" href=\"#Yearly\">Year</a>')).appendTo(topMenu);\n            $(topMenu).appendTo(mainDiv);\n\n            //create what's inside the tabs\n            var container = $(\"<div/>\", { \"class\": \"container-fluid\", \"style\": \"margin-top: 30px;margin-left: -14px;\" });\n            var row = $(\"<div/>\", { \"class\": \"row-fluid\" });\n            var span12 = $(\"<div/>\", { \"class\": \"span12\" });\n            var tabContent = $(\"<div/>\", { \"class\": \"tab-content\", \"style\": \"border:0px; margin-top:-20px;\" });\n\n\n            //creating the secondsTab\n            var secondsTab = $(\"<div/>\", { \"class\": \"tab-pane active\", id: \"Secondly\" });\n            var seconds1 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"1\", name : \"second\"}).appendTo(seconds1);\n            $(seconds1).append(\"Per second, allowed wildcard[, - * /]\");\n            $(seconds1).appendTo(secondsTab);\n\n            var seconds2 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"2\", name : \"second\"}).appendTo(seconds2);\n            $(seconds2).append(\"Cycle, from\");\n            $(\"<input/>\",{type : \"text\", id : \"secondStart_0\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(seconds2);\n            $(seconds2).append(\"-\");\n            $(\"<input/>\",{type : \"text\", id : \"secondEnd_0\", value : \"2\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(seconds2);\n            $(seconds2).append(\"second\");\n            $(seconds2).appendTo(secondsTab);\n\n            var seconds3 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"3\", name : \"second\"}).appendTo(seconds3);\n            $(seconds3).append(\"from\");\n            $(\"<input/>\",{type : \"text\", id : \"secondStart_1\", value : \"0\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(seconds3);\n            $(seconds3).append(\"seconds start, per\");\n            $(\"<input/>\",{type : \"text\", id : \"secondEnd_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(seconds3);\n            $(seconds3).append(\"second execute once\");\n            $(seconds3).appendTo(secondsTab);\n\n            var seconds4 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"4\", name : \"second\", id: \"sencond_appoint\"}).appendTo(seconds4);\n            $(seconds4).append(\"specify\");\n            $(seconds4).appendTo(secondsTab);\n\n            $(secondsTab).append('<div class=\"imp secondList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"0\">00<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"1\">01<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"2\">02<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"3\">03<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"4\">04<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"5\">05<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"6\">06<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"7\">07<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"8\">08<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"9\">09</div>');\n            $(secondsTab).append('<div class=\"imp secondList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"10\">10<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"11\">11<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"12\">12<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"13\">13<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"14\">14<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"15\">15<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"16\">16<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"17\">17<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"18\">18<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"19\">19</div>');\n            $(secondsTab).append('<div class=\"imp secondList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"20\">20<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"21\">21<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"22\">22<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"23\">23<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"24\">24<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"25\">25<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"26\">26<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"27\">27<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"28\">28<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"29\">29</div>');\n            $(secondsTab).append('<div class=\"imp secondList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"30\">30<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"31\">31<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"32\">32<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"33\">33<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"34\">34<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"35\">35<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"36\">36<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"37\">37<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"38\">38<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"39\">39</div>');\n            $(secondsTab).append('<div class=\"imp secondList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"40\">40<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"41\">41<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"42\">42<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"43\">43<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"44\">44<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"45\">45<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"46\">46<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"47\">47<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"48\">48<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"49\">49</div>');\n            $(secondsTab).append('<div class=\"imp secondList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"50\">50<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"51\">51<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"52\">52<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"53\">53<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"54\">54<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"55\">55<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"56\">56<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"57\">57<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"58\">58<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"59\">59</div>');\n            $(\"<input/>\",{type : \"hidden\", id : \"secondHidden\"}).appendTo(secondsTab);\n            $(secondsTab).appendTo(tabContent);\n\n            //creating the minutesTab\n            var minutesTab = $(\"<div/>\", { \"class\": \"tab-pane\", id: \"Minutes\" });\n\n            var minutes1 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"1\", name : \"min\"}).appendTo(minutes1);\n            $(minutes1).append(\"Per minute, allowed wildcard[, - * /]\");\n            $(minutes1).appendTo(minutesTab);\n\n            var minutes2 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"2\", name : \"min\"}).appendTo(minutes2);\n            $(minutes2).append(\"Cycle, from\");\n            $(\"<input/>\",{type : \"text\", id : \"minStart_0\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(minutes2);\n            $(minutes2).append(\"-\");\n            $(\"<input/>\",{type : \"text\", id : \"minEnd_0\", value : \"2\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(minutes2);\n            $(minutes2).append(\"minute\");\n            $(minutes2).appendTo(minutesTab);\n\n            var minutes3 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"3\", name : \"min\"}).appendTo(minutes3);\n            $(minutes3).append(\"from\");\n            $(\"<input/>\",{type : \"text\", id : \"minStart_1\", value : \"0\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(minutes3);\n            $(minutes3).append(\"seconds start, per\");\n            $(\"<input/>\",{type : \"text\", id : \"minEnd_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(minutes3);\n            $(minutes3).append(\"second execute once\");\n            $(minutes3).appendTo(minutesTab);\n\n            var minutes4 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"4\", name : \"min\", id: \"min_appoint\"}).appendTo(minutes4);\n            $(minutes4).append(\"specify\");\n            $(minutes4).appendTo(minutesTab);\n\n            $(minutesTab).append('<div class=\"imp minList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"0\">00<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"1\">01<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"2\">02<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"3\">03<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"4\">04<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"5\">05<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"6\">06<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"7\">07<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"8\">08<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"9\">09</div>');\n            $(minutesTab).append('<div class=\"imp minList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"10\">10<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"11\">11<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"12\">12<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"13\">13<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"14\">14<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"15\">15<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"16\">16<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"17\">17<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"18\">18<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"19\">19</div>');\n            $(minutesTab).append('<div class=\"imp minList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"20\">20<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"21\">21<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"22\">22<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"23\">23<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"24\">24<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"25\">25<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"26\">26<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"27\">27<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"28\">28<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"29\">29</div>');\n            $(minutesTab).append('<div class=\"imp minList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"30\">30<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"31\">31<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"32\">32<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"33\">33<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"34\">34<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"35\">35<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"36\">36<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"37\">37<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"38\">38<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"39\">39</div>');\n            $(minutesTab).append('<div class=\"imp minList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"40\">40<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"41\">41<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"42\">42<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"43\">43<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"44\">44<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"45\">45<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"46\">46<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"47\">47<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"48\">48<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"49\">49</div>');\n            $(minutesTab).append('<div class=\"imp minList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"50\">50<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"51\">51<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"52\">52<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"53\">53<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"54\">54<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"55\">55<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"56\">56<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"57\">57<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"58\">58<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"59\">59</div>');\n            $(\"<input/>\",{type : \"hidden\", id : \"minHidden\"}).appendTo(minutesTab);\n            $(minutesTab).appendTo(tabContent);\n\n            //creating the hourlyTab\n            var hourlyTab = $(\"<div/>\", { \"class\": \"tab-pane\", id: \"Hourly\" });\n\n            var hourly1 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"1\", name : \"hour\"}).appendTo(hourly1);\n            $(hourly1).append(\"Per hour, allowed wildcard[, - * /]\");\n            $(hourly1).appendTo(hourlyTab);\n\n            var hourly2 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"2\", name : \"hour\"}).appendTo(hourly2);\n            $(hourly2).append(\"Cycle, from\");\n            $(\"<input/>\",{type : \"text\", id : \"hourStart_0\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(hourly2);\n            $(hourly2).append(\"-\");\n            $(\"<input/>\",{type : \"text\", id : \"hourEnd_0\", value : \"2\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(hourly2);\n            $(hourly2).append(\"hour\");\n            $(hourly2).appendTo(hourlyTab);\n\n            var hourly3 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"3\", name : \"hour\"}).appendTo(hourly3);\n            $(hourly3).append(\"from\");\n            $(\"<input/>\",{type : \"text\", id : \"hourStart_1\", value : \"0\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(hourly3);\n            $(hourly3).append(\"hour start, per\");\n            $(\"<input/>\",{type : \"text\", id : \"hourEnd_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(hourly3);\n            $(hourly3).append(\"hour execute once\");\n            $(hourly3).appendTo(hourlyTab);\n\n            var hourly4 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"4\", name : \"hour\", id: \"hour_appoint\"}).appendTo(hourly4);\n            $(hourly4).append(\"specify\");\n            $(hourly4).appendTo(hourlyTab);\n\n            $(hourlyTab).append('<div class=\"imp hourList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"0\">00<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"1\">01<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"2\">02<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"3\">03<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"4\">04<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"5\">05</div>');\n            $(hourlyTab).append('<div class=\"imp hourList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"6\">06<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"7\">07<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"8\">08<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"9\">09<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"10\">10<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"11\">11</div>');\n            $(hourlyTab).append('<div class=\"imp hourList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"12\">12<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"13\">13<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"14\">14<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"15\">15<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"16\">16<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"17\">17</div>');\n            $(hourlyTab).append('<div class=\"imp hourList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"18\">18<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"19\">19<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"20\">20<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"21\">21<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"22\">22<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"23\">23</div>');\n            $(\"<input/>\",{type : \"hidden\", id : \"hourHidden\"}).appendTo(hourlyTab);\n            $(hourlyTab).appendTo(tabContent);\n\n\n            //creating the dailyTab\n            var dailyTab = $(\"<div/>\", { \"class\": \"tab-pane\", id: \"Daily\" });\n\n            var daily1 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"1\", name : \"day\"}).appendTo(daily1);\n            $(daily1).append(\"Per day, allowed wildcard[, - * / L W]\");\n            $(daily1).appendTo(dailyTab);\n\n            var daily5 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"2\", name : \"day\"}).appendTo(daily5);\n            $(daily5).append(\"not specify\");\n            $(daily5).appendTo(dailyTab);\n\n            var daily2 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"3\", name : \"day\"}).appendTo(daily2);\n            $(daily2).append(\"Cycle, from\");\n            $(\"<input/>\",{type : \"text\", id : \"dayStart_0\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(daily2);\n            $(daily2).append(\"-\");\n            $(\"<input/>\",{type : \"text\", id : \"dayEnd_0\", value : \"2\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(daily2);\n            $(daily2).append(\"day\");\n            $(daily2).appendTo(dailyTab);\n\n            var daily3 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"4\", name : \"day\"}).appendTo(daily3);\n            $(daily3).append(\"from\");\n            $(\"<input/>\",{type : \"text\", id : \"dayStart_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(daily3);\n            $(daily3).append(\"day start, per\");\n            $(\"<input/>\",{type : \"text\", id : \"dayEnd_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(daily3);\n            $(daily3).append(\"day execute once\");\n            $(daily3).appendTo(dailyTab);\n\n            var daily6 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"5\", name : \"day\"}).appendTo(daily6);\n            $(daily6).append(\"The most recent working day on the 1\");\n            $(\"<input/>\",{type : \"text\", id : \"dayStart_2\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(daily6);\n            $(daily6).append(\" of each month\");\n            $(daily6).appendTo(dailyTab);\n\n            var daily7 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"6\", name : \"day\"}).appendTo(daily7);\n            $(daily7).append(\"The last day of the month\");\n            $(daily7).appendTo(dailyTab);\n\n            var daily4 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"7\", name : \"day\", id: \"day_appoint\"}).appendTo(daily4);\n            $(daily4).append(\"specify\");\n            $(daily4).appendTo(dailyTab);\n\n            $(dailyTab).append('<div class=\"imp dayList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"1\">01<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"2\">02<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"3\">03<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"4\">04<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"5\">05<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"6\">06<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"7\">07<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"8\">08<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"9\">09<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"10\">10</div>');\n            $(dailyTab).append('<div class=\"imp dayList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"11\">11<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"12\">12<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"13\">13<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"14\">14<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"15\">15<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"16\">16<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"17\">17<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"18\">18<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"19\">19<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"20\">20</div>');\n            $(dailyTab).append('<div class=\"imp dayList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"21\">21<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"22\">22<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"23\">23<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"24\">24<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"25\">25<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"26\">26<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"27\">27<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"28\">28<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"29\">29<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"30\">30</div>');\n            $(dailyTab).append('<div class=\"imp dayList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"31\">31</div>');\n            $(\"<input/>\",{type : \"hidden\", id : \"dayHidden\"}).appendTo(dailyTab);\n            $(dailyTab).appendTo(tabContent);\n\n\n            //creating the monthlyTab\n            var monthlyTab = $(\"<div/>\", { \"class\": \"tab-pane\", id: \"Monthly\" });\n\n            var monthly1 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"1\", name : \"month\"}).appendTo(monthly1);\n            $(monthly1).append(\"Per month, allowed wildcard[, - * /]\");\n            $(monthly1).appendTo(monthlyTab);\n\n            var monthly2 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"2\", name : \"month\"}).appendTo(monthly2);\n            $(monthly2).append(\"not specify\");\n            $(monthly2).appendTo(monthlyTab);\n\n            var monthly3 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"3\", name : \"month\"}).appendTo(monthly3);\n            $(monthly3).append(\"Cycle, from\");\n            $(\"<input/>\",{type : \"text\", id : \"monthStart_0\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(monthly3);\n            $(monthly3).append(\"-\");\n            $(\"<input/>\",{type : \"text\", id : \"monthEnd_0\", value : \"2\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(monthly3);\n            $(monthly3).append(\"month\");\n            $(monthly3).appendTo(monthlyTab);\n\n            var monthly4 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"4\", name : \"month\"}).appendTo(monthly4);\n            $(monthly4).append(\"Starting from \");\n            $(\"<input/>\",{type : \"text\", id : \"monthStart_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(monthly4);\n            $(monthly4).append(\"day, once every\");\n            $(\"<input/>\",{type : \"text\", id : \"monthEnd_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(monthly4);\n            $(monthly4).append(\"month\");\n            $(monthly4).appendTo(monthlyTab);\n\n            var monthly5 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"5\", name : \"month\", id: \"month_appoint\"}).appendTo(monthly5);\n            $(monthly5).append(\"specify\");\n            $(monthly5).appendTo(monthlyTab);\n\n            $(monthlyTab).append('<div class=\"imp monthList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"1\">01<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"2\">02<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"3\">03<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"4\">04<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"5\">05<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"6\">06</div>');\n            $(monthlyTab).append('<div class=\"imp monthList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"7\">07<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"8\">08<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"9\">09<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"10\">10<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"11\">11<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"12\">12</div>');\n            $(\"<input/>\",{type : \"hidden\", id : \"monthHidden\"}).appendTo(monthlyTab);\n            $(monthlyTab).appendTo(tabContent);\n\n            //creating the weeklyTab\n            var weeklyTab = $(\"<div/>\", { \"class\": \"tab-pane\", id: \"Weekly\" });\n\n            var weekly1 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"1\", name : \"week\"}).appendTo(weekly1);\n            $(weekly1).append(\"Per week, allowed wildcard[, - * / L #]\");\n            $(weekly1).appendTo(weeklyTab);\n\n            var weekly2 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"2\", name : \"week\"}).appendTo(weekly2);\n            $(weekly2).append(\"not specify\");\n            $(weekly2).appendTo(weeklyTab);\n\n            var weekly3 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"3\", name : \"week\"}).appendTo(weekly3);\n            $(weekly3).append(\"Cycle, from week\");\n            $(\"<input/>\",{type : \"text\", id : \"weekStart_0\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(weekly3);\n            $(weekly3).append(\"-\");\n            $(\"<input/>\",{type : \"text\", id : \"weekEnd_0\", value : \"2\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(weekly3);\n            $(weekly3).appendTo(weeklyTab);\n\n            var weekly4 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"4\", name : \"week\"}).appendTo(weekly4);\n            $(weekly4).append(\"The\");\n            $(\"<input/>\",{type : \"text\", id : \"weekStart_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(weekly4);\n            $(weekly4).append(\"th week, and day \");\n            $(\"<input/>\",{type : \"text\", id : \"weekEnd_1\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(weekly4);\n            $(weekly4).appendTo(weeklyTab);\n\n            var weekly5 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"5\", name : \"week\"}).appendTo(weekly5);\n            $(weekly5).append(\"Last week of the month\");\n            $(\"<input/>\",{type : \"text\", id : \"weekStart_2\", value : \"1\", style:\"width:35px; height:20px; text-align: center; margin: 0 3px;\"}).appendTo(weekly5);\n            $(weekly5).appendTo(weeklyTab);\n\n            var weekly6 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"6\", name : \"week\", id: \"week_appoint\"}).appendTo(weekly6);\n            $(weekly6).append(\"specify\");\n            $(weekly6).appendTo(weeklyTab);\n\n            $(weeklyTab).append('<div class=\"imp weekList\"><input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"1\">1<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"2\">2<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"3\">3<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"4\">4<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"5\">5<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"6\">6<input type=\"checkbox\" disabled=\"disabled\" style=\"margin-left: 5px\"  value=\"7\">7</div>');\n\n            $(\"<input/>\",{type : \"hidden\", id : \"weekHidden\"}).appendTo(weeklyTab);\n            $(weeklyTab).appendTo(tabContent);\n\n            //creating the yearlyTab\n            var yearlyTab = $(\"<div/>\", { \"class\": \"tab-pane\", id: \"Yearly\" });\n\n            var yearly1 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"1\", name : \"year\"}).appendTo(yearly1);\n            $(yearly1).append(\"not specify allowed wildcard[, - * /] not required\");\n            $(yearly1).appendTo(yearlyTab);\n\n            var yearly3 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"2\", name : \"year\"}).appendTo(yearly3);\n            $(yearly3).append(\"Per year\");\n            $(yearly3).appendTo(yearlyTab);\n\n            var yearly2 = $(\"<div/>\",{\"class\":\"line\"});\n            $(\"<input/>\",{type : \"radio\", value : \"3\", name : \"year\"}).appendTo(yearly2);\n            $(yearly2).append(\"Cycle, from \");\n            $(\"<input/>\",{type : \"text\", id : \"yearStart_0\", value : \"2016\", style:\"width:45px; height:20px;\"}).appendTo(yearly2);\n            $(yearly2).append(\"-\");\n            $(\"<input/>\",{type : \"text\", id : \"yearEnd_0\", value : \"2017\", style:\"width:45px; height:20px;\"}).appendTo(yearly2);\n            $(yearly2).append(\"year\");\n            $(yearly2).appendTo(yearlyTab);\n            $(\"<input/>\",{type : \"hidden\", id : \"yearHidden\"}).appendTo(yearlyTab);\n            $(yearlyTab).appendTo(tabContent);\n\n            $(tabContent).appendTo(span12);\n\n            //creating the button and results input\n            // resultsName = $(this).prop(\"id\");\n            // $(this).prop(\"name\", resultsName);\n\n            var runTime = '<br style=\"padding-top: 10px\"><label>Recent Run Time: </label></br><textarea id=\"runTime\" rows=\"6\" style=\"width: 90%;resize: none;background: none;border: none;outline: none;\" readonly = readonly></textarea></div>';\n\n            $(span12).appendTo(row);\n            $(row).appendTo(container);\n            $(container).appendTo(mainDiv);\n            $(runTime).appendTo(mainDiv);\n            $(cronContainer).append(mainDiv);\n\n            var that = $(this);\n\n            // Hide the original input\n            that.hide();\n\n            // Replace the input with an input group\n            var $g = $(\"<div>\").addClass(\"input-group\");\n            // Add an input\n            var $i = $(\"<input>\", { type: 'text', placeholder: 'cron expression...', name: 'cronGen_display' }).addClass(\"form-control\").val($(that).val());\n            $i.appendTo($g);\n            // Add the button\n            var $b = $(\"<button class=\\\"btn btn-default\\\"><i class=\\\"fa fa-edit\\\"></i></button>\");\n            // Put button inside span\n            var $s = $(\"<span>\").addClass(\"input-group-btn\");\n            $b.appendTo($s);\n            $s.appendTo($g);\n\n            $(this).before($g);\n\n            inputElement = that;\n            displayElement = $i;\n\n            $b.popover({\n                html: true,\n                content: function () {\n                    return $(cronContainer).html();\n                },\n                template: '<div class=\"popover\" style=\"max-width:500px !important; width:425px;left:-341.656px;\"><div class=\"arrow\"></div><div class=\"popover-inner\"><h3 class=\"popover-title\"></h3><div class=\"popover-content\"><p></p></div></div></div>',\n                sanitize:false,\n                placement: options.direction\n\n            }).on('click', function (e) {\n                if (inputElement.val().trim() !== '') {\n                    refreshRunTime();\n                }\n                e.preventDefault();\n\n                //fillDataOfMinutesAndHoursSelectOptions();\n                //fillDayWeekInMonth();\n                //fillInWeekDays();\n                //fillInMonths();\n\n                $.fn.cronGen.tools.cronParse(inputElement.val());\n\n                //绑定指定事件\n                $.fn.cronGen.tools.initChangeEvent();\n\n\n                $('#CronGenTabs a').click(function (e) {\n                    e.preventDefault();\n                    $(this).tab('show');\n                    //generate();\n                });\n                $(\"#CronGenMainDiv select,input\").change(function (e) {\n                    generate();\n                    refreshRunTime();\n                });\n                $(\"#CronGenMainDiv input\").focus(function (e) {\n                    generate();\n                });\n                //generate();\n            });\n            return;\n        }\n    });\n\n\n    var fillInMonths = function () {\n        var days = [\n            { text: \"January\", val: \"1\" },\n            { text: \"February\", val: \"2\" },\n            { text: \"March\", val: \"3\" },\n            { text: \"April\", val: \"4\" },\n            { text: \"May\", val: \"5\" },\n            { text: \"June\", val: \"6\" },\n            { text: \"July\", val: \"7\" },\n            { text: \"August\", val: \"8\" },\n            { text: \"September\", val: \"9\" },\n            { text: \"October\", val: \"10\" },\n            { text: \"November\", val: \"11\" },\n            { text: \"December\", val: \"12\" }\n        ];\n        $(\".months\").each(function () {\n            fillOptions(this, days);\n        });\n    };\n\n    var fillOptions = function (elements, options) {\n        for (var i = 0; i < options.length; i++)\n            $(elements).append(\"<option value='\" + options[i].val + \"'>\" + options[i].text + \"</option>\");\n    };\n    var fillDataOfMinutesAndHoursSelectOptions = function () {\n        for (var i = 0; i < 60; i++) {\n            if (i < 24) {\n                $(\".hours\").each(function () { $(this).append(timeSelectOption(i)); });\n            }\n            $(\".minutes\").each(function () { $(this).append(timeSelectOption(i)); });\n        }\n    };\n    var fillInWeekDays = function () {\n        var days = [\n            { text: \"Tuesday\", val: \"2\" },\n            { text: \"Wednesday\", val: \"3\" },\n            { text: \"Thursday\", val: \"4\" },\n            { text: \"Friday\", val: \"5\" },\n            { text: \"Saturday\", val: \"6\" },\n            { text: \"Sunday\", val: \"7\" },\n            { text: \"Monday\", val: \"1\" }\n        ];\n        $(\".week-days\").each(function () {\n            fillOptions(this, days);\n        });\n\n    };\n    var fillDayWeekInMonth = function () {\n        var days = [\n            { text: \"First\", val: \"1\" },\n            { text: \"Second\", val: \"2\" },\n            { text: \"Third\", val: \"3\" },\n            { text: \"Fourth\", val: \"4\" }\n        ];\n        $(\".day-order-in-month\").each(function () {\n            fillOptions(this, days);\n        });\n    };\n    var displayTimeUnit = function (unit) {\n        if (unit.toString().length == 1)\n            return \"0\" + unit;\n        return unit;\n    };\n    var timeSelectOption = function (i) {\n        return \"<option id='\" + i + \"'>\" + displayTimeUnit(i) + \"</option>\";\n    };\n\n    var generate = function () {\n\n        var activeTab = $(\"ul#CronGenTabs li.active a\").prop(\"id\");\n        if (activeTab == undefined) {\n            return;\n        }\n        var results = \"\";\n        switch (activeTab) {\n            case \"SecondlyTab\":\n                switch ($(\"input:radio[name=second]:checked\").val()) {\n                    case \"1\":\n                        $.fn.cronGen.tools.everyTime(\"second\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"2\":\n                        $.fn.cronGen.tools.cycle(\"second\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"3\":\n                        $.fn.cronGen.tools.startOn(\"second\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"4\":\n                    \t$.fn.cronGen.tools.initCheckBox(\"second\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                }\n                break;\n            case \"MinutesTab\":\n                switch ($(\"input:radio[name=min]:checked\").val()) {\n                    case \"1\":\n                        $.fn.cronGen.tools.everyTime(\"min\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"2\":\n                        $.fn.cronGen.tools.cycle(\"min\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"3\":\n                        $.fn.cronGen.tools.startOn(\"min\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"4\":\n                    \t$.fn.cronGen.tools.initCheckBox(\"min\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                }\n                break;\n            case \"HourlyTab\":\n                switch ($(\"input:radio[name=hour]:checked\").val()) {\n                    case \"1\":\n                       $.fn.cronGen.tools.everyTime(\"hour\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"2\":\n                       $.fn.cronGen.tools.cycle(\"hour\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"3\":\n                        $.fn.cronGen.tools.startOn(\"hour\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"4\":\n                    \t$.fn.cronGen.tools.initCheckBox(\"hour\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                }\n                break;\n            case \"DailyTab\":\n                switch ($(\"input:radio[name=day]:checked\").val()) {\n                    case \"1\":\n                        $.fn.cronGen.tools.everyTime(\"day\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"2\":\n                        $.fn.cronGen.tools.unAppoint(\"day\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"3\":\n                        $.fn.cronGen.tools.cycle(\"day\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"4\":\n                        $.fn.cronGen.tools.startOn(\"day\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"5\":\n                        $.fn.cronGen.tools.workDay(\"day\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"6\":\n                        $.fn.cronGen.tools.lastDay(\"day\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"7\":\n                    \t$.fn.cronGen.tools.initCheckBox(\"day\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                }\n                break;\n            case \"WeeklyTab\":\n                switch ($(\"input:radio[name=week]:checked\").val()) {\n                    case \"1\":\n                        $.fn.cronGen.tools.everyTime(\"week\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"2\":\n                        $.fn.cronGen.tools.unAppoint(\"week\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"3\":\n                        $.fn.cronGen.tools.cycle(\"week\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"4\":\n                        $.fn.cronGen.tools.startOn(\"week\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"5\":\n                        $.fn.cronGen.tools.lastWeek(\"week\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"6\":\n                    \t$.fn.cronGen.tools.initCheckBox(\"week\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                }\n                break;\n            case \"MonthlyTab\":\n                switch ($(\"input:radio[name=month]:checked\").val()) {\n                    case \"1\":\n                        $.fn.cronGen.tools.everyTime(\"month\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"2\":\n                        $.fn.cronGen.tools.unAppoint(\"month\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"3\":\n                        $.fn.cronGen.tools.cycle(\"month\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"4\":\n                        $.fn.cronGen.tools.startOn(\"month\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"5\":\n                    \t$.fn.cronGen.tools.initCheckBox(\"month\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                }\n                break;\n            case \"YearlyTab\":\n                switch ($(\"input:radio[name=year]:checked\").val()) {\n                    case \"1\":\n                        $.fn.cronGen.tools.unAppoint(\"year\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"2\":\n                        $.fn.cronGen.tools.everyTime(\"year\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                    case \"3\":\n                        $.fn.cronGen.tools.cycle(\"year\");\n                        results = $.fn.cronGen.tools.cronResult();\n                        break;\n                }\n                break;\n        }\n\n        // Update original control\n        inputElement.val(results);\n        // Update display\n        displayElement.val(results);\n    };\n\n    var refreshRunTime = function () {\n        $.ajax({\n            type : 'GET',\n            url : base_url + \"/jobinfo/nextTriggerTime\",\n            data : {\n                \"scheduleType\" : 'CRON',\n                \"scheduleConf\" : inputElement.val()\n            },\n            dataType : \"json\",\n            success : function(data){\n                if (data.code === 200) {\n                    $('#runTime').val(data.content.join(\"\\n\"));\n                } else {\n                    $('#runTime').val(data.msg);\n                }\n            }\n        });\n    };\n\n})(jQuery);\n\n(function($) {\n    $.fn.cronGen.defaultOptions = {\n        direction : 'bottom'\n    };\n    $.fn.cronGen.tools = {\n        /**\n         * 每周期\n         */\n        everyTime : function(dom){\n            $(\"#\"+dom+\"Hidden\").val(\"*\");\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        /**\n         * 不指定\n         */\n        unAppoint : function(dom){\n            var val = \"?\";\n            if (dom == \"year\")\n            {\n                val = \"\";\n            }\n            $(\"#\"+dom+\"Hidden\").val(val);\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        /**\n         * 周期\n         */\n        cycle : function(dom){\n            var start = $(\"#\"+dom+\"Start_0\").val();\n            var end = $(\"#\"+dom+\"End_0\").val();\n            $(\"#\"+dom+\"Hidden\").val(start + \"-\" + end);\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        /**\n         * 从开始\n         */\n        startOn : function(dom) {\n            var start = $(\"#\"+dom+\"Start_1\").val();\n            var end = $(\"#\"+dom+\"End_1\").val();\n            $(\"#\"+dom+\"Hidden\").val(start + \"/\" + end);\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        /**\n         * 最后一天\n         */\n        lastDay : function(dom){\n            $(\"#\"+dom+\"Hidden\").val(\"L\");\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        /**\n         * 每周的某一天\n         */\n        weekOfDay : function(dom){\n            var start = $(\"#\"+dom+\"Start_0\").val();\n            var end = $(\"#\"+dom+\"End_0\").val();\n            $(\"#\"+dom+\"Hidden\").val(start + \"#\" + end);\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        /**\n         * 最后一周\n         */\n        lastWeek : function(dom){\n            var start = $(\"#\"+dom+\"Start_2\").val();\n            $(\"#\"+dom+\"Hidden\").val(start+\"L\");\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        /**\n         * 工作日\n         */\n        workDay : function(dom) {\n            var start = $(\"#\"+dom+\"Start_2\").val();\n            $(\"#\"+dom+\"Hidden\").val(start + \"W\");\n            $.fn.cronGen.tools.clearCheckbox(dom);\n        },\n        initChangeEvent : function(){\n            var secondList = $(\".secondList\").children();\n            $(\"#sencond_appoint\").click(function(){\n                if (this.checked) {\n                    if ($(secondList).filter(\":checked\").length == 0) {\n                        $(secondList.eq(0)).attr(\"checked\", true);\n                    }\n                    secondList.eq(0).change();\n                }\n            });\n\n            secondList.change(function() {\n                var sencond_appoint = $(\"#sencond_appoint\").prop(\"checked\");\n                if (sencond_appoint) {\n                    var vals = [];\n                    secondList.each(function() {\n                        if (this.checked) {\n                            vals.push(this.value);\n                        }\n                    });\n                    var val = \"?\";\n                    if (vals.length > 0 && vals.length < 59) {\n                        val = vals.join(\",\");\n                    }else if(vals.length == 59){\n                        val = \"*\";\n                    }\n                    $(\"#secondHidden\").val(val);\n                }\n            });\n\n            var minList = $(\".minList\").children();\n            $(\"#min_appoint\").click(function(){\n                if (this.checked) {\n                    if ($(minList).filter(\":checked\").length == 0) {\n                        $(minList.eq(0)).attr(\"checked\", true);\n                    }\n                    minList.eq(0).change();\n                }\n            });\n\n            minList.change(function() {\n                var min_appoint = $(\"#min_appoint\").prop(\"checked\");\n                if (min_appoint) {\n                    var vals = [];\n                    minList.each(function() {\n                        if (this.checked) {\n                            vals.push(this.value);\n                        }\n                    });\n                    var val = \"?\";\n                    if (vals.length > 0 && vals.length < 59) {\n                        val = vals.join(\",\");\n                    }else if(vals.length == 59){\n                        val = \"*\";\n                    }\n                    $(\"#minHidden\").val(val);\n                }\n            });\n\n            var hourList = $(\".hourList\").children();\n            $(\"#hour_appoint\").click(function(){\n                if (this.checked) {\n                    if ($(hourList).filter(\":checked\").length == 0) {\n                        $(hourList.eq(0)).attr(\"checked\", true);\n                    }\n                    hourList.eq(0).change();\n                }\n            });\n\n            hourList.change(function() {\n                var hour_appoint = $(\"#hour_appoint\").prop(\"checked\");\n                if (hour_appoint) {\n                    var vals = [];\n                    hourList.each(function() {\n                        if (this.checked) {\n                            vals.push(this.value);\n                        }\n                    });\n                    var val = \"?\";\n                    if (vals.length > 0 && vals.length < 24) {\n                        val = vals.join(\",\");\n                    }else if(vals.length == 24){\n                        val = \"*\";\n                    }\n                    $(\"#hourHidden\").val(val);\n                }\n            });\n\n            var dayList = $(\".dayList\").children();\n            $(\"#day_appoint\").click(function(){\n                if (this.checked) {\n                    if ($(dayList).filter(\":checked\").length == 0) {\n                        $(dayList.eq(0)).attr(\"checked\", true);\n                    }\n                    dayList.eq(0).change();\n                }\n            });\n\n            dayList.change(function() {\n                var day_appoint = $(\"#day_appoint\").prop(\"checked\");\n                if (day_appoint) {\n                    var vals = [];\n                    dayList.each(function() {\n                        if (this.checked) {\n                            vals.push(this.value);\n                        }\n                    });\n                    var val = \"?\";\n                    if (vals.length > 0 && vals.length < 31) {\n                        val = vals.join(\",\");\n                    }else if(vals.length == 31){\n                        val = \"*\";\n                    }\n                   $(\"#dayHidden\").val(val);\n                }\n            });\n\n            var monthList = $(\".monthList\").children();\n            $(\"#month_appoint\").click(function(){\n                if (this.checked) {\n                    if ($(monthList).filter(\":checked\").length == 0) {\n                        $(monthList.eq(0)).attr(\"checked\", true);\n                    }\n                    monthList.eq(0).change();\n                }\n            });\n\n            monthList.change(function() {\n                var month_appoint = $(\"#month_appoint\").prop(\"checked\");\n                if (month_appoint) {\n                    var vals = [];\n                    monthList.each(function() {\n                        if (this.checked) {\n                            vals.push(this.value);\n                        }\n                    });\n                    var val = \"?\";\n                    if (vals.length > 0 && vals.length < 12) {\n                        val = vals.join(\",\");\n                    }else if(vals.length == 12){\n                        val = \"*\";\n                    }\n                    $(\"#monthHidden\").val(val);\n                }\n            });\n\n            var weekList = $(\".weekList\").children();\n            $(\"#week_appoint\").click(function(){\n                if (this.checked) {\n                    if ($(weekList).filter(\":checked\").length == 0) {\n                        $(weekList.eq(0)).attr(\"checked\", true);\n                    }\n                    weekList.eq(0).change();\n                }\n            });\n\n            weekList.change(function() {\n                var week_appoint = $(\"#week_appoint\").prop(\"checked\");\n                if (week_appoint) {\n                    var vals = [];\n                    weekList.each(function() {\n                        if (this.checked) {\n                            vals.push(this.value);\n                        }\n                    });\n                    var val = \"?\";\n                    if (vals.length > 0 && vals.length < 7) {\n                        val = vals.join(\",\");\n                    }else if(vals.length == 7){\n                        val = \"*\";\n                    }\n                   $(\"#weekHidden\").val(val);\n                }\n            });\n        },\n        initObj : function(strVal, strid){\n            var ary = null;\n            var objRadio = $(\"input[name='\" + strid + \"'\");\n            if (strVal == \"*\") {\n                objRadio.eq(0).attr(\"checked\", \"checked\");\n            } else if (strVal.split('-').length > 1) {\n                ary = strVal.split('-');\n                objRadio.eq(1).attr(\"checked\", \"checked\");\n                $(\"#\" + strid + \"Start_0\").val(ary[0]);\n                $(\"#\" + strid + \"End_0\").val(ary[1]);\n            } else if (strVal.split('/').length > 1) {\n                ary = strVal.split('/');\n                objRadio.eq(2).attr(\"checked\", \"checked\");\n                $(\"#\" + strid + \"Start_1\").val(ary[0]);\n                $(\"#\" + strid + \"End_1\").val(ary[1]);\n            } else {\n                objRadio.eq(3).attr(\"checked\", \"checked\");\n                if (strVal != \"?\") {\n                    ary = strVal.split(\",\");\n                    for (var i = 0; i < ary.length; i++) {\n                        $(\".\" + strid + \"List input[value='\" + ary[i] + \"']\").attr(\"checked\", \"checked\");\n                    }\n                    $.fn.cronGen.tools.initCheckBox(strid);\n                }\n            }\n        },\n        initDay : function(strVal) {\n            var ary = null;\n            var objRadio = $(\"input[name='day'\");\n            if (strVal == \"*\") {\n                objRadio.eq(0).attr(\"checked\", \"checked\");\n            } else if (strVal == \"?\") {\n                objRadio.eq(1).attr(\"checked\", \"checked\");\n            } else if (strVal.split('-').length > 1) {\n                ary = strVal.split('-');\n                objRadio.eq(2).attr(\"checked\", \"checked\");\n                $(\"#dayStart_0\").val(ary[0]);\n                $(\"#dayEnd_0\").val(ary[1]);\n            } else if (strVal.split('/').length > 1) {\n                ary = strVal.split('/');\n                objRadio.eq(3).attr(\"checked\", \"checked\");\n                $(\"#dayStart_1\").val(ary[0]);\n                $(\"#dayEnd_1\").val(ary[1]);\n            } else if (strVal.split('W').length > 1) {\n                ary = strVal.split('W');\n                objRadio.eq(4).attr(\"checked\", \"checked\");\n                $(\"#dayStart_2\").val(ary[0]);\n            } else if (strVal == \"L\") {\n                objRadio.eq(5).attr(\"checked\", \"checked\");\n            } else {\n                objRadio.eq(6).attr(\"checked\", \"checked\");\n                ary = strVal.split(\",\");\n                for (var i = 0; i < ary.length; i++) {\n                    $(\".dayList input[value='\" + ary[i] + \"']\").attr(\"checked\", \"checked\");\n                }\n                $.fn.cronGen.tools.initCheckBox(\"day\");\n            }\n        },\n        initMonth : function(strVal) {\n            var ary = null;\n            var objRadio = $(\"input[name='month'\");\n            if (strVal == \"*\") {\n                objRadio.eq(0).attr(\"checked\", \"checked\");\n            } else if (strVal == \"?\") {\n                objRadio.eq(1).attr(\"checked\", \"checked\");\n            } else if (strVal.split('-').length > 1) {\n                ary = strVal.split('-');\n                objRadio.eq(2).attr(\"checked\", \"checked\");\n                $(\"#monthStart_0\").val(ary[0]);\n                $(\"#monthEnd_0\").val(ary[1]);\n            } else if (strVal.split('/').length > 1) {\n                ary = strVal.split('/');\n                objRadio.eq(3).attr(\"checked\", \"checked\");\n                $(\"#monthStart_1\").val(ary[0]);\n                $(\"#monthEnd_1\").val(ary[1]);\n\n            } else {\n                objRadio.eq(4).attr(\"checked\", \"checked\");\n\n                ary = strVal.split(\",\");\n                for (var i = 0; i < ary.length; i++) {\n                    $(\".monthList input[value='\" + ary[i] + \"']\").attr(\"checked\", \"checked\");\n                }\n                $.fn.cronGen.tools.initCheckBox(\"month\");\n            }\n        },\n        initWeek : function(strVal) {\n            var ary = null;\n            var objRadio = $(\"input[name='week'\");\n            if (strVal == \"*\") {\n                objRadio.eq(0).attr(\"checked\", \"checked\");\n            } else if (strVal == \"?\") {\n                objRadio.eq(1).attr(\"checked\", \"checked\");\n            } else if (strVal.split('/').length > 1) {\n                ary = strVal.split('/');\n                objRadio.eq(2).attr(\"checked\", \"checked\");\n                $(\"#weekStart_0\").val(ary[0]);\n                $(\"#weekEnd_0\").val(ary[1]);\n            } else if (strVal.split('-').length > 1) {\n                ary = strVal.split('-');\n                objRadio.eq(3).attr(\"checked\", \"checked\");\n                $(\"#weekStart_1\").val(ary[0]);\n                $(\"#weekEnd_1\").val(ary[1]);\n            } else if (strVal.split('L').length > 1) {\n                ary = strVal.split('L');\n                objRadio.eq(4).attr(\"checked\", \"checked\");\n                $(\"#weekStart_2\").val(ary[0]);\n            } else {\n                objRadio.eq(5).attr(\"checked\", \"checked\");\n                ary = strVal.split(\",\");\n                for (var i = 0; i < ary.length; i++) {\n                    $(\".weekList input[value='\" + ary[i] + \"']\").attr(\"checked\", \"checked\");\n                }\n                $.fn.cronGen.tools.initCheckBox(\"week\");\n            }\n        },\n        initYear : function(strVal) {\n            var ary = null;\n            var objRadio = $(\"input[name='year'\");\n            if (strVal == \"*\") {\n                objRadio.eq(1).attr(\"checked\", \"checked\");\n            } else if (strVal.split('-').length > 1) {\n                ary = strVal.split('-');\n                objRadio.eq(2).attr(\"checked\", \"checked\");\n                $(\"#yearStart_0\").val(ary[0]);\n                $(\"#yearEnd_0\").val(ary[1]);\n            }\n        },\n        cronParse : function(cronExpress) {\n            //获取参数中表达式的值\n            if (cronExpress) {\n                var regs = cronExpress.split(' ');\n                $(\"#secondHidden\").val(regs[0]);\n                $(\"#minHidden\").val(regs[1]);\n                $(\"#hourHidden\").val(regs[2]);\n                $(\"#dayHidden\").val(regs[3]);\n                $(\"#monthHidden\").val(regs[4]);\n                $(\"#weekHidden\").val(regs[5]);\n\n                $.fn.cronGen.tools.initObj(regs[0], \"second\");\n                $.fn.cronGen.tools.initObj(regs[1], \"min\");\n                $.fn.cronGen.tools.initObj(regs[2], \"hour\");\n                $.fn.cronGen.tools.initDay(regs[3]);\n                $.fn.cronGen.tools.initMonth(regs[4]);\n                $.fn.cronGen.tools.initWeek(regs[5]);\n\n                if (regs.length > 6) {\n                    $(\"input[name=yearHidden]\").val(regs[6]);\n                    $.fn.cronGen.tools.initYear(regs[6]);\n                }\n            }\n    \t},\n        cronResult : function() {\n            var result;\n            var second = $(\"#secondHidden\").val();\n            second = second== \"\" ? \"*\":second;\n            var minute = $(\"#minHidden\").val();\n            minute = minute== \"\" ? \"*\":minute;\n            var hour = $(\"#hourHidden\").val();\n            hour = hour== \"\" ? \"*\":hour;\n            var day = $(\"#dayHidden\").val();\n            day = day== \"\" ? \"*\":day;\n            var month = $(\"#monthHidden\").val();\n            month = month== \"\" ? \"*\":month;\n            var week = $(\"#weekHidden\").val();\n            week = week== \"\" ? \"?\":week;\n            var year = $(\"#yearHidden\").val();\n            if(year!=\"\")\n            {\n                result = second+\" \"+minute+\" \"+hour+\" \"+day+\" \"+month+\" \"+week+\" \"+year;\n            }else\n            {\n                result = second+\" \"+minute+\" \"+hour+\" \"+day+\" \"+month+\" \"+week;\n            }\n            return result;\n        },\n        clearCheckbox : function(dom){\n        \t//清除选中的checkbox\n            var list = $(\".\"+dom+\"List\").children().filter(\":checked\");\n            if ($(list).length > 0) {\n            \t$.each(list, function(index){\n            \t\t$(this).attr(\"checked\", false);\n            \t\t$(this).attr(\"disabled\", \"disabled\");\n            \t\t$(this).change();\n            \t});\n            }\n        },\n        initCheckBox : function(dom) {\n        \t//移除checkbox禁用\n            var list = $(\".\"+dom+\"List\").children();\n            if ($(list).length > 0) {\n            \t$.each(list, function(index){\n            \t\t$(this).removeAttr(\"disabled\");\n            \t});\n            }\n        }\n    };\n})(jQuery);\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/jquery/jquery.cookie.js",
    "content": "/*!\n * jQuery Cookie Plugin v1.4.1\n * https://github.com/carhartl/jquery-cookie\n *\n * Copyright 2013 Klaus Hartl\n * Released under the MIT license\n */\n(function (factory) {\n\tif (typeof define === 'function' && define.amd) {\n\t\t// AMD\n\t\tdefine(['jquery'], factory);\n\t} else if (typeof exports === 'object') {\n\t\t// CommonJS\n\t\tfactory(require('jquery'));\n\t} else {\n\t\t// Browser globals\n\t\tfactory(jQuery);\n\t}\n}(function ($) {\n\n\tvar pluses = /\\+/g;\n\n\tfunction encode(s) {\n\t\treturn config.raw ? s : encodeURIComponent(s);\n\t}\n\n\tfunction decode(s) {\n\t\treturn config.raw ? s : decodeURIComponent(s);\n\t}\n\n\tfunction stringifyCookieValue(value) {\n\t\treturn encode(config.json ? JSON.stringify(value) : String(value));\n\t}\n\n\tfunction parseCookieValue(s) {\n\t\tif (s.indexOf('\"') === 0) {\n\t\t\t// This is a quoted cookie as according to RFC2068, unescape...\n\t\t\ts = s.slice(1, -1).replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, '\\\\');\n\t\t}\n\n\t\ttry {\n\t\t\t// Replace server-side written pluses with spaces.\n\t\t\t// If we can't decode the cookie, ignore it, it's unusable.\n\t\t\t// If we can't parse the cookie, ignore it, it's unusable.\n\t\t\ts = decodeURIComponent(s.replace(pluses, ' '));\n\t\t\treturn config.json ? JSON.parse(s) : s;\n\t\t} catch(e) {}\n\t}\n\n\tfunction read(s, converter) {\n\t\tvar value = config.raw ? s : parseCookieValue(s);\n\t\treturn $.isFunction(converter) ? converter(value) : value;\n\t}\n\n\tvar config = $.cookie = function (key, value, options) {\n\n\t\t// Write\n\n\t\tif (value !== undefined && !$.isFunction(value)) {\n\t\t\toptions = $.extend({}, config.defaults, options);\n\n\t\t\tif (typeof options.expires === 'number') {\n\t\t\t\tvar days = options.expires, t = options.expires = new Date();\n\t\t\t\tt.setTime(+t + days * 864e+5);\n\t\t\t}\n\n\t\t\treturn (document.cookie = [\n\t\t\t\tencode(key), '=', stringifyCookieValue(value),\n\t\t\t\toptions.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE\n\t\t\t\toptions.path    ? '; path=' + options.path : '',\n\t\t\t\toptions.domain  ? '; domain=' + options.domain : '',\n\t\t\t\toptions.secure  ? '; secure' : ''\n\t\t\t].join(''));\n\t\t}\n\n\t\t// Read\n\n\t\tvar result = key ? undefined : {};\n\n\t\t// To prevent the for loop in the first place assign an empty array\n\t\t// in case there are no cookies at all. Also prevents odd result when\n\t\t// calling $.cookie().\n\t\tvar cookies = document.cookie ? document.cookie.split('; ') : [];\n\n\t\tfor (var i = 0, l = cookies.length; i < l; i++) {\n\t\t\tvar parts = cookies[i].split('=');\n\t\t\tvar name = decode(parts.shift());\n\t\t\tvar cookie = parts.join('=');\n\n\t\t\tif (key && key === name) {\n\t\t\t\t// If second argument (value) is a function it's a converter...\n\t\t\t\tresult = read(cookie, value);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Prevent storing a cookie that we couldn't decode.\n\t\t\tif (!key && (cookie = read(cookie)) !== undefined) {\n\t\t\t\tresult[name] = cookie;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t};\n\n\tconfig.defaults = {};\n\n\t$.removeCookie = function (key, options) {\n\t\tif ($.cookie(key) === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Must not alter options, thus extending a fresh object...\n\t\t$.cookie(key, '', $.extend({}, options, { expires: -1 }));\n\t\treturn !$.cookie(key);\n\t};\n\n}));\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/layer/layer.js",
    "content": "/*! layer-v3.1.1 Web弹层组件 MIT License  http://layer.layui.com/  By 贤心 */\n ;!function(e,t){\"use strict\";var i,n,a=e.layui&&layui.define,o={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,i=t.length-1,n=i;n>0;n--)if(\"interactive\"===t[n].readyState){e=t[n].src;break}return e||t[i].src}();return e.substring(0,e.lastIndexOf(\"/\")+1)}(),config:{},end:{},minIndex:0,minLeft:[],btn:[\"&#x786E;&#x5B9A;\",\"&#x53D6;&#x6D88;\"],type:[\"dialog\",\"page\",\"iframe\",\"loading\",\"tips\"],getStyle:function(t,i){var n=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return n[n.getPropertyValue?\"getPropertyValue\":\"getAttribute\"](i)},link:function(t,i,n){if(r.path){var a=document.getElementsByTagName(\"head\")[0],s=document.createElement(\"link\");\"string\"==typeof i&&(n=i);var l=(n||t).replace(/\\.|\\//g,\"\"),f=\"layuicss-\"+l,c=0;s.rel=\"stylesheet\",s.href=r.path+t,s.id=f,document.getElementById(f)||a.appendChild(s),\"function\"==typeof i&&!function u(){return++c>80?e.console&&console.error(\"layer.css: Invalid\"):void(1989===parseInt(o.getStyle(document.getElementById(f),\"width\"))?i():setTimeout(u,100))}()}}},r={v:\"3.1.1\",ie:function(){var t=navigator.userAgent.toLowerCase();return!!(e.ActiveXObject||\"ActiveXObject\"in e)&&((t.match(/msie\\s(\\d+)/)||[])[1]||\"11\")}(),index:e.layer&&e.layer.v?1e5:0,path:o.getPath,config:function(e,t){return e=e||{},r.cache=o.config=i.extend({},o.config,e),r.path=o.config.path||r.path,\"string\"==typeof e.extend&&(e.extend=[e.extend]),o.config.path&&r.ready(),e.extend?(a?layui.addcss(\"modules/layer/\"+e.extend):o.link(\"theme/\"+e.extend),this):this},ready:function(e){var t=\"layer\",i=\"\",n=(a?\"modules/layer/\":\"theme/\")+\"default/layer.css?v=\"+r.v+i;return a?layui.addcss(n,e,t):o.link(n,e,t),this},alert:function(e,t,n){var a=\"function\"==typeof t;return a&&(n=t),r.open(i.extend({content:e,yes:n},a?{}:t))},confirm:function(e,t,n,a){var s=\"function\"==typeof t;return s&&(a=n,n=t),r.open(i.extend({content:e,btn:o.btn,yes:n,btn2:a},s?{}:t))},msg:function(e,n,a){var s=\"function\"==typeof n,f=o.config.skin,c=(f?f+\" \"+f+\"-msg\":\"\")||\"layui-layer-msg\",u=l.anim.length-1;return s&&(a=n),r.open(i.extend({content:e,time:3e3,shade:!1,skin:c,title:!1,closeBtn:!1,btn:!1,resize:!1,end:a},s&&!o.config.skin?{skin:c+\" layui-layer-hui\",anim:u}:function(){return n=n||{},(n.icon===-1||n.icon===t&&!o.config.skin)&&(n.skin=c+\" \"+(n.skin||\"layui-layer-hui\")),n}()))},load:function(e,t){return r.open(i.extend({type:3,icon:e||0,resize:!1,shade:.01},t))},tips:function(e,t,n){return r.open(i.extend({type:4,content:[e,t],closeBtn:!1,time:3e3,shade:!1,resize:!1,fixed:!1,maxWidth:210},n))}},s=function(e){var t=this;t.index=++r.index,t.config=i.extend({},t.config,o.config,e),document.body?t.creat():setTimeout(function(){t.creat()},30)};s.pt=s.prototype;var l=[\"layui-layer\",\".layui-layer-title\",\".layui-layer-main\",\".layui-layer-dialog\",\"layui-layer-iframe\",\"layui-layer-content\",\"layui-layer-btn\",\"layui-layer-close\"];l.anim=[\"layer-anim-00\",\"layer-anim-01\",\"layer-anim-02\",\"layer-anim-03\",\"layer-anim-04\",\"layer-anim-05\",\"layer-anim-06\"],s.pt.config={type:0,shade:.3,fixed:!0,move:l[1],title:\"&#x4FE1;&#x606F;\",offset:\"auto\",area:\"auto\",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,anim:0,isOutAnim:!0,icon:-1,moveType:1,resize:!0,scrollbar:!0,tips:2},s.pt.vessel=function(e,t){var n=this,a=n.index,r=n.config,s=r.zIndex+a,f=\"object\"==typeof r.title,c=r.maxmin&&(1===r.type||2===r.type),u=r.title?'<div class=\"layui-layer-title\" style=\"'+(f?r.title[1]:\"\")+'\">'+(f?r.title[0]:r.title)+\"</div>\":\"\";return r.zIndex=s,t([r.shade?'<div class=\"layui-layer-shade\" id=\"layui-layer-shade'+a+'\" times=\"'+a+'\" style=\"'+(\"z-index:\"+(s-1)+\"; \")+'\"></div>':\"\",'<div class=\"'+l[0]+(\" layui-layer-\"+o.type[r.type])+(0!=r.type&&2!=r.type||r.shade?\"\":\" layui-layer-border\")+\" \"+(r.skin||\"\")+'\" id=\"'+l[0]+a+'\" type=\"'+o.type[r.type]+'\" times=\"'+a+'\" showtime=\"'+r.time+'\" conType=\"'+(e?\"object\":\"string\")+'\" style=\"z-index: '+s+\"; width:\"+r.area[0]+\";height:\"+r.area[1]+(r.fixed?\"\":\";position:absolute;\")+'\">'+(e&&2!=r.type?\"\":u)+'<div id=\"'+(r.id||\"\")+'\" class=\"layui-layer-content'+(0==r.type&&r.icon!==-1?\" layui-layer-padding\":\"\")+(3==r.type?\" layui-layer-loading\"+r.icon:\"\")+'\">'+(0==r.type&&r.icon!==-1?'<i class=\"layui-layer-ico layui-layer-ico'+r.icon+'\"></i>':\"\")+(1==r.type&&e?\"\":r.content||\"\")+'</div><span class=\"layui-layer-setwin\">'+function(){var e=c?'<a class=\"layui-layer-min\" href=\"javascript:;\"><cite></cite></a><a class=\"layui-layer-ico layui-layer-max\" href=\"javascript:;\"></a>':\"\";return r.closeBtn&&(e+='<a class=\"layui-layer-ico '+l[7]+\" \"+l[7]+(r.title?r.closeBtn:4==r.type?\"1\":\"2\")+'\" href=\"javascript:;\"></a>'),e}()+\"</span>\"+(r.btn?function(){var e=\"\";\"string\"==typeof r.btn&&(r.btn=[r.btn]);for(var t=0,i=r.btn.length;t<i;t++)e+='<a class=\"'+l[6]+t+'\">'+r.btn[t]+\"</a>\";return'<div class=\"'+l[6]+\" layui-layer-btn-\"+(r.btnAlign||\"\")+'\">'+e+\"</div>\"}():\"\")+(r.resize?'<span class=\"layui-layer-resize\"></span>':\"\")+\"</div>\"],u,i('<div class=\"layui-layer-move\"></div>')),n},s.pt.creat=function(){var e=this,t=e.config,a=e.index,s=t.content,f=\"object\"==typeof s,c=i(\"body\");if(!t.id||!i(\"#\"+t.id)[0]){switch(\"string\"==typeof t.area&&(t.area=\"auto\"===t.area?[\"\",\"\"]:[t.area,\"\"]),t.shift&&(t.anim=t.shift),6==r.ie&&(t.fixed=!1),t.type){case 0:t.btn=\"btn\"in t?t.btn:o.btn[0],r.closeAll(\"dialog\");break;case 2:var s=t.content=f?t.content:[t.content||\"http://layer.layui.com\",\"auto\"];t.content='<iframe scrolling=\"'+(t.content[1]||\"auto\")+'\" allowtransparency=\"true\" id=\"'+l[4]+a+'\" name=\"'+l[4]+a+'\" onload=\"this.className=\\'\\';\" class=\"layui-layer-load\" frameborder=\"0\" src=\"'+t.content[0]+'\"></iframe>';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll(\"loading\");break;case 4:f||(t.content=[t.content,\"body\"]),t.follow=t.content[1],t.content=t.content[0]+'<i class=\"layui-layer-TipsG\"></i>',delete t.title,t.tips=\"object\"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll(\"tips\")}if(e.vessel(f,function(n,r,u){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i(\"body\").append(n[1])}():function(){s.parents(\".\"+l[0])[0]||(s.data(\"display\",s.css(\"display\")).show().addClass(\"layui-layer-wrap\").wrap(n[1]),i(\"#\"+l[0]+a).find(\".\"+l[5]).before(r))}()}():c.append(n[1]),i(\".layui-layer-move\")[0]||c.append(o.moveElem=u),e.layero=i(\"#\"+l[0]+a),t.scrollbar||l.html.css(\"overflow\",\"hidden\").attr(\"layer-full\",a)}).auto(a),i(\"#layui-layer-shade\"+e.index).css({\"background-color\":t.shade[1]||\"#000\",opacity:t.shade[0]||t.shade}),2==t.type&&6==r.ie&&e.layero.find(\"iframe\").attr(\"src\",s[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on(\"resize\",function(){e.offset(),(/^\\d+%$/.test(t.area[0])||/^\\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),l.anim[t.anim]){var u=\"layer-anim \"+l.anim[t.anim];e.layero.addClass(u).one(\"webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend\",function(){i(this).removeClass(u)})}t.isOutAnim&&e.layero.data(\"isOutAnim\",!0)}},s.pt.auto=function(e){var t=this,a=t.config,o=i(\"#\"+l[0]+e);\"\"===a.area[0]&&a.maxWidth>0&&(r.ie&&r.ie<8&&a.btn&&o.width(o.innerWidth()),o.outerWidth()>a.maxWidth&&o.width(a.maxWidth));var s=[o.innerWidth(),o.innerHeight()],f=o.find(l[1]).outerHeight()||0,c=o.find(\".\"+l[6]).outerHeight()||0,u=function(e){e=o.find(e),e.height(s[1]-f-c-2*(0|parseFloat(e.css(\"padding-top\"))))};switch(a.type){case 2:u(\"iframe\");break;default:\"\"===a.area[1]?a.maxHeight>0&&o.outerHeight()>a.maxHeight?(s[1]=a.maxHeight,u(\".\"+l[5])):a.fixed&&s[1]>=n.height()&&(s[1]=n.height(),u(\".\"+l[5])):u(\".\"+l[5])}return t},s.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o=\"object\"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):\"auto\"!==t.offset&&(\"t\"===t.offset?e.offsetTop=0:\"r\"===t.offset?e.offsetLeft=n.width()-a[0]:\"b\"===t.offset?e.offsetTop=n.height()-a[1]:\"l\"===t.offset?e.offsetLeft=0:\"lt\"===t.offset?(e.offsetTop=0,e.offsetLeft=0):\"lb\"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):\"rt\"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):\"rb\"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr(\"minLeft\")&&(e.offsetTop=n.height()-(i.find(l[1]).outerHeight()||0),e.offsetLeft=i.css(\"left\")),i.css({top:e.offsetTop,left:e.offsetLeft})},s.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i(\"body\"));var s={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(\".layui-layer-TipsG\"),c=t.tips[0];t.tips[1]||f.remove(),s.autoLeft=function(){s.left+o[0]-n.width()>0?(s.tipLeft=s.left+s.width-o[0],f.css({right:12,left:\"auto\"})):s.tipLeft=s.left},s.where=[function(){s.autoLeft(),s.tipTop=s.top-o[1]-10,f.removeClass(\"layui-layer-TipsB\").addClass(\"layui-layer-TipsT\").css(\"border-right-color\",t.tips[1])},function(){s.tipLeft=s.left+s.width+10,s.tipTop=s.top,f.removeClass(\"layui-layer-TipsL\").addClass(\"layui-layer-TipsR\").css(\"border-bottom-color\",t.tips[1])},function(){s.autoLeft(),s.tipTop=s.top+s.height+10,f.removeClass(\"layui-layer-TipsT\").addClass(\"layui-layer-TipsB\").css(\"border-right-color\",t.tips[1])},function(){s.tipLeft=s.left-o[0]-10,s.tipTop=s.top,f.removeClass(\"layui-layer-TipsR\").addClass(\"layui-layer-TipsL\").css(\"border-bottom-color\",t.tips[1])}],s.where[c-1](),1===c?s.top-(n.scrollTop()+o[1]+16)<0&&s.where[2]():2===c?n.width()-(s.left+s.width+o[0]+16)>0||s.where[3]():3===c?s.top-n.scrollTop()+s.height+o[1]+16-n.height()>0&&s.where[0]():4===c&&o[0]+16-s.left>0&&s.where[1](),a.find(\".\"+l[5]).css({\"background-color\":t.tips[1],\"padding-right\":t.closeBtn?\"30px\":\"\"}),a.css({left:s.tipLeft-(t.fixed?n.scrollLeft():0),top:s.tipTop-(t.fixed?n.scrollTop():0)})},s.pt.move=function(){var e=this,t=e.config,a=i(document),s=e.layero,l=s.find(t.move),f=s.find(\".layui-layer-resize\"),c={};return t.move&&l.css(\"cursor\",\"move\"),l.on(\"mousedown\",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(s.css(\"left\")),e.clientY-parseFloat(s.css(\"top\"))],o.moveElem.css(\"cursor\",\"move\").show())}),f.on(\"mousedown\",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[s.outerWidth(),s.outerHeight()],o.moveElem.css(\"cursor\",\"se-resize\").show()}),a.on(\"mousemove\",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],l=\"fixed\"===s.css(\"position\");if(i.preventDefault(),c.stX=l?0:n.scrollLeft(),c.stY=l?0:n.scrollTop(),!t.moveOut){var f=n.width()-s.outerWidth()+c.stX,u=n.height()-s.outerHeight()+c.stY;a<c.stX&&(a=c.stX),a>f&&(a=f),o<c.stY&&(o=c.stY),o>u&&(o=u)}s.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0,t.resizing&&t.resizing(s)}}).on(\"mouseup\",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd(s)),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},s.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find(\"iframe\").on(\"load\",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find(\".\"+l[6]).children(\"a\").on(\"click\",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a[\"btn\"+(e+1)]&&a[\"btn\"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find(\".\"+l[7]).on(\"click\",e),a.shadeClose&&i(\"#layui-layer-shade\"+t.index).on(\"click\",function(){r.close(t.index)}),n.find(\".layui-layer-min\").on(\"click\",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(\".layui-layer-max\").on(\"click\",function(){i(this).hasClass(\"layui-layer-maxmin\")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i(\"select\"),function(e,t){var n=i(this);n.parents(\".\"+l[0])[0]||1==n.attr(\"layer\")&&i(\".\"+l[0]).length<1&&n.removeAttr(\"layer\").show(),n=null})},s.pt.IE6=function(e){i(\"select\").each(function(e,t){var n=i(this);n.parents(\".\"+l[0])[0]||\"none\"===n.css(\"display\")||n.attr({layer:\"1\"}).hide(),n=null})},s.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css(\"z-index\",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on(\"mousedown\",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css(\"margin-left\"))];e.find(\".layui-layer-max\").addClass(\"layui-layer-maxmin\"),e.attr({area:t})},o.rescollbar=function(e){l.html.attr(\"layer-full\")==e&&(l.html[0].style.removeProperty?l.html[0].style.removeProperty(\"overflow\"):l.html[0].style.removeAttribute(\"overflow\"),l.html.removeAttr(\"layer-full\"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i(\".\"+l[4]).attr(\"times\"),i(\"#\"+l[0]+t).find(\"iframe\").contents().find(e)},r.getFrameIndex=function(e){return i(\"#\"+e).parents(\".\"+l[4]).attr(\"times\")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame(\"html\",e).outerHeight(),n=i(\"#\"+l[0]+e),a=n.find(l[1]).outerHeight()||0,o=n.find(\".\"+l[6]).outerHeight()||0;n.css({height:t+a+o}),n.find(\"iframe\").css({height:t})}},r.iframeSrc=function(e,t){i(\"#\"+l[0]+e).find(\"iframe\").attr(\"src\",t)},r.style=function(e,t,n){var a=i(\"#\"+l[0]+e),r=a.find(\".layui-layer-content\"),s=a.attr(\"type\"),f=a.find(l[1]).outerHeight()||0,c=a.find(\".\"+l[6]).outerHeight()||0;a.attr(\"minLeft\");s!==o.type[3]&&s!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find(\".\"+l[6]).outerHeight(),s===o.type[2]?a.find(\"iframe\").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css(\"padding-top\"))-parseFloat(r.css(\"padding-bottom\"))}))},r.min=function(e,t){var a=i(\"#\"+l[0]+e),s=a.find(l[1]).outerHeight()||0,f=a.attr(\"minLeft\")||181*o.minIndex+\"px\",c=a.css(\"position\");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr(\"position\",c),r.style(e,{width:180,height:s,left:f,top:n.height()-s,position:\"fixed\",overflow:\"hidden\"},!0),a.find(\".layui-layer-min\").hide(),\"page\"===a.attr(\"type\")&&a.find(l[4]).hide(),o.rescollbar(e),a.attr(\"minLeft\")||o.minIndex++,a.attr(\"minLeft\",f)},r.restore=function(e){var t=i(\"#\"+l[0]+e),n=t.attr(\"area\").split(\",\");t.attr(\"type\");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr(\"position\"),overflow:\"visible\"},!0),t.find(\".layui-layer-max\").removeClass(\"layui-layer-maxmin\"),t.find(\".layui-layer-min\").show(),\"page\"===t.attr(\"type\")&&t.find(l[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i(\"#\"+l[0]+e);o.record(a),l.html.attr(\"layer-full\")||l.html.css(\"overflow\",\"hidden\").attr(\"layer-full\",e),clearTimeout(t),t=setTimeout(function(){var t=\"fixed\"===a.css(\"position\");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(\".layui-layer-min\").hide()},100)},r.title=function(e,t){var n=i(\"#\"+l[0]+(t||r.index)).find(l[1]);n.html(e)},r.close=function(e){var t=i(\"#\"+l[0]+e),n=t.attr(\"type\"),a=\"layer-anim-close\";if(t[0]){var s=\"layui-layer-wrap\",f=function(){if(n===o.type[1]&&\"object\"===t.attr(\"conType\")){t.children(\":not(.\"+l[5]+\")\").remove();for(var a=t.find(\".\"+s),r=0;r<2;r++)a.unwrap();a.css(\"display\",a.data(\"display\")).removeClass(s)}else{if(n===o.type[2])try{var f=i(\"#\"+l[4]+e)[0];f.contentWindow.document.write(\"\"),f.contentWindow.close(),t.find(\".\"+l[5])[0].removeChild(f)}catch(c){}t[0].innerHTML=\"\",t.remove()}\"function\"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data(\"isOutAnim\")&&t.addClass(\"layer-anim \"+a),i(\"#layui-layer-moves, #layui-layer-shade\"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr(\"minLeft\")&&(o.minIndex--,o.minLeft.push(t.attr(\"minLeft\"))),r.ie&&r.ie<10||!t.data(\"isOutAnim\")?f():setTimeout(function(){f()},200)}},r.closeAll=function(e){i.each(i(\".\"+l[0]),function(){var t=i(this),n=e?t.attr(\"type\")===e:1;n&&r.close(t.attr(\"times\")),n=null})};var f=r.cache||{},c=function(e){return f.skin?\" \"+f.skin+\" \"+f.skin+\"-\"+e:\"\"};r.prompt=function(e,t){var a=\"\";if(e=e||{},\"function\"==typeof e&&(t=e),e.area){var o=e.area;a='style=\"width: '+o[0]+\"; height: \"+o[1]+';\"',delete e.area}var s,l=2==e.formType?'<textarea class=\"layui-layer-input\"'+a+\">\"+(e.value||\"\")+\"</textarea>\":function(){return'<input type=\"'+(1==e.formType?\"password\":\"text\")+'\" class=\"layui-layer-input\" value=\"'+(e.value||\"\")+'\">'}(),f=e.success;return delete e.success,r.open(i.extend({type:1,btn:[\"&#x786E;&#x5B9A;\",\"&#x53D6;&#x6D88;\"],content:l,skin:\"layui-layer-prompt\"+c(\"prompt\"),maxWidth:n.width(),success:function(e){s=e.find(\".layui-layer-input\"),s.focus(),\"function\"==typeof f&&f(e)},resize:!1,yes:function(i){var n=s.val();\"\"===n?s.focus():n.length>(e.maxlength||500)?r.tips(\"&#x6700;&#x591A;&#x8F93;&#x5165;\"+(e.maxlength||500)+\"&#x4E2A;&#x5B57;&#x6570;\",s,{tips:1}):t&&t(n,i,s)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{},n=\"layui-this\",a=e.success;return delete e.success,r.open(i.extend({type:1,skin:\"layui-layer-tab\"+c(\"tab\"),resize:!1,title:function(){var e=t.length,i=1,a=\"\";if(e>0)for(a='<span class=\"'+n+'\">'+t[0].title+\"</span>\";i<e;i++)a+=\"<span>\"+t[i].title+\"</span>\";return a}(),content:'<ul class=\"layui-layer-tabmain\">'+function(){var e=t.length,i=1,a=\"\";if(e>0)for(a='<li class=\"layui-layer-tabli '+n+'\">'+(t[0].content||\"no content\")+\"</li>\";i<e;i++)a+='<li class=\"layui-layer-tabli\">'+(t[i].content||\"no  content\")+\"</li>\";return a}()+\"</ul>\",success:function(t){var o=t.find(\".layui-layer-title\").children(),r=t.find(\".layui-layer-tabmain\").children();o.on(\"mousedown\",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var a=i(this),o=a.index();a.addClass(n).siblings().removeClass(n),r.eq(o).show().siblings().hide(),\"function\"==typeof e.change&&e.change(o)}),\"function\"==typeof a&&a(t)}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var s={};if(t=t||{},t.photos){var l=t.photos.constructor===Object,f=l?t.photos:{},u=f.data||[],d=f.start||0;s.imgIndex=(0|d)+1,t.img=t.img||\"img\";var y=t.success;if(delete t.success,l){if(0===u.length)return r.msg(\"&#x6CA1;&#x6709;&#x56FE;&#x7247;\")}else{var p=i(t.photos),h=function(){u=[],p.find(t.img).each(function(e){var t=i(this);t.attr(\"layer-index\",e),u.push({alt:t.attr(\"alt\"),pid:t.attr(\"layer-pid\"),src:t.attr(\"layer-src\")||t.attr(\"src\"),thumb:t.attr(\"src\")})})};if(h(),0===u.length)return;if(n||p.on(\"click\",t.img,function(){var e=i(this),n=e.attr(\"layer-index\");r.photos(i.extend(t,{photos:{start:n,data:u,tab:t.tab},full:t.full}),!0),h()}),!n)return}s.imgprev=function(e){s.imgIndex--,s.imgIndex<1&&(s.imgIndex=u.length),s.tabimg(e)},s.imgnext=function(e,t){s.imgIndex++,s.imgIndex>u.length&&(s.imgIndex=1,t)||s.tabimg(e)},s.keyup=function(e){if(!s.end){var t=e.keyCode;e.preventDefault(),37===t?s.imgprev(!0):39===t?s.imgnext(!0):27===t&&r.close(s.index)}},s.tabimg=function(e){if(!(u.length<=1))return f.start=s.imgIndex-1,r.close(s.index),r.photos(t,!0,e)},s.event=function(){s.bigimg.hover(function(){s.imgsee.show()},function(){s.imgsee.hide()}),s.bigimg.find(\".layui-layer-imgprev\").on(\"click\",function(e){e.preventDefault(),s.imgprev()}),s.bigimg.find(\".layui-layer-imgnext\").on(\"click\",function(e){e.preventDefault(),s.imgnext()}),i(document).on(\"keyup\",s.keyup)},s.loadi=r.load(1,{shade:!(\"shade\"in t)&&.9,scrollbar:!1}),o(u[d].src,function(n){r.close(s.loadi),s.index=r.open(i.extend({type:1,id:\"layui-layer-photos\",area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0]<r[1]&&(a[0]=a[0]/r[1],a[1]=a[1]/r[1])}return[a[0]+\"px\",a[1]+\"px\"]}(),title:!1,shade:.9,shadeClose:!0,closeBtn:!1,move:\".layui-layer-phimg img\",moveType:1,scrollbar:!1,moveOut:!0,isOutAnim:!1,skin:\"layui-layer-photos\"+c(\"photos\"),content:'<div class=\"layui-layer-phimg\"><img src=\"'+u[d].src+'\" alt=\"'+(u[d].alt||\"\")+'\" layer-pid=\"'+u[d].pid+'\"><div class=\"layui-layer-imgsee\">'+(u.length>1?'<span class=\"layui-layer-imguide\"><a href=\"javascript:;\" class=\"layui-layer-iconext layui-layer-imgprev\"></a><a href=\"javascript:;\" class=\"layui-layer-iconext layui-layer-imgnext\"></a></span>':\"\")+'<div class=\"layui-layer-imgbar\" style=\"display:'+(a?\"block\":\"\")+'\"><span class=\"layui-layer-imgtit\"><a href=\"javascript:;\">'+(u[d].alt||\"\")+\"</a><em>\"+s.imgIndex+\"/\"+u.length+\"</em></span></div></div></div>\",success:function(e,i){s.bigimg=e.find(\".layui-layer-phimg\"),s.imgsee=e.find(\".layui-layer-imguide,.layui-layer-imgbar\"),s.event(e),t.tab&&t.tab(u[d],e),\"function\"==typeof y&&y(e)},end:function(){s.end=!0,i(document).off(\"keyup\",s.keyup)}},t))},function(){r.close(s.loadi),r.msg(\"&#x5F53;&#x524D;&#x56FE;&#x7247;&#x5730;&#x5740;&#x5F02;&#x5E38;<br>&#x662F;&#x5426;&#x7EE7;&#x7EED;&#x67E5;&#x770B;&#x4E0B;&#x4E00;&#x5F20;&#xFF1F;\",{time:3e4,btn:[\"&#x4E0B;&#x4E00;&#x5F20;\",\"&#x4E0D;&#x770B;&#x4E86;\"],yes:function(){u.length>1&&s.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),l.html=i(\"html\"),r.open=function(e){var t=new s(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define(\"jquery\",function(t){r.path=layui.cache.dir,o.run(layui.$),e.layer=r,t(\"layer\",r)})):\"function\"==typeof define&&define.amd?define([\"jquery\"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window);"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/layer/theme/default/layer.css",
    "content": ".layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span,.layui-layer-title{text-overflow:ellipsis;white-space:nowrap}html #layuicss-layer{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+\"px\")}.layui-layer{-webkit-overflow-scrolling:touch;top:150px;left:0;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;border-radius:2px;box-shadow:1px 1px 50px rgba(0,0,0,.3)}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #B2B2B2;border:1px solid rgba(0,0,0,.1);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-load{background:url(loading-1.gif) center center no-repeat #eee}.layui-layer-ico{background:url(icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-move{display:none;position:fixed;*position:absolute;left:0;top:0;width:100%;height:100%;cursor:move;opacity:0;filter:alpha(opacity=0);background-color:#fff;z-index:2147483647}.layui-layer-resize{position:absolute;width:15px;height:15px;right:0;bottom:0;cursor:se-resize}.layer-anim{-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}@-webkit-keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-00{-webkit-animation-name:layer-bounceIn;animation-name:layer-bounceIn}@-webkit-keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-01{-webkit-animation-name:layer-zoomInDown;animation-name:layer-zoomInDown}@-webkit-keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layer-anim-02{-webkit-animation-name:layer-fadeInUpBig;animation-name:layer-fadeInUpBig}@-webkit-keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-03{-webkit-animation-name:layer-zoomInLeft;animation-name:layer-zoomInLeft}@-webkit-keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layer-anim-04{-webkit-animation-name:layer-rollIn;animation-name:layer-rollIn}@keyframes layer-fadeIn{0%{opacity:0}100%{opacity:1}}.layer-anim-05{-webkit-animation-name:layer-fadeIn;animation-name:layer-fadeIn}@-webkit-keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layer-anim-06{-webkit-animation-name:layer-shake;animation-name:layer-shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;background-color:#F8F8F8;border-radius:2px 2px 0 0}.layui-layer-setwin{position:absolute;right:15px;*right:0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2E2D3C;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2D93CA}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:1px -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;background-position:-149px -31px;*right:-18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -31px}.layui-layer-btn{text-align:right;padding:0 15px 12px;pointer-events:auto;user-select:none;-webkit-user-select:none}.layui-layer-btn a{height:28px;line-height:28px;margin:5px 5px 0;padding:0 15px;border:1px solid #dedede;background-color:#fff;color:#333;border-radius:2px;font-weight:400;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.8}.layui-layer-btn .layui-layer-btn0{border-color:#1E9FFF;background-color:#1E9FFF;color:#fff}.layui-layer-btn-l{text-align:left}.layui-layer-btn-c{text-align:center}.layui-layer-dialog{min-width:260px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;overflow:hidden;font-size:14px;overflow-x:hidden;overflow-y:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;_left:-40px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8D8D8D;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #D3D4D3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:none}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0 0}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0 0;box-shadow:none;border:none}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(loading-2.gif) no-repeat}.layui-layer-tips{background:0 0;box-shadow:none;border:none}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:8px 15px;font-size:12px;_float:left;border-radius:2px;box-shadow:1px 1px 3px rgba(0,0,0,.2);background-color:#000;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#000}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:5px;border-bottom-style:solid;border-bottom-color:#000}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476A7;color:#fff;border:none}.layui-layer-lan .layui-layer-btn{padding:5px 10px 10px;text-align:right;border-top:1px solid #E9E7E7}.layui-layer-lan .layui-layer-btn a{background:#fff;border-color:#E9E7E7;color:#333}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#C9C5C5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:none}.layui-layer-molv .layui-layer-btn a{background:#009f95;border-color:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92B8B1}.layui-layer-iconext{background:url(icon-ext.png) no-repeat}.layui-layer-prompt .layui-layer-input{display:block;width:230px;height:36px;margin:0 auto;line-height:30px;padding-left:10px;border:1px solid #e6e6e6;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px;padding:6px 10px}.layui-layer-prompt .layui-layer-content{padding:20px}.layui-layer-prompt .layui-layer-btn{padding-top:0}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;overflow:hidden;cursor:pointer}.layui-layer-tab .layui-layer-title span.layui-this{height:43px;border-left:1px solid #eee;border-right:1px solid #eee;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.layui-this{display:block}.layui-layer-photos{-webkit-animation-duration:.8s;animation-duration:.8s}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}@-webkit-keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);-ms-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-close{-webkit-animation-name:layer-bounceOut;animation-name:layer-bounceOut;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}@media screen and (max-width:1100px){.layui-layer-iframe{overflow-y:auto;-webkit-overflow-scrolling:touch}}"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/common/common.exception.ftl",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>Error</title>\n    <style type=\"text/css\"> \n        body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n        div.dialog {\n            width: 80%;\n            padding: 1em 4em;\n            margin: 4em auto 0 auto;\n            border: 1px solid #ccc;\n            border-right-color: #999;\n            border-bottom-color: #999;\n        }\n        h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n    </style>\n    \n</head> \n</head>\n<body> \n\n\t<div class=\"dialog\"> \n\t    <h1>System Error</h1>\n\t    <p>${exceptionMsg}</p>\n\t\t<a href=\"javascript:window.location.href='${request.contextPath}/'\">Back</a>\n\t    </p> \n\t</div>\n\n</body>\n</html>"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/common/common.macro.ftl",
    "content": "<#macro commonStyle>\n\n\t<#-- favicon -->\n\t<link rel=\"icon\" href=\"${request.contextPath}/static/favicon.ico\" />\n\n\t<meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <!-- Tell the browser to be responsive to screen width -->\n    <meta content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\" name=\"viewport\">\n    <!-- Bootstrap -->\n    <link rel=\"stylesheet\" href=\"${request.contextPath}/static/adminlte/bower_components/bootstrap/css/bootstrap.min.css\">\n    <!-- Font Awesome -->\n    <link rel=\"stylesheet\" href=\"${request.contextPath}/static/adminlte/bower_components/font-awesome/css/font-awesome.min.css\">\n    <!-- Ionicons -->\n    <link rel=\"stylesheet\" href=\"${request.contextPath}/static/adminlte/bower_components/Ionicons/css/ionicons.min.css\">\n    <!-- Theme style -->\n    <link rel=\"stylesheet\" href=\"${request.contextPath}/static/adminlte/dist/css/AdminLTE.min.css\">\n    <!-- AdminLTE Skins. Choose a skin from the css/skins folder instead of downloading all of them to reduce the load. -->\n    <link rel=\"stylesheet\" href=\"${request.contextPath}/static/adminlte/dist/css/skins/_all-skins.min.css\">\n\n\t<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->\n    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->\n    <!--[if lt IE 9]>\n    <script src=\"https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js\"></script>\n    <script src=\"https://oss.maxcdn.com/respond/1.4.2/respond.min.js\"></script>\n    <![endif]-->\n\n\t<!-- pace -->\n\t<link rel=\"stylesheet\" href=\"${request.contextPath}/static/adminlte/bower_components/PACE/themes/blue/pace-theme-flash.css\">\n\n\t<#-- i18n -->\n\t<#global I18n = I18nUtil.getMultString()?eval />\n\n</#macro>\n\n<#macro commonScript>\n\t<!-- jQuery -->\n\t<script src=\"${request.contextPath}/static/adminlte/bower_components/jquery/jquery.min.js\"></script>\n\t<!-- Bootstrap -->\n\t<script src=\"${request.contextPath}/static/adminlte/bower_components/bootstrap/js/bootstrap.min.js\"></script>\n\t<!-- FastClick -->\n\t<script src=\"${request.contextPath}/static/adminlte/bower_components/fastclick/fastclick.js\"></script>\n\t<!-- AdminLTE App -->\n\t<script src=\"${request.contextPath}/static/adminlte/dist/js/adminlte.min.js\"></script>\n\t<!-- jquery.slimscroll -->\n\t<script src=\"${request.contextPath}/static/adminlte/bower_components/jquery-slimscroll/jquery.slimscroll.min.js\"></script>\n\n    <!-- pace -->\n    <script src=\"${request.contextPath}/static/adminlte/bower_components/PACE/pace.min.js\"></script>\n    <#-- jquery cookie -->\n\t<script src=\"${request.contextPath}/static/plugins/jquery/jquery.cookie.js\"></script>\n\t<#-- jquery.validate -->\n\t<script src=\"${request.contextPath}/static/plugins/jquery/jquery.validate.min.js\"></script>\n\n\t<#-- layer -->\n\t<script src=\"${request.contextPath}/static/plugins/layer/layer.js\"></script>\n\n\t<#-- common -->\n    <script src=\"${request.contextPath}/static/js/common.1.js\"></script>\n    <script>\n\t\tvar base_url = '${request.contextPath}';\n        var I18n = ${I18nUtil.getMultString()};\n\t</script>\n\n</#macro>\n\n<#macro commonHeader>\n\t<header class=\"main-header\">\n\t\t<a href=\"${request.contextPath}/\" class=\"logo\">\n\t\t\t<span class=\"logo-mini\"><b>XXL</b></span>\n\t\t\t<span class=\"logo-lg\"><b>${I18n.admin_name}</b></span>\n\t\t</a>\n\t\t<nav class=\"navbar navbar-static-top\" role=\"navigation\">\n\n\t\t\t<a href=\"#\" class=\"sidebar-toggle\" data-toggle=\"push-menu\" role=\"button\">\n                <span class=\"sr-only\">Toggle navigation</span>\n                <span class=\"icon-bar\"></span>\n                <span class=\"icon-bar\"></span>\n                <span class=\"icon-bar\"></span>\n            </a>\n\n          \t<div class=\"navbar-custom-menu\">\n\t\t\t\t<ul class=\"nav navbar-nav\">\n\t\t\t\t\t<#-- login user -->\n                    <li class=\"dropdown\">\n                        <a href=\"javascript:\" class=\"dropdown-toggle\" data-toggle=\"dropdown\" aria-expanded=\"false\">\n                            ${I18n.system_welcome} ${Request[\"XXL_JOB_LOGIN_IDENTITY\"].username}\n                            <span class=\"caret\"></span>\n                        </a>\n                        <ul class=\"dropdown-menu\" role=\"menu\">\n                            <li id=\"updatePwd\" ><a href=\"javascript:\">${I18n.change_pwd}</a></li>\n                            <li id=\"logoutBtn\" ><a href=\"javascript:\">${I18n.logout_btn}</a></li>\n                        </ul>\n                    </li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\n\t\t</nav>\n\t</header>\n\n\t<!-- 修改密码.模态框 -->\n\t<div class=\"modal fade\" id=\"updatePwdModal\" tabindex=\"-1\" role=\"dialog\"  aria-hidden=\"true\">\n\t\t<div class=\"modal-dialog \">\n\t\t\t<div class=\"modal-content\">\n\t\t\t\t<div class=\"modal-header\">\n\t\t\t\t\t<h4 class=\"modal-title\" >${I18n.change_pwd}</h4>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"modal-body\">\n\t\t\t\t\t<form class=\"form-horizontal form\" role=\"form\" >\n\t\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t\t<label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.change_pwd_field_newpwd}<font color=\"red\">*</font></label>\n\t\t\t\t\t\t\t<div class=\"col-sm-10\"><input type=\"text\" class=\"form-control\" name=\"password\" placeholder=\"${I18n.system_please_input} ${I18n.change_pwd_field_newpwd}\" maxlength=\"18\" ></div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<hr>\n\t\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t\t<div class=\"col-sm-offset-3 col-sm-6\">\n\t\t\t\t\t\t\t\t<button type=\"submit\" class=\"btn btn-primary\"  >${I18n.system_save}</button>\n\t\t\t\t\t\t\t\t<button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">${I18n.system_cancel}</button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</form>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n\n</#macro>\n\n<#macro commonLeft pageName >\n\t<!-- Left side column. contains the logo and sidebar -->\n\t<aside class=\"main-sidebar\">\n\t\t<!-- sidebar: style can be found in sidebar.less -->\n\t\t<section class=\"sidebar\">\n\t\t\t<!-- sidebar menu: : style can be found in sidebar.less -->\n\t\t\t<ul class=\"sidebar-menu\">\n                <li class=\"header\">${I18n.system_nav}</li>\n                <li class=\"nav-click <#if pageName == \"index\">active</#if>\" ><a href=\"${request.contextPath}/\"><i class=\"fa fa-circle-o text-aqua\"></i><span>${I18n.job_dashboard_name}</span></a></li>\n\t\t\t\t<li class=\"nav-click <#if pageName == \"jobinfo\">active</#if>\" ><a href=\"${request.contextPath}/jobinfo\"><i class=\"fa fa-circle-o text-yellow\"></i><span>${I18n.jobinfo_name}</span></a></li>\n\t\t\t\t<li class=\"nav-click <#if pageName == \"joblog\">active</#if>\" ><a href=\"${request.contextPath}/joblog\"><i class=\"fa fa-circle-o text-green\"></i><span>${I18n.joblog_name}</span></a></li>\n\t\t\t\t<#if Request[\"XXL_JOB_LOGIN_IDENTITY\"].role == 1>\n                    <li class=\"nav-click <#if pageName == \"jobgroup\">active</#if>\" ><a href=\"${request.contextPath}/jobgroup\"><i class=\"fa fa-circle-o text-red\"></i><span>${I18n.jobgroup_name}</span></a></li>\n                    <li class=\"nav-click <#if pageName == \"user\">active</#if>\" ><a href=\"${request.contextPath}/user\"><i class=\"fa fa-circle-o text-purple\"></i><span>${I18n.user_manage}</span></a></li>\n\t\t\t\t</#if>\n\t\t\t\t<li class=\"nav-click <#if pageName == \"help\">active</#if>\" ><a href=\"${request.contextPath}/help\"><i class=\"fa fa-circle-o text-gray\"></i><span>${I18n.job_help}</span></a></li>\n\t\t\t</ul>\n\t\t</section>\n\t\t<!-- /.sidebar -->\n\t</aside>\n</#macro>\n\n<#macro commonControl >\n\t<!-- Control Sidebar -->\n\t<aside class=\"control-sidebar control-sidebar-dark\">\n\t\t<!-- Create the tabs -->\n\t\t<ul class=\"nav nav-tabs nav-justified control-sidebar-tabs\">\n\t\t\t<li class=\"active\"><a href=\"#control-sidebar-home-tab\" data-toggle=\"tab\"><i class=\"fa fa-home\"></i></a></li>\n\t\t\t<li><a href=\"#control-sidebar-settings-tab\" data-toggle=\"tab\"><i class=\"fa fa-gears\"></i></a></li>\n\t\t</ul>\n\t\t<!-- Tab panes -->\n\t\t<div class=\"tab-content\">\n\t\t\t<!-- Home tab content -->\n\t\t\t<div class=\"tab-pane active\" id=\"control-sidebar-home-tab\">\n\t\t\t\t<h3 class=\"control-sidebar-heading\">近期活动</h3>\n\t\t\t\t<ul class=\"control-sidebar-menu\">\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"javascript::;\">\n\t\t\t\t\t\t\t<i class=\"menu-icon fa fa-birthday-cake bg-red\"></i>\n\t\t\t\t\t\t\t<div class=\"menu-info\">\n\t\t\t\t\t\t\t\t<h4 class=\"control-sidebar-subheading\">张三今天过生日</h4>\n\t\t\t\t\t\t\t\t<p>2015-09-10</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"javascript::;\">\n\t\t\t\t\t\t\t<i class=\"menu-icon fa fa-user bg-yellow\"></i>\n\t\t\t\t\t\t\t<div class=\"menu-info\">\n\t\t\t\t\t\t\t\t<h4 class=\"control-sidebar-subheading\">Frodo 更新了资料</h4>\n\t\t\t\t\t\t\t\t<p>更新手机号码 +1(800)555-1234</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"javascript::;\">\n\t\t\t\t\t\t\t<i class=\"menu-icon fa fa-envelope-o bg-light-blue\"></i>\n\t\t\t\t\t\t\t<div class=\"menu-info\">\n\t\t\t\t\t\t\t\t<h4 class=\"control-sidebar-subheading\">Nora 加入邮件列表</h4>\n\t\t\t\t\t\t\t\t<p>nora@example.com</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"javascript::;\">\n\t\t\t\t\t\t<i class=\"menu-icon fa fa-file-code-o bg-green\"></i>\n\t\t\t\t\t\t<div class=\"menu-info\">\n\t\t\t\t\t\t\t<h4 class=\"control-sidebar-subheading\">001号定时作业调度</h4>\n\t\t\t\t\t\t\t<p>5秒前执行</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t\t<!-- /.control-sidebar-menu -->\n\t\t\t</div>\n\t\t\t<!-- /.tab-pane -->\n\n\t\t\t<!-- Settings tab content -->\n\t\t\t<div class=\"tab-pane\" id=\"control-sidebar-settings-tab\">\n\t\t\t\t<form method=\"post\">\n\t\t\t\t\t<h3 class=\"control-sidebar-heading\">个人设置</h3>\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<label class=\"control-sidebar-subheading\"> 左侧菜单自适应\n\t\t\t\t\t\t\t<input type=\"checkbox\" class=\"pull-right\" checked>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<p>左侧菜单栏样式自适应</p>\n\t\t\t\t\t</div>\n\t\t\t\t\t<!-- /.form-group -->\n\n\t\t\t\t</form>\n\t\t\t</div>\n\t\t\t<!-- /.tab-pane -->\n\t\t</div>\n\t</aside>\n\t<!-- /.control-sidebar -->\n\t<!-- Add the sidebar's background. This div must be placed immediately after the control sidebar -->\n\t<div class=\"control-sidebar-bg\"></div>\n</#macro>\n\n<#macro commonFooter >\n\t<footer class=\"main-footer\">\n\t</footer>\n</#macro>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/help.ftl",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  \t<#import \"./common/common.macro.ftl\" as netCommon>\n\t<@netCommon.commonStyle />\n\t<title>${I18n.admin_name}</title>\n</head>\n<body class=\"hold-transition skin-blue sidebar-mini <#if cookieMap?exists && cookieMap[\"xxljob_adminlte_settings\"]?exists && \"off\" == cookieMap[\"xxljob_adminlte_settings\"].value >sidebar-collapse</#if> \">\n<div class=\"wrapper\">\n\t<!-- header -->\n\t<@netCommon.commonHeader />\n\t<!-- left -->\n\t<@netCommon.commonLeft \"help\" />\n\t\n\t<!-- Content Wrapper. Contains page content -->\n\t<div class=\"content-wrapper\">\n\t\t<!-- Content Header (Page header) -->\n\t\t<section class=\"content-header\">\n\t\t\t<h1>${I18n.job_help}</h1>\n\t\t</section>\n\n\t\t<!-- Main content -->\n\t\t<section class=\"content\">\n\t\t\t<div class=\"callout callout-info\">\n\t\t\t\t<h4>${I18n.admin_name_full}</h4>\n\t\t\t\t<br>\n\t\t\t\t<p>\n\t\t\t\t\t<a target=\"_blank\" href=\"https://github.com/xuxueli/xxl-job\">Github</a>&nbsp;&nbsp;&nbsp;&nbsp;\n\t\t\t\t\t<iframe src=\"https://ghbtns.com/github-btn.html?user=xuxueli&repo=xxl-job&type=star&count=true\" frameborder=\"0\" scrolling=\"0\" width=\"170px\" height=\"20px\" style=\"margin-bottom:-5px;\"></iframe> \n\t\t\t\t\t<br><br>\n                    <a target=\"_blank\" href=\"https://www.xuxueli.com/xxl-job/\">${I18n.job_help_document}</a>\n                    <br><br>\n\n\t\t\t\t</p>\n\t\t\t\t<p></p>\n            </div>\n\t\t</section>\n\t\t<!-- /.content -->\n\t</div>\n\t<!-- /.content-wrapper -->\n\t\n\t<!-- footer -->\n\t<@netCommon.commonFooter />\n</div>\n<@netCommon.commonScript />\n</body>\n</html>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/index.ftl",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  \t<#import \"./common/common.macro.ftl\" as netCommon>\n\t<@netCommon.commonStyle />\n    <!-- daterangepicker -->\n    <link rel=\"stylesheet\" href=\"${request.contextPath}/static/adminlte/bower_components/bootstrap-daterangepicker/daterangepicker.css\">\n    <title>${I18n.admin_name}</title>\n</head>\n<body class=\"hold-transition skin-blue sidebar-mini <#if cookieMap?exists && cookieMap[\"xxljob_adminlte_settings\"]?exists && \"off\" == cookieMap[\"xxljob_adminlte_settings\"].value >sidebar-collapse</#if> \">\n<div class=\"wrapper\">\n\t<!-- header -->\n\t<@netCommon.commonHeader />\n\t<!-- left -->\n\t<@netCommon.commonLeft \"index\" />\n\t\n\t<!-- Content Wrapper. Contains page content -->\n\t<div class=\"content-wrapper\">\n\t\t<!-- Content Header (Page header) -->\n\t\t<section class=\"content-header\">\n\t\t\t<h1>${I18n.job_dashboard_name}</h1>\n\t\t\t<!--\n\t\t\t<h1>运行报表<small>任务调度中心</small></h1>\n\t\t\t<ol class=\"breadcrumb\">\n\t\t\t\t<li><a><i class=\"fa fa-dashboard\"></i>调度中心</a></li>\n\t\t\t\t<li class=\"active\">使用教程</li>\n\t\t\t</ol>\n\t\t\t-->\n\t\t</section>\n\n\t\t<!-- Main content -->\n\t\t<section class=\"content\">\n\n            <!-- 任务信息 -->\n            <div class=\"row\">\n\n                <#-- 任务信息 -->\n                <div class=\"col-md-4 col-sm-6 col-xs-12\">\n                    <div class=\"info-box bg-aqua\">\n                        <span class=\"info-box-icon\"><i class=\"fa fa-flag-o\"></i></span>\n\n                        <div class=\"info-box-content\">\n                            <span class=\"info-box-text\">${I18n.job_dashboard_job_num}</span>\n                            <span class=\"info-box-number\">${jobInfoCount}</span>\n\n                            <div class=\"progress\">\n                                <div class=\"progress-bar\" style=\"width: 100%\"></div>\n                            </div>\n                            <span class=\"progress-description\">${I18n.job_dashboard_job_num_tip}</span>\n                        </div>\n                    </div>\n                </div>\n\n                <#-- 调度信息 -->\n                <div class=\"col-md-4 col-sm-6 col-xs-12\" >\n                    <div class=\"info-box bg-yellow\">\n                        <span class=\"info-box-icon\"><i class=\"fa fa-calendar\"></i></span>\n\n                        <div class=\"info-box-content\">\n                            <span class=\"info-box-text\">${I18n.job_dashboard_trigger_num}</span>\n                            <span class=\"info-box-number\">${jobLogCount}</span>\n\n                            <div class=\"progress\">\n                                <div class=\"progress-bar\" style=\"width: 100%\" ></div>\n                            </div>\n                            <span class=\"progress-description\">\n                                ${I18n.job_dashboard_trigger_num_tip}\n                                <#--<#if jobLogCount gt 0>\n                                    调度成功率：${(jobLogSuccessCount*100/jobLogCount)?string(\"0.00\")}<small>%</small>\n                                </#if>-->\n                            </span>\n                        </div>\n                    </div>\n                </div>\n\n                <#-- 执行器 -->\n                <div class=\"col-md-4 col-sm-6 col-xs-12\">\n                    <div class=\"info-box bg-green\">\n                        <span class=\"info-box-icon\"><i class=\"fa ion-ios-settings-strong\"></i></span>\n\n                        <div class=\"info-box-content\">\n                            <span class=\"info-box-text\">${I18n.job_dashboard_jobgroup_num}</span>\n                            <span class=\"info-box-number\">${executorCount}</span>\n\n                            <div class=\"progress\">\n                                <div class=\"progress-bar\" style=\"width: 100%\"></div>\n                            </div>\n                            <span class=\"progress-description\">${I18n.job_dashboard_jobgroup_num_tip}</span>\n                        </div>\n                    </div>\n                </div>\n\n            </div>\n\n            <#-- 调度报表：时间区间筛选，左侧折线图 + 右侧饼图 -->\n            <div class=\"row\">\n                <div class=\"col-md-12\">\n                    <div class=\"box\">\n                        <div class=\"box-header with-border\">\n                            <h3 class=\"box-title\">${I18n.job_dashboard_report}</h3>\n                            <#--<input type=\"text\" class=\"form-control\" id=\"filterTime\" readonly >-->\n\n                            <!-- tools box -->\n                            <div class=\"pull-right box-tools\">\n                                <button type=\"button\" class=\"btn btn-primary btn-sm daterange pull-right\" data-toggle=\"tooltip\" id=\"filterTime\" >\n                                    <i class=\"fa fa-calendar\"></i>\n                                </button>\n                                <#--<button type=\"button\" class=\"btn btn-primary btn-sm pull-right\" data-widget=\"collapse\" data-toggle=\"tooltip\" title=\"\" style=\"margin-right: 5px;\" data-original-title=\"Collapse\">\n                                    <i class=\"fa fa-minus\"></i>\n                                </button>-->\n                            </div>\n                            <!-- /. tools -->\n\n                        </div>\n                        <div class=\"box-body\">\n                            <div class=\"row\">\n                                <#-- 左侧折线图 -->\n                                <div class=\"col-md-8\">\n                                    <div id=\"lineChart\" style=\"height: 350px;\"></div>\n                                </div>\n                                <#-- 右侧饼图 -->\n                                <div class=\"col-md-4\">\n                                    <div id=\"pieChart\" style=\"height: 350px;\"></div>\n                                </div>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n\t\t</section>\n\t\t<!-- /.content -->\n\t</div>\n\t<!-- /.content-wrapper -->\n\t\n\t<!-- footer -->\n\t<@netCommon.commonFooter />\n</div>\n<@netCommon.commonScript />\n<!-- daterangepicker -->\n<script src=\"${request.contextPath}/static/adminlte/bower_components/moment/moment.min.js\"></script>\n<script src=\"${request.contextPath}/static/adminlte/bower_components/bootstrap-daterangepicker/daterangepicker.js\"></script>\n<#-- echarts -->\n<script src=\"${request.contextPath}/static/plugins/echarts/echarts.common.min.js\"></script>\n<script src=\"${request.contextPath}/static/js/index.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/jobcode/jobcode.index.ftl",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  \t<#import \"../common/common.macro.ftl\" as netCommon>\n\t<@netCommon.commonStyle />\n\t<link rel=\"stylesheet\" href=\"${request.contextPath}/static/plugins/codemirror/lib/codemirror.css\">\n\t<link rel=\"stylesheet\" href=\"${request.contextPath}/static/plugins/codemirror/addon/hint/show-hint.css\">\n    <title>${I18n.admin_name}</title>\n\t<style type=\"text/css\">\n\t\t.CodeMirror {\n      \t\tfont-size:16px;\n            width: 100%;\n      \t\theight: 100%;\n            /*bottom: 0;\n            top: 0px;*/\n            position: absolute;\n\t\t}\n    </style>\n</head>\n<body class=\"skin-blue fixed layout-top-nav\">\n\n\t<div class=\"wrapper\">\n\n        <header class=\"main-header\">\n            <nav class=\"navbar navbar-static-top\">\n                <div class=\"container\">\n\t\t\t\t\t<#-- icon -->\n                    <div class=\"navbar-header\">\n                        <a class=\"navbar-brand\"><b>Web</b>IDE</a>\n                        <button type=\"button\" class=\"navbar-toggle collapsed\" data-toggle=\"collapse\" data-target=\"#navbar-collapse\">\n                            <i class=\"fa fa-bars\"></i>\n                        </button>\n                    </div>\n\n                    <#-- left nav -->\n                    <div class=\"collapse navbar-collapse pull-left\" id=\"navbar-collapse\">\n                        <ul class=\"nav navbar-nav\">\n                            <li class=\"active\" ><a href=\"javascript:;\">\n                                <span class=\"sr-only\">(current)</span>\n                                【<#list GlueTypeEnum as item><#if item == jobInfo.glueType>${item.desc}</#if></#list>】\n                                ${jobInfo.jobDesc}\n                            </a></li>\n                        </ul>\n                    </div>\n\n\t\t\t\t\t<#-- right nav -->\n                    <div class=\"navbar-custom-menu\">\n                        <ul class=\"nav navbar-nav\">\n                            <li class=\"dropdown\">\n                                <a href=\"#\" class=\"dropdown-toggle\" data-toggle=\"dropdown\" aria-expanded=\"false\">${I18n.jobinfo_glue_rollback} <span class=\"caret\"></span></a>\n                                <ul class=\"dropdown-menu\" role=\"menu\">\n                                    <li <#if jobLogGlues?exists && jobLogGlues?size gt 0 >style=\"display: none;\"</#if> >\n                                        <a href=\"javascript:;\" class=\"source_version\" version=\"version_now\" glueType=\"${jobInfo.glueType}\" >\n                                            <#list GlueTypeEnum as item><#if item == jobInfo.glueType>${item.desc}</#if></#list>： ${jobInfo.glueRemark}\n                                        </a>\n                                    </li>\n                                    <textarea id=\"version_now\" style=\"display:none;\" >${jobInfo.glueSource}</textarea>\n\t\t\t\t\t\t\t\t\t<#if jobLogGlues?exists && jobLogGlues?size gt 0 >\n\t\t\t\t\t\t\t\t\t\t<#list jobLogGlues as glue>\n                                            <li>\n                                                <a href=\"javascript:;\" class=\"source_version\" version=\"version_${glue.id}\" glueType=\"${glue.glueType}\" >\n                                                    <#list GlueTypeEnum as item><#if item == glue.glueType>${item.desc}</#if></#list>： ${glue.glueRemark}\n                                                </a>\n                                            </li>\n                                            <textarea id=\"version_${glue.id}\" style=\"display:none;\" >${glue.glueSource}</textarea>\n\t\t\t\t\t\t\t\t\t\t</#list>\n\t\t\t\t\t\t\t\t\t</#if>\n                                </ul>\n                            </li>\n                            <li id=\"save\" >\n\t\t\t\t\t\t\t\t<a href=\"javascript:;\" >\n\t\t\t\t\t\t\t\t\t<i class=\"fa fa-fw fa-save\" ></i>\n                                    ${I18n.system_save}\n\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t</li>\n                            <li>\n                                <a href=\"javascript:window.close();\" >\n                                    <i class=\"fa fa-fw fa-close\" ></i>\n                                ${I18n.system_close}\n                                </a>\n                            </li>\n                        </ul>\n                    </div>\n\n                </div>\n            </nav>\n        </header>\n\n\t\t<div class=\"content-wrapper\" id=\"ideWindow\" ></div>\n\n\t\t<!-- footer -->\n\t\t<#--<@netCommon.commonFooter />-->\n\t</div>\n\n    <!-- 保存.模态框 -->\n    <div class=\"modal fade\" id=\"saveModal\" tabindex=\"-1\" role=\"dialog\"  aria-hidden=\"true\">\n        <div class=\"modal-dialog \">\n            <div class=\"modal-content\">\n                <div class=\"modal-header\">\n                    <h4 class=\"modal-title\" ><i class=\"fa fa-fw fa-save\"></i>${I18n.system_save}</h4>\n                </div>\n                <div class=\"modal-body\">\n                    <div class=\"form-horizontal form\" role=\"form\" >\n                        <div class=\"form-group\">\n                            <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_glue_remark}<font color=\"red\">*</font></label>\n                            <div class=\"col-sm-10\"><input type=\"text\" class=\"form-control\" id=\"glueRemark\" placeholder=\"${I18n.system_please_input}${I18n.jobinfo_glue_remark}\" maxlength=\"64\" ></div>\n                        </div>\n                        <hr>\n                        <div class=\"form-group\">\n                            <div class=\"col-sm-offset-3 col-sm-6\">\n                                <button type=\"button\" class=\"btn btn-primary ok\" >${I18n.system_save}</button>\n                                <button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">${I18n.system_cancel}</button>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n\t\n<@netCommon.commonScript />\n\n\n    <#assign glueTypeModeSrc = \"${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js\" />\n    <#assign glueTypeIdeMode = \"text/x-java\" />\n\n    <#if jobInfo.glueType == \"GLUE_GROOVY\" >\n        <#assign glueTypeModeSrc = \"${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js\" />\n        <#assign glueTypeIdeMode = \"text/x-java\" />\n    <#elseif jobInfo.glueType == \"GLUE_SHELL\" >\n        <#assign glueTypeModeSrc = \"${request.contextPath}/static/plugins/codemirror/mode/shell/shell.js\" />\n        <#assign glueTypeIdeMode = \"text/x-sh\" />\n    <#elseif jobInfo.glueType == \"GLUE_PYTHON\" >\n        <#assign glueTypeModeSrc = \"${request.contextPath}/static/plugins/codemirror/mode/python/python.js\" />\n        <#assign glueTypeIdeMode = \"text/x-python\" />\n    <#elseif jobInfo.glueType == \"GLUE_PHP\" >\n        <#assign glueTypeModeSrc = \"${request.contextPath}/static/plugins/codemirror/mode/php/php.js\" />\n        <#assign glueTypeIdeMode = \"text/x-php\" />\n        <#assign glueTypeModeSrc02 = \"${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js\" />\n    <#elseif jobInfo.glueType == \"GLUE_NODEJS\" >\n        <#assign glueTypeModeSrc = \"${request.contextPath}/static/plugins/codemirror/mode/javascript/javascript.js\" />\n        <#assign glueTypeIdeMode = \"text/javascript\" />\n    <#elseif jobInfo.glueType == \"GLUE_POWERSHELL\" >\n        <#assign glueTypeModeSrc = \"${request.contextPath}/static/plugins/codemirror/mode/powershell/powershell.js\" />\n        <#assign glueTypeIdeMode = \"powershell\" />\n    </#if>\n\n\n<script src=\"${request.contextPath}/static/plugins/codemirror/lib/codemirror.js\"></script>\n<script src=\"${glueTypeModeSrc}\"></script>\n<#if glueTypeModeSrc02?exists>\n    <script src=\"${glueTypeModeSrc02}\"></script>\n</#if>\n<script src=\"${request.contextPath}/static/plugins/codemirror/addon/hint/show-hint.js\"></script>\n<script src=\"${request.contextPath}/static/plugins/codemirror/addon/hint/anyword-hint.js\"></script>\n\n<script>\nvar id = '${jobInfo.id}';\nvar ideMode = '${glueTypeIdeMode}';\n</script>\n<script src=\"${request.contextPath}/static/js/jobcode.index.1.js\"></script>\n\n</body>\n</html>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/jobgroup/jobgroup.index.ftl",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  \t<#import \"../common/common.macro.ftl\" as netCommon>\n\t<@netCommon.commonStyle />\n\t<!-- DataTables -->\n  \t<link rel=\"stylesheet\" href=\"${request.contextPath}/static/adminlte/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css\">\n    <title>${I18n.admin_name}</title>\n</head>\n<body class=\"hold-transition skin-blue sidebar-mini <#if cookieMap?exists && cookieMap[\"xxljob_adminlte_settings\"]?exists && \"off\" == cookieMap[\"xxljob_adminlte_settings\"].value >sidebar-collapse</#if> \">\n<div class=\"wrapper\">\n\t<!-- header -->\n\t<@netCommon.commonHeader />\n\t<!-- left -->\n\t<@netCommon.commonLeft \"jobgroup\" />\n\t\n\t<!-- Content Wrapper. Contains page content -->\n\t<div class=\"content-wrapper\">\n\t\t<!-- Content Header (Page header) -->\n\t\t<section class=\"content-header\">\n\t\t\t<h1>${I18n.jobgroup_name}</h1>\n\t\t</section>\n\n\t\t<!-- Main content -->\n\t    <section class=\"content\">\n\n            <div class=\"row\">\n                <div class=\"col-xs-3\">\n                    <div class=\"input-group\">\n                        <span class=\"input-group-addon\">AppName</span>\n                        <input type=\"text\" class=\"form-control\" id=\"appname\" placeholder=\"${I18n.system_please_input}AppName\" >\n                    </div>\n                </div>\n                <div class=\"col-xs-3\">\n                    <div class=\"input-group\">\n                        <span class=\"input-group-addon\">${I18n.jobgroup_field_title}</span>\n                        <input type=\"text\" class=\"form-control\" id=\"title\" placeholder=\"${I18n.jobgroup_field_title}\" >\n                    </div>\n                </div>\n                <div class=\"col-xs-2\">\n                    <button class=\"btn btn-block btn-info\" id=\"searchBtn\">${I18n.system_search}</button>\n                </div>\n                <div class=\"col-xs-2\">\n                    <button class=\"btn btn-block btn-success add\" type=\"button\">${I18n.jobinfo_field_add}</button>\n                </div>\n            </div>\n\t\t\t\n\t\t\t<div class=\"row\">\n\t\t\t\t<div class=\"col-xs-12\">\n\t\t\t\t\t<div class=\"box\">\n\t\t\t            <div class=\"box-body\">\n\t\t\t              \t<table id=\"jobgroup_list\" class=\"table table-bordered table-striped display\" width=\"100%\" >\n\t\t\t\t                <thead>\n\t\t\t\t\t            \t<tr>\n                                        <th name=\"id\" >ID</th>\n                                        <th name=\"appname\" >AppName</th>\n                                        <th name=\"title\" >${I18n.jobgroup_field_title}</th>\n                                        <th name=\"addressType\" >${I18n.jobgroup_field_addressType}</th>\n                                        <th name=\"registryList\" >OnLine ${I18n.jobgroup_field_registryList}</th>\n                                        <th>${I18n.system_opt}</th>\n\t\t\t\t\t                </tr>\n\t\t\t\t                </thead>\n                                <tbody>\n\t\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t    </section>\n\t</div>\n\n    <!-- 新增.模态框 -->\n    <div class=\"modal fade\" id=\"addModal\" tabindex=\"-1\" role=\"dialog\"  aria-hidden=\"true\">\n        <div class=\"modal-dialog \">\n            <div class=\"modal-content\">\n                <div class=\"modal-header\">\n                    <h4 class=\"modal-title\" >${I18n.jobgroup_add}</h4>\n                </div>\n                <div class=\"modal-body\">\n                    <form class=\"form-horizontal form\" role=\"form\" >\n                        <div class=\"form-group\">\n                            <label for=\"lastname\" class=\"col-sm-2 control-label\">AppName<font color=\"red\">*</font></label>\n                            <div class=\"col-sm-10\"><input type=\"text\" class=\"form-control\" name=\"appname\" placeholder=\"${I18n.system_please_input}AppName\" maxlength=\"64\" ></div>\n                        </div>\n                        <div class=\"form-group\">\n                            <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobgroup_field_title}<font color=\"red\">*</font></label>\n                            <div class=\"col-sm-10\"><input type=\"text\" class=\"form-control\" name=\"title\" placeholder=\"${I18n.system_please_input}${I18n.jobgroup_field_title}\" maxlength=\"12\" ></div>\n                        </div>\n                        <div class=\"form-group\">\n                            <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobgroup_field_addressType}<font color=\"red\">*</font></label>\n                            <div class=\"col-sm-10\">\n                                <input type=\"radio\" name=\"addressType\" value=\"0\" checked />${I18n.jobgroup_field_addressType_0}\n                                &nbsp;&nbsp;&nbsp;&nbsp;\n                                <input type=\"radio\" name=\"addressType\" value=\"1\" />${I18n.jobgroup_field_addressType_1}\n                            </div>\n                        </div>\n                        <div class=\"form-group\">\n                            <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobgroup_field_registryList}<font color=\"red\">*</font></label>\n                            <div class=\"col-sm-10\">\n                                <textarea class=\"textarea\" name=\"addressList\" maxlength=\"20000\" placeholder=\"${I18n.jobgroup_field_registryList_placeholder}\" readonly=\"readonly\" style=\"background-color:#eee; width: 100%; height: 100px; font-size: 14px; line-height: 15px; border: 1px solid #dddddd; padding: 5px;\"></textarea>\n                            </div>\n                        </div>\n                        <hr>\n                        <div class=\"form-group\">\n                            <div class=\"col-sm-offset-3 col-sm-6\">\n                                <button type=\"submit\" class=\"btn btn-primary\"  >${I18n.system_save}</button>\n                                <button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">${I18n.system_cancel}</button>\n                            </div>\n                        </div>\n                    </form>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <!-- 更新.模态框 -->\n    <div class=\"modal fade\" id=\"updateModal\" tabindex=\"-1\" role=\"dialog\"  aria-hidden=\"true\">\n        <div class=\"modal-dialog \">\n            <div class=\"modal-content\">\n                <div class=\"modal-header\">\n                    <h4 class=\"modal-title\" >${I18n.jobgroup_edit}</h4>\n                </div>\n                <div class=\"modal-body\">\n                    <form class=\"form-horizontal form\" role=\"form\" >\n                        <div class=\"form-group\">\n                            <label for=\"lastname\" class=\"col-sm-2 control-label\">AppName<font color=\"red\">*</font></label>\n                            <div class=\"col-sm-10\"><input type=\"text\" class=\"form-control\" name=\"appname\" placeholder=\"${I18n.system_please_input}AppName\" maxlength=\"64\" ></div>\n                        </div>\n                        <div class=\"form-group\">\n                            <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobgroup_field_title}<font color=\"red\">*</font></label>\n                            <div class=\"col-sm-10\"><input type=\"text\" class=\"form-control\" name=\"title\" placeholder=\"${I18n.system_please_input}${I18n.jobgroup_field_title}\" maxlength=\"12\" ></div>\n                        </div>\n                        <div class=\"form-group\">\n                            <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobgroup_field_addressType}<font color=\"red\">*</font></label>\n                            <div class=\"col-sm-10\">\n                                <input type=\"radio\" name=\"addressType\" value=\"0\" />${I18n.jobgroup_field_addressType_0}\n                                &nbsp;&nbsp;&nbsp;&nbsp;\n                                <input type=\"radio\" name=\"addressType\" value=\"1\" />${I18n.jobgroup_field_addressType_1}\n                            </div>\n                        </div>\n                        <div class=\"form-group\">\n                            <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobgroup_field_registryList}<font color=\"red\">*</font></label>\n                            <div class=\"col-sm-10\">\n                                <textarea class=\"textarea\" name=\"addressList\" maxlength=\"20000\" placeholder=\"${I18n.jobgroup_field_registryList_placeholder}\" readonly=\"readonly\" style=\"background-color:#eee; width: 100%; height: 100px; font-size: 14px; line-height: 15px; border: 1px solid #dddddd; padding: 5px;\"></textarea>\n                            </div>\n                        </div>\n                        <hr>\n                        <div class=\"form-group\">\n                            <div class=\"col-sm-offset-3 col-sm-6\">\n                                <button type=\"submit\" class=\"btn btn-primary\"  >${I18n.system_save}</button>\n                                <button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">${I18n.system_cancel}</button>\n                                <input type=\"hidden\" name=\"id\" >\n                            </div>\n                        </div>\n                    </form>\n                </div>\n            </div>\n        </div>\n    </div>\n\t\n\t<!-- footer -->\n\t<@netCommon.commonFooter />\n</div>\n\n<@netCommon.commonScript />\n<!-- DataTables -->\n<script src=\"${request.contextPath}/static/adminlte/bower_components/datatables.net/js/jquery.dataTables.min.js\"></script>\n<script src=\"${request.contextPath}/static/adminlte/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js\"></script>\n<script src=\"${request.contextPath}/static/js/jobgroup.index.1.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/jobinfo/jobinfo.index.ftl",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  \t<#import \"../common/common.macro.ftl\" as netCommon>\n\t<@netCommon.commonStyle />\n\t<!-- DataTables -->\n  \t<link rel=\"stylesheet\" href=\"${request.contextPath}/static/adminlte/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css\">\n    <title>${I18n.admin_name}</title>\n</head>\n<body class=\"hold-transition skin-blue sidebar-mini <#if cookieMap?exists && cookieMap[\"xxljob_adminlte_settings\"]?exists && \"off\" == cookieMap[\"xxljob_adminlte_settings\"].value >sidebar-collapse</#if>\">\n<div class=\"wrapper\">\n\t<!-- header -->\n\t<@netCommon.commonHeader />\n\t<!-- left -->\n\t<@netCommon.commonLeft \"jobinfo\" />\n\t\n\t<!-- Content Wrapper. Contains page content -->\n\t<div class=\"content-wrapper\">\n\t\t<!-- Content Header (Page header) -->\n\t\t<section class=\"content-header\">\n\t\t\t<h1>${I18n.jobinfo_name}</h1>\n\t\t</section>\n\t\t\n\t\t<!-- Main content -->\n\t    <section class=\"content\">\n\t    \n\t    \t<div class=\"row\">\n\t    \t\t<div class=\"col-xs-3\">\n\t              \t<div class=\"input-group\">\n\t                \t<span class=\"input-group-addon\">${I18n.jobinfo_field_jobgroup}</span>\n                \t\t<select class=\"form-control\" id=\"jobGroup\" >\n                \t\t\t<#list JobGroupList as group>\n                \t\t\t\t<option value=\"${group.id}\" <#if jobGroup==group.id>selected</#if> >${group.title}</option>\n                \t\t\t</#list>\n\t                  \t</select>\n\t              \t</div>\n\t            </div>\n                <div class=\"col-xs-1\">\n                    <div class=\"input-group\">\n                        <select class=\"form-control\" id=\"triggerStatus\" >\n                            <option value=\"-1\" >${I18n.system_all}</option>\n                            <option value=\"0\" >${I18n.jobinfo_opt_stop}</option>\n                            <option value=\"1\" >${I18n.jobinfo_opt_start}</option>\n                        </select>\n                    </div>\n                </div>\n                <div class=\"col-xs-2\">\n                    <div class=\"input-group\">\n                        <input type=\"text\" class=\"form-control\" id=\"jobDesc\" placeholder=\"${I18n.system_please_input}${I18n.jobinfo_field_jobdesc}\" >\n                    </div>\n                </div>\n                <div class=\"col-xs-2\">\n                    <div class=\"input-group\">\n                        <input type=\"text\" class=\"form-control\" id=\"executorHandler\" placeholder=\"${I18n.system_please_input}JobHandler\" >\n                    </div>\n                </div>\n                <div class=\"col-xs-2\">\n                    <div class=\"input-group\">\n                        <input type=\"text\" class=\"form-control\" id=\"author\" placeholder=\"${I18n.system_please_input}${I18n.jobinfo_field_author}\" >\n                    </div>\n                </div>\n\t            <div class=\"col-xs-1\">\n\t            \t<button class=\"btn btn-block btn-info\" id=\"searchBtn\">${I18n.system_search}</button>\n\t            </div>\n\t            <div class=\"col-xs-1\">\n\t            \t<button class=\"btn btn-block btn-success add\" type=\"button\">${I18n.jobinfo_field_add}</button>\n\t            </div>\n          \t</div>\n\t    \t\n\t\t\t<div class=\"row\">\n\t\t\t\t<div class=\"col-xs-12\">\n\t\t\t\t\t<div class=\"box\">\n\t\t\t            <#--<div class=\"box-header hide\">\n\t\t\t            \t<h3 class=\"box-title\">调度列表</h3>\n\t\t\t            </div>-->\n\t\t\t            <div class=\"box-body\" >\n\t\t\t              \t<table id=\"job_list\" class=\"table table-bordered table-striped\" width=\"100%\" >\n\t\t\t\t                <thead>\n\t\t\t\t\t            \t<tr>\n\t\t\t\t\t            \t\t<th name=\"id\" >${I18n.jobinfo_field_id}</th>\n\t\t\t\t\t                \t<th name=\"jobGroup\" >${I18n.jobinfo_field_jobgroup}</th>\n\t\t\t\t\t                  \t<th name=\"jobDesc\" >${I18n.jobinfo_field_jobdesc}</th>\n                                        <th name=\"scheduleType\" >${I18n.schedule_type}</th>\n                                        <th name=\"glueType\" >${I18n.jobinfo_field_gluetype}</th>\n                                        <th name=\"executorParam\" >${I18n.jobinfo_field_executorparam}</th>\n\t\t\t\t\t                  \t<th name=\"addTime\" >addTime</th>\n\t\t\t\t\t                  \t<th name=\"updateTime\" >updateTime</th>\n\t\t\t\t\t                  \t<th name=\"author\" >${I18n.jobinfo_field_author}</th>\n\t\t\t\t\t                  \t<th name=\"alarmEmail\" >${I18n.jobinfo_field_alarmemail}</th>\n\t\t\t\t\t                  \t<th name=\"triggerStatus\" >${I18n.system_status}</th>\n\t\t\t\t\t                  \t<th>${I18n.system_opt}</th>\n\t\t\t\t\t                </tr>\n\t\t\t\t                </thead>\n\t\t\t\t                <tbody></tbody>\n\t\t\t\t                <tfoot></tfoot>\n\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t    </section>\n\t</div>\n\t\n\t<!-- footer -->\n\t<@netCommon.commonFooter />\n</div>\n\n<!-- job新增.模态框 -->\n<div class=\"modal fade\" id=\"addModal\" tabindex=\"-1\" role=\"dialog\"  aria-hidden=\"true\">\n\t<div class=\"modal-dialog modal-lg\">\n\t\t<div class=\"modal-content\">\n\t\t\t<div class=\"modal-header\">\n            \t<h4 class=\"modal-title\" >${I18n.jobinfo_field_add}</h4>\n         \t</div>\n         \t<div class=\"modal-body\">\n\t\t\t\t<form class=\"form-horizontal form\" role=\"form\" >\n\n                    <p style=\"margin: 0 0 10px;text-align: left;border-bottom: 1px solid #e5e5e5;color: gray;\">${I18n.jobinfo_conf_base}</p>    <#-- 基础信息 -->\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_jobgroup}<font color=\"red\">*</font></label>\n\t\t\t\t\t\t<div class=\"col-sm-4\">\n\t\t\t\t\t\t\t<select class=\"form-control\" name=\"jobGroup\" >\n\t\t            \t\t\t<#list JobGroupList as group>\n\t\t            \t\t\t\t<option value=\"${group.id}\" <#if jobGroup==group.id>selected</#if> >${group.title}</option>\n\t\t            \t\t\t</#list>\n\t\t                  \t</select>\n\t\t\t\t\t\t</div>\n\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_jobdesc}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"jobDesc\" placeholder=\"${I18n.system_please_input}${I18n.jobinfo_field_jobdesc}\" maxlength=\"50\" ></div>\n\t\t\t\t\t</div>\n                    <div class=\"form-group\">\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_author}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"author\" placeholder=\"${I18n.system_please_input}${I18n.jobinfo_field_author}\" maxlength=\"50\" ></div>\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_alarmemail}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"alarmEmail\" placeholder=\"${I18n.jobinfo_field_alarmemail_placeholder}\" maxlength=\"100\" ></div>\n                    </div>\n\n                    <br>\n                    <p style=\"margin: 0 0 10px;text-align: left;border-bottom: 1px solid #e5e5e5;color: gray;\">${I18n.jobinfo_conf_schedule}</p>    <#-- 调度 -->\n                    <div class=\"form-group\">\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.schedule_type}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-4\">\n                            <select class=\"form-control scheduleType\" name=\"scheduleType\" >\n                                <#list ScheduleTypeEnum as item>\n                                    <option value=\"${item}\" <#if 'CRON' == item >selected</#if> >${item.title}</option>\n                                </#list>\n                            </select>\n                        </div>\n\n                        <input type=\"hidden\" name=\"scheduleConf\" />\n                        <div class=\"schedule_conf schedule_conf_NONE\" style=\"display: none\" >\n                        </div>\n                        <div class=\"schedule_conf schedule_conf_CRON\" >\n                            <label for=\"lastname\" class=\"col-sm-2 control-label\">Cron<font color=\"red\">*</font></label>\n                            <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"schedule_conf_CRON\" placeholder=\"${I18n.system_please_input}Cron\" maxlength=\"128\" ></div>\n                        </div>\n                        <div class=\"schedule_conf schedule_conf_FIX_RATE\" style=\"display: none\" >\n                            <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.schedule_type_fix_rate}<font color=\"red\">*</font></label>\n                            <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"schedule_conf_FIX_RATE\" placeholder=\"${I18n.system_please_input} （ Second ）\" maxlength=\"10\" onkeyup=\"this.value=this.value.replace(/\\D/g,'')\" onafterpaste=\"this.value=this.value.replace(/\\D/g,'')\" ></div>\n                        </div>\n                        <div class=\"schedule_conf schedule_conf_FIX_DELAY\" style=\"display: none\" >\n                            <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.schedule_type_fix_delay}<font color=\"red\">*</font></label>\n                            <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"schedule_conf_FIX_DELAY\" placeholder=\"${I18n.system_please_input} （ Second ）\" maxlength=\"10\" onkeyup=\"this.value=this.value.replace(/\\D/g,'')\" onafterpaste=\"this.value=this.value.replace(/\\D/g,'')\" ></div>\n                        </div>\n                    </div>\n\n                    <br>\n                    <p style=\"margin: 0 0 10px;text-align: left;border-bottom: 1px solid #e5e5e5;color: gray;\">${I18n.jobinfo_conf_job}</p>    <#-- 任务配置 -->\n\n                    <div class=\"form-group\">\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_gluetype}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-4\">\n                            <select class=\"form-control glueType\" name=\"glueType\" >\n                                <#list GlueTypeEnum as item>\n                                    <option value=\"${item}\" >${item.desc}</option>\n                                </#list>\n                            </select>\n                        </div>\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">JobHandler<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"executorHandler\" placeholder=\"${I18n.system_please_input}JobHandler\" maxlength=\"100\" ></div>\n                    </div>\n\n                    <div class=\"form-group\">\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_executorparam}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-10\">\n                            <textarea class=\"textarea form-control\" name=\"executorParam\" placeholder=\"${I18n.system_please_input}${I18n.jobinfo_field_executorparam}\" maxlength=\"512\" style=\"height: 63px; line-height: 1.2;\"></textarea>\n                        </div>\n                    </div>\n\n                    <br>\n                    <p style=\"margin: 0 0 10px;text-align: left;border-bottom: 1px solid #e5e5e5;color: gray;\">${I18n.jobinfo_conf_advanced}</p>    <#-- 高级配置 -->\n\n                    <div class=\"form-group\">\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_executorRouteStrategy}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-4\">\n                            <select class=\"form-control\" name=\"executorRouteStrategy\" >\n\t\t\t\t\t\t\t<#list ExecutorRouteStrategyEnum as item>\n                                <option value=\"${item}\" >${item.title}</option>\n\t\t\t\t\t\t\t</#list>\n                            </select>\n                        </div>\n\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_childJobId}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"childJobId\" placeholder=\"${I18n.jobinfo_field_childJobId_placeholder}\" maxlength=\"100\" ></div>\n                    </div>\n\n                    <div class=\"form-group\">\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.misfire_strategy}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-4\">\n                            <select class=\"form-control\" name=\"misfireStrategy\" >\n                                <#list MisfireStrategyEnum as item>\n                                    <option value=\"${item}\" <#if 'DO_NOTHING' == item >selected</#if> >${item.title}</option>\n                                </#list>\n                            </select>\n                        </div>\n\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_executorBlockStrategy}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-4\">\n                            <select class=\"form-control\" name=\"executorBlockStrategy\" >\n\t\t\t\t\t\t\t\t<#list ExecutorBlockStrategyEnum as item>\n                                    <option value=\"${item}\" >${item.title}</option>\n                                </#list>\n                            </select>\n                        </div>\n                    </div>\n\n                    <div class=\"form-group\">\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_timeout}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"executorTimeout\" placeholder=\"${I18n.jobinfo_field_executorTimeout_placeholder}\" maxlength=\"6\" onkeyup=\"this.value=this.value.replace(/\\D/g,'')\" onafterpaste=\"this.value=this.value.replace(/\\D/g,'')\" ></div>\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_executorFailRetryCount}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"executorFailRetryCount\" placeholder=\"${I18n.jobinfo_field_executorFailRetryCount_placeholder}\" maxlength=\"4\" onkeyup=\"this.value=this.value.replace(/\\D/g,'')\" onafterpaste=\"this.value=this.value.replace(/\\D/g,'')\" ></div>\n                    </div>\n\n                    <hr>\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<div class=\"col-sm-offset-3 col-sm-6\">\n\t\t\t\t\t\t\t<button type=\"submit\" class=\"btn btn-primary\"  >${I18n.system_save}</button>\n\t\t\t\t\t\t\t<button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">${I18n.system_cancel}</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n<input type=\"hidden\" name=\"glueRemark\" value=\"GLUE代码初始化\" >\n<textarea name=\"glueSource\" style=\"display:none;\" ></textarea>\n<textarea class=\"glueSource_java\" style=\"display:none;\" >\npackage com.xxl.job.service.handler;\n\nimport com.xxl.job.core.context.XxlJobHelper;\nimport com.xxl.job.core.handler.IJobHandler;\n\npublic class DemoGlueJobHandler extends IJobHandler {\n\n\t@Override\n\tpublic void execute() throws Exception {\n\t\tXxlJobHelper.log(\"XXL-JOB, Hello World.\");\n\t}\n\n}\n</textarea>\n<textarea class=\"glueSource_shell\" style=\"display:none;\" >\n#!/bin/bash\necho \"xxl-job: hello shell\"\n\necho \"${I18n.jobinfo_script_location}：$0\"\necho \"${I18n.jobinfo_field_executorparam}：$1\"\necho \"${I18n.jobinfo_shard_index} = $2\"\necho \"${I18n.jobinfo_shard_total} = $3\"\n<#--echo \"参数数量：$#\"\nfor param in $*\ndo\n    echo \"参数 : $param\"\n    sleep 1s\ndone-->\n\necho \"Good bye!\"\nexit 0\n</textarea>\n<textarea class=\"glueSource_python\" style=\"display:none;\" >\n#!/usr/bin/python\n# -*- coding: UTF-8 -*-\nimport time\nimport sys\n\nprint \"xxl-job: hello python\"\n\nprint \"${I18n.jobinfo_script_location}：\", sys.argv[0]\nprint \"${I18n.jobinfo_field_executorparam}：\", sys.argv[1]\nprint \"${I18n.jobinfo_shard_index}：\", sys.argv[2]\nprint \"${I18n.jobinfo_shard_total}：\", sys.argv[3]\n<#--for i in range(1, len(sys.argv)):\n\ttime.sleep(1)\n\tprint \"参数\", i, sys.argv[i]-->\n\nprint \"Good bye!\"\nexit(0)\n<#--\nimport logging\nlogging.basicConfig(level=logging.DEBUG)\nlogging.info(\"脚本文件：\" + sys.argv[0])\n-->\n</textarea>\n<#--这里有问题，新建一个运行模式为 php 的任务后，GLUE 中没有下边的 php 代码-->\n<textarea class=\"glueSource_php\" style=\"display:none;\" >\n<?php\n\n    echo \"xxl-job: hello php  \\n\";\n\n    echo \"${I18n.jobinfo_script_location}：$argv[0]  \\n\";\n    echo \"${I18n.jobinfo_field_executorparam}：$argv[1]  \\n\";\n    echo \"${I18n.jobinfo_shard_index} = $argv[2]  \\n\";\n    echo \"${I18n.jobinfo_shard_total} = $argv[3]  \\n\";\n\n    echo \"Good bye!  \\n\";\n    exit(0);\n\n?>\n</textarea>\n<textarea class=\"glueSource_nodejs\" style=\"display:none;\" >\n#!/usr/bin/env node\nconsole.log(\"xxl-job: hello nodejs\")\n\nvar arguments = process.argv\n\nconsole.log(\"${I18n.jobinfo_script_location}: \" + arguments[1])\nconsole.log(\"${I18n.jobinfo_field_executorparam}: \" + arguments[2])\nconsole.log(\"${I18n.jobinfo_shard_index}: \" + arguments[3])\nconsole.log(\"${I18n.jobinfo_shard_total}: \" + arguments[4])\n<#--for (var i = 2; i < arguments.length; i++){\n\tconsole.log(\"参数 %s = %s\", (i-1), arguments[i]);\n}-->\n\nconsole.log(\"Good bye!\")\nprocess.exit(0)\n</textarea>\n<textarea class=\"glueSource_powershell\" style=\"display:none;\" >\nWrite-Host \"xxl-job: hello powershell\"\n\nWrite-Host \"${I18n.jobinfo_script_location}: \" $MyInvocation.MyCommand.Definition\nWrite-Host \"${I18n.jobinfo_field_executorparam}: \"\n\tif ($args.Count -gt 2) { $args[0..($args.Count-3)] }\nWrite-Host \"${I18n.jobinfo_shard_index}: \" $args[$args.Count-2]\nWrite-Host \"${I18n.jobinfo_shard_total}: \" $args[$args.Count-1]\n\nWrite-Host \"Good bye!\"\nexit 0\n</textarea>\n\t\t\t\t</form>\n         \t</div>\n\t\t</div>\n\t</div>\n</div>\n\n<!-- 更新.模态框 -->\n<div class=\"modal fade\" id=\"updateModal\" tabindex=\"-1\" role=\"dialog\"  aria-hidden=\"true\">\n\t<div class=\"modal-dialog modal-lg\">\n\t\t<div class=\"modal-content\">\n\t\t\t<div class=\"modal-header\">\n            \t<h4 class=\"modal-title\" >${I18n.jobinfo_field_update}</h4>\n         \t</div>\n         \t<div class=\"modal-body\">\n\t\t\t\t<form class=\"form-horizontal form\" role=\"form\" >\n\n                    <p style=\"margin: 0 0 10px;text-align: left;border-bottom: 1px solid #e5e5e5;color: gray;\">${I18n.jobinfo_conf_base}</p>    <#-- 基础信息 -->\n                    <div class=\"form-group\">\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_jobgroup}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-4\">\n                            <select class=\"form-control\" name=\"jobGroup\" >\n                                <#list JobGroupList as group>\n                                    <option value=\"${group.id}\" >${group.title}</option>\n                                </#list>\n                            </select>\n                        </div>\n\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_jobdesc}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"jobDesc\" placeholder=\"${I18n.system_please_input}${I18n.jobinfo_field_jobdesc}\" maxlength=\"50\" ></div>\n                    </div>\n                    <div class=\"form-group\">\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_author}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"author\" placeholder=\"${I18n.system_please_input}${I18n.jobinfo_field_author}\" maxlength=\"50\" ></div>\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_alarmemail}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"alarmEmail\" placeholder=\"${I18n.jobinfo_field_alarmemail_placeholder}\" maxlength=\"100\" ></div>\n                    </div>\n\n                    <br>\n                    <p style=\"margin: 0 0 10px;text-align: left;border-bottom: 1px solid #e5e5e5;color: gray;\">${I18n.jobinfo_conf_schedule}</p>    <#-- 调度配置 -->\n                    <div class=\"form-group\">\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.schedule_type}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-4\">\n                            <select class=\"form-control scheduleType\" name=\"scheduleType\" >\n                                <#list ScheduleTypeEnum as item>\n                                    <option value=\"${item}\" >${item.title}</option>\n                                </#list>\n                            </select>\n                        </div>\n\n                        <input type=\"hidden\" name=\"scheduleConf\" />\n                        <div class=\"schedule_conf schedule_conf_NONE\" style=\"display: none\" >\n                        </div>\n                        <div class=\"schedule_conf schedule_conf_CRON\" >\n                            <label for=\"lastname\" class=\"col-sm-2 control-label\">Cron<font color=\"red\">*</font></label>\n                            <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"schedule_conf_CRON\" placeholder=\"${I18n.system_please_input}Cron\" maxlength=\"128\" ></div>\n                        </div>\n                        <div class=\"schedule_conf schedule_conf_FIX_RATE\" style=\"display: none\" >\n                            <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.schedule_type_fix_rate}<font color=\"red\">*</font></label>\n                            <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"schedule_conf_FIX_RATE\" placeholder=\"${I18n.system_please_input} （ Second ）\" maxlength=\"10\" onkeyup=\"this.value=this.value.replace(/\\D/g,'')\" onafterpaste=\"this.value=this.value.replace(/\\D/g,'')\" ></div>\n                        </div>\n                        <div class=\"schedule_conf schedule_conf_FIX_DELAY\" style=\"display: none\" >\n                            <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.schedule_type_fix_delay}<font color=\"red\">*</font></label>\n                            <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"schedule_conf_FIX_DELAY\" placeholder=\"${I18n.system_please_input} （ Second ）\" maxlength=\"10\" onkeyup=\"this.value=this.value.replace(/\\D/g,'')\" onafterpaste=\"this.value=this.value.replace(/\\D/g,'')\" ></div>\n                        </div>\n                    </div>\n\n                    <br>\n                    <p style=\"margin: 0 0 10px;text-align: left;border-bottom: 1px solid #e5e5e5;color: gray;\">${I18n.jobinfo_conf_job}</p>    <#-- 任务配置 -->\n\n                    <div class=\"form-group\">\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_gluetype}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-4\">\n                            <select class=\"form-control glueType\" name=\"glueType\" disabled >\n                                <#list GlueTypeEnum as item>\n                                    <option value=\"${item}\" >${item.desc}</option>\n                                </#list>\n                            </select>\n                        </div>\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">JobHandler<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"executorHandler\" placeholder=\"${I18n.system_please_input}JobHandler\" maxlength=\"100\" ></div>\n                    </div>\n\n                    <div class=\"form-group\">\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_executorparam}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-10\">\n                            <textarea class=\"textarea form-control\" name=\"executorParam\" placeholder=\"${I18n.system_please_input}${I18n.jobinfo_field_executorparam}\" maxlength=\"512\" style=\"height: 63px; line-height: 1.2;\"></textarea>\n                        </div>\n                    </div>\n\n                    <br>\n                    <p style=\"margin: 0 0 10px;text-align: left;border-bottom: 1px solid #e5e5e5;color: gray;\">${I18n.jobinfo_conf_advanced}</p>    <#-- 高级配置 -->\n\n                    <div class=\"form-group\">\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_executorRouteStrategy}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-4\">\n                            <select class=\"form-control\" name=\"executorRouteStrategy\" >\n                                <#list ExecutorRouteStrategyEnum as item>\n                                    <option value=\"${item}\" >${item.title}</option>\n                                </#list>\n                            </select>\n                        </div>\n\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_childJobId}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"childJobId\" placeholder=\"${I18n.jobinfo_field_childJobId_placeholder}\" maxlength=\"100\" ></div>\n                    </div>\n\n                    <div class=\"form-group\">\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.misfire_strategy}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-4\">\n                            <select class=\"form-control\" name=\"misfireStrategy\" >\n                                <#list MisfireStrategyEnum as item>\n                                    <option value=\"${item}\" <#if 'DO_NOTHING' == item >selected</#if> >${item.title}</option>\n                                </#list>\n                            </select>\n                        </div>\n\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_executorBlockStrategy}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-4\">\n                            <select class=\"form-control\" name=\"executorBlockStrategy\" >\n                                <#list ExecutorBlockStrategyEnum as item>\n                                    <option value=\"${item}\" >${item.title}</option>\n                                </#list>\n                            </select>\n                        </div>\n                    </div>\n\n                    <div class=\"form-group\">\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_timeout}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"executorTimeout\" placeholder=\"${I18n.jobinfo_field_executorTimeout_placeholder}\" maxlength=\"6\" onkeyup=\"this.value=this.value.replace(/\\D/g,'')\" onafterpaste=\"this.value=this.value.replace(/\\D/g,'')\" ></div>\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_executorFailRetryCount}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-4\"><input type=\"text\" class=\"form-control\" name=\"executorFailRetryCount\" placeholder=\"${I18n.jobinfo_field_executorFailRetryCount_placeholder}\" maxlength=\"4\" onkeyup=\"this.value=this.value.replace(/\\D/g,'')\" onafterpaste=\"this.value=this.value.replace(/\\D/g,'')\" ></div>\n                    </div>\n\n\t\t\t\t\t<hr>\n\t\t\t\t\t<div class=\"form-group\">\n                        <div class=\"col-sm-offset-3 col-sm-6\">\n\t\t\t\t\t\t\t<button type=\"submit\" class=\"btn btn-primary\"  >${I18n.system_save}</button>\n\t\t\t\t\t\t\t<button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">${I18n.system_cancel}</button>\n                            <input type=\"hidden\" name=\"id\" >\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t</form>\n         \t</div>\n\t\t</div>\n\t</div>\n</div>\n\n<#-- trigger -->\n<div class=\"modal fade\" id=\"jobTriggerModal\" tabindex=\"-1\" role=\"dialog\"  aria-hidden=\"true\">\n    <div class=\"modal-dialog \">\n        <div class=\"modal-content\">\n            <div class=\"modal-header\">\n                <h4 class=\"modal-title\" >${I18n.jobinfo_opt_run}</h4>\n            </div>\n            <div class=\"modal-body\">\n                <form class=\"form-horizontal form\" role=\"form\" >\n                    <div class=\"form-group\">\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.jobinfo_field_executorparam}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-10\">\n                            <textarea class=\"textarea form-control\" name=\"executorParam\" placeholder=\"${I18n.system_please_input}${I18n.jobinfo_field_executorparam}\" maxlength=\"512\" style=\"height: 63px; line-height: 1.2;\"></textarea>\n                        </div>\n                    </div>\n                    <div class=\"form-group\">\n                        <label for=\"firstname\" class=\"col-sm-2 control-label\">${I18n.jobgroup_field_registryList}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-10\">\n                            <textarea class=\"textarea form-control\" name=\"addressList\" placeholder=\"${I18n.jobinfo_opt_run_tips}\" maxlength=\"512\" style=\"height: 63px; line-height: 1.2;\"></textarea>\n                        </div>\n                    </div>\n                    <hr>\n                    <div class=\"form-group\">\n                        <div class=\"col-sm-offset-3 col-sm-6\">\n                            <button type=\"button\" class=\"btn btn-primary ok\" >${I18n.system_save}</button>\n                            <button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">${I18n.system_cancel}</button>\n                            <input type=\"hidden\" name=\"id\" >\n                        </div>\n                    </div>\n                </form>\n            </div>\n        </div>\n    </div>\n</div>\n\n<@netCommon.commonScript />\n<!-- DataTables -->\n<script src=\"${request.contextPath}/static/adminlte/bower_components/datatables.net/js/jquery.dataTables.min.js\"></script>\n<script src=\"${request.contextPath}/static/adminlte/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js\"></script>\n<!-- moment -->\n<script src=\"${request.contextPath}/static/adminlte/bower_components/moment/moment.min.js\"></script>\n<#-- cronGen -->\n<script src=\"${request.contextPath}/static/plugins/cronGen/cronGen<#if I18n.admin_i18n?default('')?length gt 0 >_${I18n.admin_i18n}</#if>.js\"></script>\n<script src=\"${request.contextPath}/static/js/jobinfo.index.1.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/joblog/joblog.detail.ftl",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <#import \"../common/common.macro.ftl\" as netCommon>\n    <@netCommon.commonStyle />\n    <title>${I18n.admin_name}</title>\n</head>\n<body class=\"hold-transition skin-blue layout-top-nav\">\n\n<div class=\"wrapper\">\n\n    <header class=\"main-header\">\n        <nav class=\"navbar navbar-static-top\">\n            <div class=\"container\">\n                <#-- icon -->\n                <div class=\"navbar-header\">\n                    <a class=\"navbar-brand\"><b>${I18n.joblog_rolling_log}</b> Console</a>\n                    <button type=\"button\" class=\"navbar-toggle collapsed\" data-toggle=\"collapse\" data-target=\"#navbar-collapse\">\n                        <i class=\"fa fa-bars\"></i>\n                    </button>\n                </div>\n\n                <#-- left nav -->\n                <div class=\"collapse navbar-collapse pull-left\" id=\"navbar-collapse\">\n                    <ul class=\"nav navbar-nav\">\n                        <#--<li class=\"active\" ><a href=\"javascript:;\">任务：<span class=\"sr-only\">(current)</span></a></li>-->\n                    </ul>\n                </div>\n\n                <#-- right nav -->\n                <div class=\"navbar-custom-menu\">\n                    <ul class=\"nav navbar-nav\">\n                        <li>\n                            <a href=\"javascript:window.location.reload();\" >\n                                <i class=\"fa fa-fw fa-refresh\" ></i>\n                                ${I18n.joblog_rolling_log_refresh}\n                            </a>\n                        </li>\n                    </ul>\n                </div>\n\n            </div>\n        </nav>\n    </header>\n\n    <div class=\"content-wrapper\" >\n        <section class=\"content\">\n            <pre style=\"font-size:12px;position:relative;\" >\n                <div id=\"logConsole\"></div>\n                <li class=\"fa fa-refresh fa-spin\" style=\"font-size: 20px;float: left;\" id=\"logConsoleRunning\" ></li>\n            </pre>\n        </section>\n    </div>\n\n    <!-- footer -->\n    <@netCommon.commonFooter />\n\n</div>\n\n<@netCommon.commonScript />\n<script>\n    // 参数\n    var triggerCode = '${triggerCode}';\n    var handleCode = '${handleCode}';\n    var logId = '${logId}';\n</script>\n<script src=\"${request.contextPath}/static/js/joblog.detail.1.js\"></script>\n\n</body>\n</html>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/joblog/joblog.index.ftl",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  \t<#import \"../common/common.macro.ftl\" as netCommon>\n\t<@netCommon.commonStyle />\n\t<!-- DataTables -->\n  \t<link rel=\"stylesheet\" href=\"${request.contextPath}/static/adminlte/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css\">\n  \t<!-- daterangepicker -->\n  \t<link rel=\"stylesheet\" href=\"${request.contextPath}/static/adminlte/bower_components/bootstrap-daterangepicker/daterangepicker.css\">\n    <title>${I18n.admin_name}</title>\n</head>\n<body class=\"hold-transition skin-blue sidebar-mini <#if cookieMap?exists && cookieMap[\"xxljob_adminlte_settings\"]?exists && \"off\" == cookieMap[\"xxljob_adminlte_settings\"].value >sidebar-collapse</#if> \">\n<div class=\"wrapper\">\n\t<!-- header -->\n\t<@netCommon.commonHeader />\n\t<!-- left -->\n\t<@netCommon.commonLeft \"joblog\" />\n\t\n\t<!-- Content Wrapper. Contains page content -->\n\t<div class=\"content-wrapper\">\n\t\t<!-- Content Header (Page header) -->\n\t\t<section class=\"content-header\">\n\t\t\t<h1>${I18n.joblog_name}</h1>\n\t\t</section>\n\t\t\n\t\t<!-- Main content -->\n\t    <section class=\"content\">\n\t    \t<div class=\"row\">\n\t    \t\t<div class=\"col-xs-2\">\n \t\t\t\t\t<div class=\"input-group\">\n\t                \t<span class=\"input-group-addon\">${I18n.jobinfo_field_jobgroup}</span>\n                \t\t<select class=\"form-control\" id=\"jobGroup\"  paramVal=\"<#if jobInfo?exists>${jobInfo.jobGroup}</#if>\" >\n                            <#if Request[\"XXL_JOB_LOGIN_IDENTITY\"].role == 1>\n                                <option value=\"0\" >${I18n.system_all}</option>  <#-- 仅管理员支持查询全部；普通用户仅支持查询有权限的 jobGroup -->\n                            </#if>\n                \t\t\t<#list JobGroupList as group>\n                \t\t\t\t<option value=\"${group.id}\" >${group.title}</option>\n                \t\t\t</#list>\n\t                  \t</select>\n\t              \t</div>\n\t            </div>\n\t            <div class=\"col-xs-2\">\n\t              \t<div class=\"input-group\">\n\t                \t<span class=\"input-group-addon\">${I18n.jobinfo_job}</span>\n                        <select class=\"form-control\" id=\"jobId\" paramVal=\"<#if jobInfo?exists>${jobInfo.id}</#if>\" >\n                            <option value=\"0\" >${I18n.system_all}</option>\n\t\t\t\t\t\t</select>\n\t              \t</div>\n\t            </div>\n\n                <div class=\"col-xs-2\">\n                    <div class=\"input-group\">\n                        <span class=\"input-group-addon\">${I18n.joblog_status}</span>\n                        <select class=\"form-control\" id=\"logStatus\" >\n                            <option value=\"-1\" >${I18n.joblog_status_all}</option>\n                            <option value=\"1\" >${I18n.joblog_status_suc}</option>\n                            <option value=\"2\" >${I18n.joblog_status_fail}</option>\n                            <option value=\"3\" >${I18n.joblog_status_running}</option>\n                        </select>\n                    </div>\n                </div>\n\n\t            <div class=\"col-xs-4\">\n              \t\t<div class=\"input-group\">\n                \t\t<span class=\"input-group-addon\">\n\t                  \t\t${I18n.joblog_field_triggerTime}\n\t                \t</span>\n\t                \t<input type=\"text\" class=\"form-control\" id=\"filterTime\" readonly >\n\t              \t</div>\n\t            </div>\n\n                <div class=\"col-xs-1\">\n                    <button class=\"btn btn-block btn-info\" id=\"searchBtn\">${I18n.system_search}</button>\n                </div>\n\n\t            <div class=\"col-xs-1\">\n                    <button class=\"btn btn-block btn-default\" id=\"clearLog\">${I18n.joblog_clean}</button>\n\t            </div>\n          \t</div>\n\t\t\t\n\t\t\t<div class=\"row\">\n\t\t\t\t<div class=\"col-xs-12\">\n\t\t\t\t\t<div class=\"box\">\n\t\t\t            <#--<div class=\"box-header hide\"><h3 class=\"box-title\">调度日志</h3></div>-->\n\t\t\t            <div class=\"box-body\">\n\t\t\t              \t<table id=\"joblog_list\" class=\"table table-bordered table-striped display\" width=\"100%\" >\n\t\t\t\t                <thead>\n\t\t\t\t\t            \t<tr>\n                                        <th name=\"jobId\" >${I18n.jobinfo_field_id}</th>\n                                        <th name=\"jobGroup\" >jobGroup</th>\n\t\t\t\t\t\t\t\t\t\t<#--<th name=\"executorAddress\" >执行器地址</th>\n\t\t\t\t\t\t\t\t\t\t<th name=\"glueType\" >运行模式</th>\n                                      \t<th name=\"executorParam\" >任务参数</th>-->\n                                        <th name=\"triggerTime\" >${I18n.joblog_field_triggerTime}</th>\n                                        <th name=\"triggerCode\" >${I18n.joblog_field_triggerCode}</th>\n                                        <th name=\"triggerMsg\" >${I18n.joblog_field_triggerMsg}</th>\n\t\t\t\t\t                  \t<th name=\"handleTime\" >${I18n.joblog_field_handleTime}</th>\n\t\t\t\t\t                  \t<th name=\"handleCode\" >${I18n.joblog_field_handleCode}</th>\n\t\t\t\t\t                  \t<th name=\"handleMsg\" >${I18n.joblog_field_handleMsg}</th>\n\t\t\t\t\t                  \t<th name=\"handleMsg\" >${I18n.system_opt}</th>\n\t\t\t\t\t                </tr>\n\t\t\t\t                </thead>\n\t\t\t\t                <tbody></tbody>\n\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t    </section>\n\t</div>\n\t\n\t<!-- footer -->\n\t<@netCommon.commonFooter />\n</div>\n\n<!-- 日志清理.模态框 -->\n<div class=\"modal fade\" id=\"clearLogModal\" tabindex=\"-1\" role=\"dialog\"  aria-hidden=\"true\">\n    <div class=\"modal-dialog\">\n        <div class=\"modal-content\">\n            <div class=\"modal-header\">\n                <h4 class=\"modal-title\" >${I18n.joblog_clean_log}</h4>\n            </div>\n            <div class=\"modal-body\">\n                <form class=\"form-horizontal form\" role=\"form\" >\n                    <div class=\"form-group\">\n                        <label class=\"col-sm-3 control-label\">${I18n.jobinfo_field_jobgroup}：</label>\n                        <div class=\"col-sm-9\">\n                            <input type=\"text\" class=\"form-control jobGroupText\" readonly >\n\t\t\t\t\t\t\t<input type=\"hidden\" name=\"jobGroup\" >\n\t\t\t\t\t\t</div>\n                    </div>\n\n                    <div class=\"form-group\">\n                        <label class=\"col-sm-3 control-label\">${I18n.jobinfo_job}：</label>\n                        <div class=\"col-sm-9\">\n                            <input type=\"text\" class=\"form-control jobIdText\" readonly >\n                            <input type=\"hidden\" name=\"jobId\" >\n\t\t\t\t\t\t</div>\n                    </div>\n\n                    <div class=\"form-group\">\n                        <label class=\"col-sm-3 control-label\">${I18n.joblog_clean_type}：</label>\n                        <div class=\"col-sm-9\">\n                            <select class=\"form-control\" name=\"type\" >\n                                <option value=\"1\" >${I18n.joblog_clean_type_1}</option>\n                                <option value=\"2\" >${I18n.joblog_clean_type_2}</option>\n                                <option value=\"3\" >${I18n.joblog_clean_type_3}</option>\n                                <option value=\"4\" >${I18n.joblog_clean_type_4}</option>\n                                <option value=\"5\" >${I18n.joblog_clean_type_5}</option>\n                                <option value=\"6\" >${I18n.joblog_clean_type_6}</option>\n                                <option value=\"7\" >${I18n.joblog_clean_type_7}</option>\n                                <option value=\"8\" >${I18n.joblog_clean_type_8}</option>\n                                <option value=\"9\" >${I18n.joblog_clean_type_9}</option>\n                            </select>\n                        </div>\n                    </div>\n\n                    <hr>\n                    <div class=\"form-group\">\n                        <div class=\"col-sm-offset-3 col-sm-6\">\n                            <button type=\"button\" class=\"btn btn-primary ok\" >${I18n.system_ok}</button>\n                            <button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">${I18n.system_cancel}</button>\n                        </div>\n                    </div>\n                </form>\n            </div>\n        </div>\n    </div>\n</div>\n\n<@netCommon.commonScript />\n<!-- DataTables -->\n<script src=\"${request.contextPath}/static/adminlte/bower_components/datatables.net/js/jquery.dataTables.min.js\"></script>\n<script src=\"${request.contextPath}/static/adminlte/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js\"></script>\n<!-- daterangepicker -->\n<script src=\"${request.contextPath}/static/adminlte/bower_components/moment/moment.min.js\"></script>\n<script src=\"${request.contextPath}/static/adminlte/bower_components/bootstrap-daterangepicker/daterangepicker.js\"></script>\n<script src=\"${request.contextPath}/static/js/joblog.index.1.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/login.ftl",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  \t<#import \"./common/common.macro.ftl\" as netCommon>\n\t<@netCommon.commonStyle />\n    <link rel=\"stylesheet\" href=\"${request.contextPath}/static/adminlte/plugins/iCheck/square/blue.css\">\n\t<title>${I18n.admin_name}</title>\n</head>\n<body class=\"hold-transition login-page\">\n\t<div class=\"login-box\">\n\t\t<div class=\"login-logo\">\n\t\t\t<a><b>XXL</b>JOB</a>\n\t\t</div>\n\t\t<form id=\"loginForm\" method=\"post\" >\n\t\t\t<div class=\"login-box-body\">\n\t\t\t\t<p class=\"login-box-msg\">${I18n.admin_name}</p>\n\t\t\t\t<div class=\"form-group has-feedback\">\n\t            \t<input type=\"text\" name=\"userName\" class=\"form-control\" placeholder=\"${I18n.login_username_placeholder}\"  maxlength=\"18\" >\n\t            \t<span class=\"glyphicon glyphicon-envelope form-control-feedback\"></span>\n\t\t\t\t</div>\n\t          \t<div class=\"form-group has-feedback\">\n\t            \t<input type=\"password\" name=\"password\" class=\"form-control\" placeholder=\"${I18n.login_password_placeholder}\"  maxlength=\"18\" >\n\t            \t<span class=\"glyphicon glyphicon-lock form-control-feedback\"></span>\n\t          \t</div>\n\t\t\t\t<div class=\"row\">\n\t\t\t\t\t<div class=\"col-xs-8\">\n\t\t              \t<div class=\"checkbox icheck\">\n\t\t                \t<label>\n\t\t                  \t\t<input type=\"checkbox\" name=\"ifRemember\" > &nbsp; ${I18n.login_remember_me}\n\t\t                \t</label>\n\t\t\t\t\t\t</div>\n\t\t            </div><!-- /.col -->\n\t\t            <div class=\"col-xs-4\">\n\t\t\t\t\t\t<button type=\"submit\" class=\"btn btn-primary btn-block btn-flat\">${I18n.login_btn}</button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</form>\n\t</div>\n<@netCommon.commonScript />\n<script src=\"${request.contextPath}/static/adminlte/plugins/iCheck/icheck.min.js\"></script>\n<script src=\"${request.contextPath}/static/js/login.1.js\"></script>\n\n</body>\n</html>\n"
  },
  {
    "path": "ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/user/user.index.ftl",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  \t<#import \"../common/common.macro.ftl\" as netCommon>\n\t<@netCommon.commonStyle />\n\t<!-- DataTables -->\n  \t<link rel=\"stylesheet\" href=\"${request.contextPath}/static/adminlte/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css\">\n    <title>${I18n.admin_name}</title>\n</head>\n<body class=\"hold-transition skin-blue sidebar-mini <#if cookieMap?exists && cookieMap[\"xxljob_adminlte_settings\"]?exists && \"off\" == cookieMap[\"xxljob_adminlte_settings\"].value >sidebar-collapse</#if>\">\n<div class=\"wrapper\">\n\t<!-- header -->\n\t<@netCommon.commonHeader />\n\t<!-- left -->\n\t<@netCommon.commonLeft \"user\" />\n\t\n\t<!-- Content Wrapper. Contains page content -->\n\t<div class=\"content-wrapper\">\n\t\t<!-- Content Header (Page header) -->\n\t\t<section class=\"content-header\">\n\t\t\t<h1>${I18n.user_manage}</h1>\n\t\t</section>\n\t\t\n\t\t<!-- Main content -->\n\t    <section class=\"content\">\n\t    \n\t    \t<div class=\"row\">\n                <div class=\"col-xs-3\">\n                    <div class=\"input-group\">\n                        <span class=\"input-group-addon\">${I18n.user_role}</span>\n                        <select class=\"form-control\" id=\"role\" >\n                            <option value=\"-1\" >${I18n.system_all}</option>\n                            <option value=\"1\" >${I18n.user_role_admin}</option>\n                            <option value=\"0\" >${I18n.user_role_normal}</option>\n                        </select>\n                    </div>\n                </div>\n                <div class=\"col-xs-3\">\n                    <div class=\"input-group\">\n                        <span class=\"input-group-addon\">${I18n.user_username}</span>\n                        <input type=\"text\" class=\"form-control\" id=\"username\" autocomplete=\"on\" >\n                    </div>\n                </div>\n\t            <div class=\"col-xs-1\">\n\t            \t<button class=\"btn btn-block btn-info\" id=\"searchBtn\">${I18n.system_search}</button>\n\t            </div>\n\t            <div class=\"col-xs-2\">\n\t            \t<button class=\"btn btn-block btn-success add\" type=\"button\">${I18n.user_add}</button>\n\t            </div>\n          \t</div>\n\t    \t\n\t\t\t<div class=\"row\">\n\t\t\t\t<div class=\"col-xs-12\">\n\t\t\t\t\t<div class=\"box\">\n\t\t\t            <div class=\"box-body\" >\n\t\t\t              \t<table id=\"user_list\" class=\"table table-bordered table-striped\" width=\"100%\" >\n\t\t\t\t                <thead>\n\t\t\t\t\t            \t<tr>\n                                        <th name=\"id\" >ID</th>\n                                        <th name=\"username\" >${I18n.user_username}</th>\n\t\t\t\t\t                  \t<th name=\"password\" >${I18n.user_password}</th>\n                                        <th name=\"role\" >${I18n.user_role}</th>\n\t\t\t\t\t                  \t<th name=\"permission\" >${I18n.user_permission}</th>\n\t\t\t\t\t                  \t<th>${I18n.system_opt}</th>\n\t\t\t\t\t                </tr>\n\t\t\t\t                </thead>\n\t\t\t\t                <tbody></tbody>\n\t\t\t\t                <tfoot></tfoot>\n\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t    </section>\n\t</div>\n\t\n\t<!-- footer -->\n\t<@netCommon.commonFooter />\n</div>\n\n<!-- 新增.模态框 -->\n<div class=\"modal fade\" id=\"addModal\" tabindex=\"-1\" role=\"dialog\"  aria-hidden=\"true\">\n\t<div class=\"modal-dialog\">\n\t\t<div class=\"modal-content\">\n\t\t\t<div class=\"modal-header\">\n            \t<h4 class=\"modal-title\" >${I18n.user_add}</h4>\n         \t</div>\n         \t<div class=\"modal-body\">\n\t\t\t\t<form class=\"form-horizontal form\" role=\"form\" >\n                    <div class=\"form-group\">\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.user_username}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-8\"><input type=\"text\" class=\"form-control\" name=\"username\" placeholder=\"${I18n.system_please_input}${I18n.user_username}\" maxlength=\"20\" ></div>\n                    </div>\n                    <div class=\"form-group\">\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.user_password}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-8\"><input type=\"text\" class=\"form-control\" name=\"password\" placeholder=\"${I18n.system_please_input}${I18n.user_password}\" maxlength=\"20\" ></div>\n                    </div>\n                    <div class=\"form-group\">\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.user_role}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-10\">\n                            <input type=\"radio\" name=\"role\" value=\"0\" checked />${I18n.user_role_normal}\n                            &nbsp;&nbsp;&nbsp;&nbsp;\n                            <input type=\"radio\" name=\"role\" value=\"1\" />${I18n.user_role_admin}\n                        </div>\n                    </div>\n                    <div class=\"form-group\">\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.user_permission}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-10\">\n\t\t\t\t\t\t\t<#if groupList?exists && groupList?size gt 0>\n\t\t\t\t\t\t\t\t<#list groupList as item>\n                                    <input type=\"checkbox\" name=\"permission\" value=\"${item.id}\" />${item.title}(${item.appname})<br>\n\t\t\t\t\t\t\t\t</#list>\n\t\t\t\t\t\t\t</#if>\n                        </div>\n                    </div>\n\n                    <hr>\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<div class=\"col-sm-offset-3 col-sm-6\">\n\t\t\t\t\t\t\t<button type=\"submit\" class=\"btn btn-primary\"  >${I18n.system_save}</button>\n\t\t\t\t\t\t\t<button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">${I18n.system_cancel}</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t</form>\n         \t</div>\n\t\t</div>\n\t</div>\n</div>\n\n<!-- 更新.模态框 -->\n<div class=\"modal fade\" id=\"updateModal\" tabindex=\"-1\" role=\"dialog\"  aria-hidden=\"true\">\n\t<div class=\"modal-dialog\">\n\t\t<div class=\"modal-content\">\n\t\t\t<div class=\"modal-header\">\n            \t<h4 class=\"modal-title\" >${I18n.user_update}</h4>\n         \t</div>\n         \t<div class=\"modal-body\">\n\t\t\t\t<form class=\"form-horizontal form\" role=\"form\" >\n                    <div class=\"form-group\">\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.user_username}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-8\"><input type=\"text\" class=\"form-control\" name=\"username\" placeholder=\"${I18n.system_please_input}${I18n.user_username}\" maxlength=\"20\" readonly ></div>\n                    </div>\n                    <div class=\"form-group\">\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.user_password}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-8\"><input type=\"text\" class=\"form-control\" name=\"password\" placeholder=\"${I18n.user_password_update_placeholder}\" maxlength=\"20\" ></div>\n                    </div>\n                    <div class=\"form-group\">\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.user_role}<font color=\"red\">*</font></label>\n                        <div class=\"col-sm-10\">\n                            <input type=\"radio\" name=\"role\" value=\"0\" />${I18n.user_role_normal}\n                            &nbsp;&nbsp;&nbsp;&nbsp;\n                            <input type=\"radio\" name=\"role\" value=\"1\" />${I18n.user_role_admin}\n                        </div>\n                    </div>\n                    <div class=\"form-group\">\n                        <label for=\"lastname\" class=\"col-sm-2 control-label\">${I18n.user_permission}<font color=\"black\">*</font></label>\n                        <div class=\"col-sm-10\">\n\t\t\t\t\t\t<#if groupList?exists && groupList?size gt 0>\n\t\t\t\t\t\t\t<#list groupList as item>\n                                <input type=\"checkbox\" name=\"permission\" value=\"${item.id}\" />${item.title}(${item.appname})<br>\n\t\t\t\t\t\t\t</#list>\n\t\t\t\t\t\t</#if>\n                        </div>\n                    </div>\n\n\t\t\t\t\t<hr>\n\t\t\t\t\t<div class=\"form-group\">\n                        <div class=\"col-sm-offset-3 col-sm-6\">\n\t\t\t\t\t\t\t<button type=\"submit\" class=\"btn btn-primary\"  >${I18n.system_save}</button>\n\t\t\t\t\t\t\t<button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">${I18n.system_cancel}</button>\n                            <input type=\"hidden\" name=\"id\" >\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t</form>\n         \t</div>\n\t\t</div>\n\t</div>\n</div>\n\n<@netCommon.commonScript />\n<!-- DataTables -->\n<script src=\"${request.contextPath}/static/adminlte/bower_components/datatables.net/js/jquery.dataTables.min.js\"></script>\n<script src=\"${request.contextPath}/static/adminlte/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js\"></script>\n<script src=\"${request.contextPath}/static/js/user.index.1.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "ruoyi-framework/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>ruoyi-vue-plus</artifactId>\n        <groupId>com.ruoyi</groupId>\n        <version>4.7.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ruoyi-framework</artifactId>\n\n    <description>\n        framework框架核心\n    </description>\n\n    <dependencies>\n\n        <!-- SpringBoot Web容器 -->\n         <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n             <exclusions>\n                 <exclusion>\n                     <artifactId>spring-boot-starter-tomcat</artifactId>\n                     <groupId>org.springframework.boot</groupId>\n                 </exclusion>\n             </exclusions>\n        </dependency>\n        <!-- web 容器使用 undertow 性能更强 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-undertow</artifactId>\n        </dependency>\n\n        <!-- SpringBoot 拦截器 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n        </dependency>\n\n        <!-- sql性能分析插件 -->\n        <dependency>\n            <groupId>p6spy</groupId>\n            <artifactId>p6spy</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-client</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>transmittable-thread-local</artifactId>\n        </dependency>\n\n        <!-- 系统模块-->\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>ruoyi-common</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/aspectj/LogAspect.java",
    "content": "package top.flya.framework.aspectj;\n\nimport cn.hutool.core.lang.Dict;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.domain.event.OperLogEvent;\nimport top.flya.common.enums.BusinessStatus;\nimport top.flya.common.enums.HttpMethod;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.common.utils.ServletUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.annotation.AfterReturning;\nimport org.aspectj.lang.annotation.AfterThrowing;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.springframework.stereotype.Component;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * 操作日志记录处理\n *\n * @author Lion Li\n */\n@Slf4j\n@Aspect\n@Component\npublic class LogAspect {\n\n    /**\n     * 排除敏感属性字段\n     */\n    public static final String[] EXCLUDE_PROPERTIES = { \"password\", \"oldPassword\", \"newPassword\", \"confirmPassword\" };\n\n    /**\n     * 处理完请求后执行\n     *\n     * @param joinPoint 切点\n     */\n    @AfterReturning(pointcut = \"@annotation(controllerLog)\", returning = \"jsonResult\")\n    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {\n        handleLog(joinPoint, controllerLog, null, jsonResult);\n    }\n\n    /**\n     * 拦截异常操作\n     *\n     * @param joinPoint 切点\n     * @param e         异常\n     */\n    @AfterThrowing(value = \"@annotation(controllerLog)\", throwing = \"e\")\n    public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {\n        handleLog(joinPoint, controllerLog, e, null);\n    }\n\n    protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {\n        try {\n\n            // *========数据库日志=========*//\n            OperLogEvent operLog = new OperLogEvent();\n            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());\n            // 请求的地址\n            String ip = ServletUtils.getClientIP();\n            operLog.setOperIp(ip);\n            operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));\n            operLog.setOperName(LoginHelper.getUsername());\n\n            if (e != null) {\n                operLog.setStatus(BusinessStatus.FAIL.ordinal());\n                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));\n            }\n            // 设置方法名称\n            String className = joinPoint.getTarget().getClass().getName();\n            String methodName = joinPoint.getSignature().getName();\n            operLog.setMethod(className + \".\" + methodName + \"()\");\n            // 设置请求方式\n            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());\n            // 处理设置注解上的参数\n            getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);\n            // 发布事件保存数据库\n            SpringUtils.context().publishEvent(operLog);\n        } catch (Exception exp) {\n            // 记录本地异常日志\n            log.error(\"异常信息:{}\", exp.getMessage());\n            exp.printStackTrace();\n        }\n    }\n\n    /**\n     * 获取注解中对方法的描述信息 用于Controller层注解\n     *\n     * @param log     日志\n     * @param operLog 操作日志\n     * @throws Exception\n     */\n    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogEvent operLog, Object jsonResult) throws Exception {\n        // 设置action动作\n        operLog.setBusinessType(log.businessType().ordinal());\n        // 设置标题\n        operLog.setTitle(log.title());\n        // 设置操作人类别\n        operLog.setOperatorType(log.operatorType().ordinal());\n        // 是否需要保存request，参数和值\n        if (log.isSaveRequestData()) {\n            // 获取参数的信息，传入到数据库中。\n            setRequestValue(joinPoint, operLog, log.excludeParamNames());\n        }\n        // 是否需要保存response，参数和值\n        if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) {\n            operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 2000));\n        }\n    }\n\n    /**\n     * 获取请求的参数，放到log中\n     *\n     * @param operLog 操作日志\n     * @throws Exception 异常\n     */\n    private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog, String[] excludeParamNames) throws Exception {\n        Map<String, String> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());\n        String requestMethod = operLog.getRequestMethod();\n        if (MapUtil.isEmpty(paramsMap)\n            && HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {\n            String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);\n            operLog.setOperParam(StringUtils.substring(params, 0, 2000));\n        } else {\n            MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES);\n            MapUtil.removeAny(paramsMap, excludeParamNames);\n            operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 2000));\n        }\n    }\n\n    /**\n     * 参数拼装\n     */\n    private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) {\n        StringBuilder params = new StringBuilder();\n        if (paramsArray != null && paramsArray.length > 0) {\n            for (Object o : paramsArray) {\n                if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {\n                    try {\n                        String str = JsonUtils.toJsonString(o);\n                        Dict dict = JsonUtils.parseMap(str);\n                        if (MapUtil.isNotEmpty(dict)) {\n                            MapUtil.removeAny(dict, EXCLUDE_PROPERTIES);\n                            MapUtil.removeAny(dict, excludeParamNames);\n                            str = JsonUtils.toJsonString(dict);\n                        }\n                        params.append(str).append(\" \");\n                    } catch (Exception e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n        }\n        return params.toString().trim();\n    }\n\n    /**\n     * 判断是否需要过滤的对象。\n     *\n     * @param o 对象信息。\n     * @return 如果是需要过滤的对象，则返回true；否则返回false。\n     */\n    @SuppressWarnings(\"rawtypes\")\n    public boolean isFilterObject(final Object o) {\n        Class<?> clazz = o.getClass();\n        if (clazz.isArray()) {\n            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);\n        } else if (Collection.class.isAssignableFrom(clazz)) {\n            Collection collection = (Collection) o;\n            for (Object value : collection) {\n                return value instanceof MultipartFile;\n            }\n        } else if (Map.class.isAssignableFrom(clazz)) {\n            Map map = (Map) o;\n            for (Object value : map.entrySet()) {\n                Map.Entry entry = (Map.Entry) value;\n                return entry.getValue() instanceof MultipartFile;\n            }\n        }\n        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse\n            || o instanceof BindingResult;\n    }\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/aspectj/RateLimiterAspect.java",
    "content": "package top.flya.framework.aspectj;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport top.flya.common.annotation.RateLimiter;\nimport top.flya.common.constant.CacheConstants;\nimport top.flya.common.enums.LimitType;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.utils.MessageUtils;\nimport top.flya.common.utils.ServletUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.redis.RedisUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\nimport org.aspectj.lang.reflect.MethodSignature;\nimport org.redisson.api.RateType;\nimport org.springframework.core.DefaultParameterNameDiscoverer;\nimport org.springframework.core.ParameterNameDiscoverer;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.ParserContext;\nimport org.springframework.expression.common.TemplateParserContext;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.stereotype.Component;\n\nimport java.lang.reflect.Method;\n\n/**\n * 限流处理\n *\n * @author Lion Li\n */\n@Slf4j\n@Aspect\n@Component\npublic class RateLimiterAspect {\n\n    /**\n     * 定义spel表达式解析器\n     */\n    private final ExpressionParser parser = new SpelExpressionParser();\n    /**\n     * 定义spel解析模版\n     */\n    private final ParserContext parserContext = new TemplateParserContext();\n    /**\n     * 定义spel上下文对象进行解析\n     */\n    private final EvaluationContext context = new StandardEvaluationContext();\n    /**\n     * 方法参数解析器\n     */\n    private final ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();\n\n    @Before(\"@annotation(rateLimiter)\")\n    public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable {\n        int time = rateLimiter.time();\n        int count = rateLimiter.count();\n        String combineKey = getCombineKey(rateLimiter, point);\n        try {\n            RateType rateType = RateType.OVERALL;\n            if (rateLimiter.limitType() == LimitType.CLUSTER) {\n                rateType = RateType.PER_CLIENT;\n            }\n            long number = RedisUtils.rateLimiter(combineKey, rateType, count, time);\n            if (number == -1) {\n                String message = rateLimiter.message();\n                if (StringUtils.startsWith(message, \"{\") && StringUtils.endsWith(message, \"}\")) {\n                    message = MessageUtils.message(StringUtils.substring(message, 1, message.length() - 1));\n                }\n                throw new ServiceException(message);\n            }\n            log.info(\"限制令牌 => {}, 剩余令牌 => {}, 缓存key => '{}'\", count, number, combineKey);\n        } catch (Exception e) {\n            if (e instanceof ServiceException) {\n                throw e;\n            } else {\n                throw new RuntimeException(\"服务器限流异常，请稍候再试\");\n            }\n        }\n    }\n\n    public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) {\n        String key = rateLimiter.key();\n        // 获取方法(通过方法签名来获取)\n        MethodSignature signature = (MethodSignature) point.getSignature();\n        Method method = signature.getMethod();\n        Class<?> targetClass = method.getDeclaringClass();\n        // 判断是否是spel格式\n        if (StringUtils.containsAny(key, \"#\")) {\n            // 获取参数值\n            Object[] args = point.getArgs();\n            // 获取方法上参数的名称\n            String[] parameterNames = pnd.getParameterNames(method);\n            if (ArrayUtil.isEmpty(parameterNames)) {\n                throw new ServiceException(\"限流key解析异常!请联系管理员!\");\n            }\n            for (int i = 0; i < parameterNames.length; i++) {\n                context.setVariable(parameterNames[i], args[i]);\n            }\n            // 解析返回给key\n            try {\n                Expression expression;\n                if (StringUtils.startsWith(key, parserContext.getExpressionPrefix())\n                    && StringUtils.endsWith(key, parserContext.getExpressionSuffix())) {\n                    expression = parser.parseExpression(key, parserContext);\n                } else {\n                    expression = parser.parseExpression(key);\n                }\n                key = expression.getValue(context, String.class) + \":\";\n            } catch (Exception e) {\n                throw new ServiceException(\"限流key解析异常!请联系管理员!\");\n            }\n        }\n        StringBuilder stringBuffer = new StringBuilder(CacheConstants.RATE_LIMIT_KEY);\n        stringBuffer.append(ServletUtils.getRequest().getRequestURI()).append(\":\");\n        if (rateLimiter.limitType() == LimitType.IP) {\n            // 获取请求ip\n            stringBuffer.append(ServletUtils.getClientIP()).append(\":\");\n        } else if (rateLimiter.limitType() == LimitType.CLUSTER) {\n            // 获取客户端实例id\n            stringBuffer.append(RedisUtils.getClient().getId()).append(\":\");\n        }\n        return stringBuffer.append(key).toString();\n    }\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/aspectj/RepeatSubmitAspect.java",
    "content": "package top.flya.framework.aspectj;\n\nimport cn.dev33.satoken.SaManager;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.crypto.SecureUtil;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.constant.CacheConstants;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.common.utils.MessageUtils;\nimport top.flya.common.utils.ServletUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.redis.RedisUtils;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.annotation.AfterReturning;\nimport org.aspectj.lang.annotation.AfterThrowing;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\nimport org.springframework.stereotype.Component;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.time.Duration;\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * 防止重复提交(参考美团GTIS防重系统)\n *\n * @author Lion Li\n */\n@Slf4j\n@RequiredArgsConstructor\n@Aspect\n@Component\npublic class RepeatSubmitAspect {\n\n    private static final ThreadLocal<String> KEY_CACHE = new ThreadLocal<>();\n\n    @Before(\"@annotation(repeatSubmit)\")\n    public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable {\n        // 如果注解不为0 则使用注解数值\n        long interval = 0;\n        if (repeatSubmit.interval() > 0) {\n            interval = repeatSubmit.timeUnit().toMillis(repeatSubmit.interval());\n        }\n        if (interval < 1000) {\n            throw new ServiceException(\"重复提交间隔时间不能小于'1'秒\");\n        }\n        HttpServletRequest request = ServletUtils.getRequest();\n        String nowParams = argsArrayToString(point.getArgs());\n\n        // 请求地址（作为存放cache的key值）\n        String url = request.getRequestURI();\n\n        // 唯一值（没有消息头则使用请求地址）\n        String submitKey = StringUtils.trimToEmpty(request.getHeader(SaManager.getConfig().getTokenName()));\n\n        submitKey = SecureUtil.md5(submitKey + \":\" + nowParams);\n        // 唯一标识（指定key + url + 消息头）\n        String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey;\n        String key = RedisUtils.getCacheObject(cacheRepeatKey);\n        if (key == null) {\n            RedisUtils.setCacheObject(cacheRepeatKey, \"\", Duration.ofMillis(interval));\n            KEY_CACHE.set(cacheRepeatKey);\n        } else {\n            String message = repeatSubmit.message();\n            if (StringUtils.startsWith(message, \"{\") && StringUtils.endsWith(message, \"}\")) {\n                message = MessageUtils.message(StringUtils.substring(message, 1, message.length() - 1));\n            }\n            throw new ServiceException(message);\n        }\n    }\n\n    /**\n     * 处理完请求后执行\n     *\n     * @param joinPoint 切点\n     */\n    @AfterReturning(pointcut = \"@annotation(repeatSubmit)\", returning = \"jsonResult\")\n    public void doAfterReturning(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Object jsonResult) {\n        if (jsonResult instanceof R) {\n            try {\n                R<?> r = (R<?>) jsonResult;\n                // 成功则不删除redis数据 保证在有效时间内无法重复提交\n                if (r.getCode() == R.SUCCESS) {\n                    return;\n                }\n                RedisUtils.deleteObject(KEY_CACHE.get());\n            } finally {\n                KEY_CACHE.remove();\n            }\n        }\n    }\n\n    /**\n     * 拦截异常操作\n     *\n     * @param joinPoint 切点\n     * @param e         异常\n     */\n    @AfterThrowing(value = \"@annotation(repeatSubmit)\", throwing = \"e\")\n    public void doAfterThrowing(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Exception e) {\n        RedisUtils.deleteObject(KEY_CACHE.get());\n        KEY_CACHE.remove();\n    }\n\n    /**\n     * 参数拼装\n     */\n    private String argsArrayToString(Object[] paramsArray) {\n        StringBuilder params = new StringBuilder();\n        if (paramsArray != null && paramsArray.length > 0) {\n            for (Object o : paramsArray) {\n                if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {\n                    try {\n                        params.append(JsonUtils.toJsonString(o)).append(\" \");\n                    } catch (Exception e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n        }\n        return params.toString().trim();\n    }\n\n    /**\n     * 判断是否需要过滤的对象。\n     *\n     * @param o 对象信息。\n     * @return 如果是需要过滤的对象，则返回true；否则返回false。\n     */\n    @SuppressWarnings(\"rawtypes\")\n    public boolean isFilterObject(final Object o) {\n        Class<?> clazz = o.getClass();\n        if (clazz.isArray()) {\n            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);\n        } else if (Collection.class.isAssignableFrom(clazz)) {\n            Collection collection = (Collection) o;\n            for (Object value : collection) {\n                return value instanceof MultipartFile;\n            }\n        } else if (Map.class.isAssignableFrom(clazz)) {\n            Map map = (Map) o;\n            for (Object value : map.entrySet()) {\n                Map.Entry entry = (Map.Entry) value;\n                return entry.getValue() instanceof MultipartFile;\n            }\n        }\n        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse\n            || o instanceof BindingResult;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/ApplicationConfig.java",
    "content": "package top.flya.framework.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\n\n/**\n * 程序注解配置\n *\n * @author Lion Li\n */\n@Configuration\n// 表示通过aop框架暴露该代理对象,AopContext能够访问\n@EnableAspectJAutoProxy(exposeProxy = true)\npublic class ApplicationConfig {\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/AsyncConfig.java",
    "content": "package top.flya.framework.config;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport top.flya.common.exception.ServiceException;\nimport org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.scheduling.annotation.AsyncConfigurerSupport;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\nimport java.util.Arrays;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.ScheduledExecutorService;\n\n/**\n * 异步配置\n *\n * @author Lion Li\n */\n@EnableAsync(proxyTargetClass = true)\n@Configuration\npublic class AsyncConfig extends AsyncConfigurerSupport {\n\n    @Autowired\n    @Qualifier(\"scheduledExecutorService\")\n    private ScheduledExecutorService scheduledExecutorService;\n\n    /**\n     * 自定义 @Async 注解使用系统线程池\n     */\n    @Override\n    public Executor getAsyncExecutor() {\n        return scheduledExecutorService;\n    }\n\n    /**\n     * 异步执行异常处理\n     */\n    @Override\n    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {\n        return (throwable, method, objects) -> {\n            throwable.printStackTrace();\n            StringBuilder sb = new StringBuilder();\n            sb.append(\"Exception message - \").append(throwable.getMessage())\n                .append(\", Method name - \").append(method.getName());\n            if (ArrayUtil.isNotEmpty(objects)) {\n                sb.append(\", Parameter value - \").append(Arrays.toString(objects));\n            }\n            throw new ServiceException(sb.toString());\n        };\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/CaptchaConfig.java",
    "content": "package top.flya.framework.config;\n\nimport cn.hutool.captcha.CaptchaUtil;\nimport cn.hutool.captcha.CircleCaptcha;\nimport cn.hutool.captcha.LineCaptcha;\nimport cn.hutool.captcha.ShearCaptcha;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Lazy;\n\nimport java.awt.*;\n\n/**\n * 验证码配置\n *\n * @author Lion Li\n */\n@Configuration\npublic class CaptchaConfig {\n\n    private static final int WIDTH = 160;\n    private static final int HEIGHT = 60;\n    private static final Color BACKGROUND = Color.PINK;\n    private static final Font FONT = new Font(\"Arial\", Font.BOLD, 48);\n\n    /**\n     * 圆圈干扰验证码\n     */\n    @Lazy\n    @Bean\n    public CircleCaptcha circleCaptcha() {\n        CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(WIDTH, HEIGHT);\n        captcha.setBackground(BACKGROUND);\n        captcha.setFont(FONT);\n        return captcha;\n    }\n\n    /**\n     * 线段干扰的验证码\n     */\n    @Lazy\n    @Bean\n    public LineCaptcha lineCaptcha() {\n        LineCaptcha captcha = CaptchaUtil.createLineCaptcha(WIDTH, HEIGHT);\n        captcha.setBackground(BACKGROUND);\n        captcha.setFont(FONT);\n        return captcha;\n    }\n\n    /**\n     * 扭曲干扰验证码\n     */\n    @Lazy\n    @Bean\n    public ShearCaptcha shearCaptcha() {\n        ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(WIDTH, HEIGHT);\n        captcha.setBackground(BACKGROUND);\n        captcha.setFont(FONT);\n        return captcha;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/EncryptorConfig.java",
    "content": "package top.flya.framework.config;\n\nimport top.flya.framework.config.properties.EncryptorProperties;\nimport top.flya.framework.manager.EncryptorManager;\nimport top.flya.framework.encrypt.MybatisDecryptInterceptor;\nimport top.flya.framework.encrypt.MybatisEncryptInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * 加解密配置\n *\n * @author 老马\n * @version 4.6.0\n */\n@Configuration\n@ConditionalOnProperty(value = \"mybatis-encryptor.enable\", havingValue = \"true\")\npublic class EncryptorConfig {\n\n    @Autowired\n    private EncryptorProperties properties;\n\n    @Bean\n    public EncryptorManager encryptorManager() {\n        return new EncryptorManager();\n    }\n\n    @Bean\n    public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptorManager encryptorManager) {\n        return new MybatisEncryptInterceptor(encryptorManager, properties);\n    }\n\n    @Bean\n    public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) {\n        return new MybatisDecryptInterceptor(encryptorManager, properties);\n    }\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/FilterConfig.java",
    "content": "package top.flya.framework.config;\n\nimport top.flya.common.filter.RepeatableFilter;\nimport top.flya.common.filter.XssFilter;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.framework.config.properties.XssProperties;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport javax.servlet.DispatcherType;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Filter配置\n *\n * @author Lion Li\n */\n@Configuration\npublic class FilterConfig {\n\n    @Autowired\n    private XssProperties xssProperties;\n\n    @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n    @Bean\n    @ConditionalOnProperty(value = \"xss.enabled\", havingValue = \"true\")\n    public FilterRegistrationBean xssFilterRegistration() {\n        FilterRegistrationBean registration = new FilterRegistrationBean();\n        registration.setDispatcherTypes(DispatcherType.REQUEST);\n        registration.setFilter(new XssFilter());\n        registration.addUrlPatterns(StringUtils.split(xssProperties.getUrlPatterns(), StringUtils.SEPARATOR));\n        registration.setName(\"xssFilter\");\n        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);\n        Map<String, String> initParameters = new HashMap<String, String>();\n        initParameters.put(\"excludes\", xssProperties.getExcludes());\n        registration.setInitParameters(initParameters);\n        return registration;\n    }\n\n    @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n    @Bean\n    public FilterRegistrationBean someFilterRegistration() {\n        FilterRegistrationBean registration = new FilterRegistrationBean();\n        registration.setFilter(new RepeatableFilter());\n        registration.addUrlPatterns(\"/*\");\n        registration.setName(\"repeatableFilter\");\n        registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);\n        return registration;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/I18nConfig.java",
    "content": "package top.flya.framework.config;\n\nimport cn.hutool.core.util.StrUtil;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.LocaleResolver;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.Locale;\n\n/**\n * 国际化配置\n *\n * @author Lion Li\n */\n@Configuration\npublic class I18nConfig {\n\n    @Bean\n    public LocaleResolver localeResolver() {\n        return new I18nLocaleResolver();\n    }\n\n    /**\n     * 获取请求头国际化信息\n     */\n    static class I18nLocaleResolver implements LocaleResolver {\n\n        @Override\n        public Locale resolveLocale(HttpServletRequest httpServletRequest) {\n            String language = httpServletRequest.getHeader(\"content-language\");\n            Locale locale = Locale.getDefault();\n            if (StrUtil.isNotBlank(language)) {\n                String[] split = language.split(\"_\");\n                locale = new Locale(split[0], split[1]);\n            }\n            return locale;\n        }\n\n        @Override\n        public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {\n\n        }\n    }\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/JacksonConfig.java",
    "content": "package top.flya.framework.config;\n\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;\nimport com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;\nimport top.flya.framework.jackson.BigNumberSerializer;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.TimeZone;\n\n/**\n * jackson 配置\n *\n * @author Lion Li\n */\n@Slf4j\n@Configuration\npublic class JacksonConfig {\n\n    @Bean\n    public Jackson2ObjectMapperBuilderCustomizer customizer() {\n        return builder -> {\n            // 全局配置序列化返回 JSON 处理\n            JavaTimeModule javaTimeModule = new JavaTimeModule();\n            javaTimeModule.addSerializer(Long.class, BigNumberSerializer.INSTANCE);\n            javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE);\n            javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE);\n            javaTimeModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);\n            DateTimeFormatter formatter = DateTimeFormatter.ofPattern(\"yyyy-MM-dd HH:mm:ss\");\n            javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));\n            javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));\n            builder.modules(javaTimeModule);\n            builder.timeZone(TimeZone.getDefault());\n            log.info(\"初始化 jackson 配置\");\n        };\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/MailConfig.java",
    "content": "package top.flya.framework.config;\n\nimport cn.hutool.extra.mail.MailAccount;\nimport top.flya.framework.config.properties.MailProperties;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * JavaMail 配置\n *\n * @author Michelle.Chung\n */\n@Configuration\npublic class MailConfig {\n\n    @Bean\n    @ConditionalOnProperty(value = \"mail.enabled\", havingValue = \"true\")\n    public MailAccount mailAccount(MailProperties mailProperties) {\n        MailAccount account = new MailAccount();\n        account.setHost(mailProperties.getHost());\n        account.setPort(mailProperties.getPort());\n        account.setAuth(mailProperties.getAuth());\n        account.setFrom(mailProperties.getFrom());\n        account.setUser(mailProperties.getUser());\n        account.setPass(mailProperties.getPass());\n        account.setSocketFactoryPort(mailProperties.getPort());\n        account.setStarttlsEnable(mailProperties.getStarttlsEnable());\n        account.setSslEnable(mailProperties.getSslEnable());\n        account.setTimeout(mailProperties.getTimeout());\n        account.setConnectionTimeout(mailProperties.getConnectionTimeout());\n        return account;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/MybatisPlusConfig.java",
    "content": "package top.flya.framework.config;\n\nimport cn.hutool.core.net.NetUtil;\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;\nimport com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport top.flya.framework.handler.CreateAndUpdateMetaObjectHandler;\nimport top.flya.framework.interceptor.PlusDataPermissionInterceptor;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.transaction.annotation.EnableTransactionManagement;\n\n/**\n * mybatis-plus配置类(下方注释有插件介绍)\n *\n * @author Lion Li\n */\n@EnableTransactionManagement(proxyTargetClass = true)\n@Configuration\n@MapperScan(\"${mybatis-plus.mapperPackage}\")\npublic class MybatisPlusConfig {\n\n    @Bean\n    public MybatisPlusInterceptor mybatisPlusInterceptor() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        // 数据权限处理\n        interceptor.addInnerInterceptor(dataPermissionInterceptor());\n        // 分页插件\n        interceptor.addInnerInterceptor(paginationInnerInterceptor());\n        // 乐观锁插件\n        interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());\n        return interceptor;\n    }\n\n    /**\n     * 数据权限拦截器\n     */\n    public PlusDataPermissionInterceptor dataPermissionInterceptor() {\n        return new PlusDataPermissionInterceptor();\n    }\n\n    /**\n     * 分页插件，自动识别数据库类型\n     */\n    public PaginationInnerInterceptor paginationInnerInterceptor() {\n        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();\n        // 设置最大单页限制数量，默认 500 条，-1 不受限制\n        paginationInnerInterceptor.setMaxLimit(-1L);\n        // 分页合理化\n        paginationInnerInterceptor.setOverflow(true);\n        return paginationInnerInterceptor;\n    }\n\n    /**\n     * 乐观锁插件\n     */\n    public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {\n        return new OptimisticLockerInnerInterceptor();\n    }\n\n    /**\n     * 元对象字段填充控制器\n     */\n    @Bean\n    public MetaObjectHandler metaObjectHandler() {\n        return new CreateAndUpdateMetaObjectHandler();\n    }\n\n    /**\n     * 使用网卡信息绑定雪花生成器\n     * 防止集群雪花ID重复\n     */\n    @Bean\n    public IdentifierGenerator idGenerator() {\n        return new DefaultIdentifierGenerator(NetUtil.getLocalhost());\n    }\n\n    /**\n     * PaginationInnerInterceptor 分页插件，自动识别数据库类型\n     * https://baomidou.com/pages/97710a/\n     * OptimisticLockerInnerInterceptor 乐观锁插件\n     * https://baomidou.com/pages/0d93c0/\n     * MetaObjectHandler 元对象字段填充控制器\n     * https://baomidou.com/pages/4c6bcf/\n     * ISqlInjector sql注入器\n     * https://baomidou.com/pages/42ea4a/\n     * BlockAttackInnerInterceptor 如果是对全表的删除或更新操作，就会终止该操作\n     * https://baomidou.com/pages/f9a237/\n     * IllegalSQLInnerInterceptor sql性能规范插件(垃圾SQL拦截)\n     * IdentifierGenerator 自定义主键策略\n     * https://baomidou.com/pages/568eb2/\n     * TenantLineInnerInterceptor 多租户插件\n     * https://baomidou.com/pages/aef2f2/\n     * DynamicTableNameInnerInterceptor 动态表名插件\n     * https://baomidou.com/pages/2a45ff/\n     */\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/RedisConfig.java",
    "content": "package top.flya.framework.config;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.data.redis.connection.MessageListener;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.listener.PatternTopic;\nimport org.springframework.data.redis.listener.RedisMessageListenerContainer;\nimport org.springframework.data.redis.serializer.RedisSerializer;\nimport org.springframework.data.redis.serializer.StringRedisSerializer;\nimport top.flya.framework.config.properties.RedissonProperties;\nimport top.flya.framework.handler.KeyPrefixHandler;\nimport top.flya.framework.manager.PlusSpringCacheManager;\nimport lombok.extern.slf4j.Slf4j;\nimport org.redisson.codec.JsonJacksonCodec;\nimport org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.cache.CacheManager;\nimport org.springframework.cache.annotation.EnableCaching;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * redis配置\n *\n * @author Lion Li\n */\n@Slf4j\n@Configuration\n@EnableCaching\n@EnableConfigurationProperties(RedissonProperties.class)\npublic class RedisConfig {\n\n    @Autowired\n    private RedissonProperties redissonProperties;\n\n    @Autowired\n    private ObjectMapper objectMapper;\n\n    @Bean\n    public RedissonAutoConfigurationCustomizer redissonCustomizer() {\n        return config -> {\n            config.setThreads(redissonProperties.getThreads())\n                .setNettyThreads(redissonProperties.getNettyThreads())\n                .setCodec(new JsonJacksonCodec(objectMapper));\n            RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();\n            if (ObjectUtil.isNotNull(singleServerConfig)) {\n                // 使用单机模式\n                config.useSingleServer()\n                    //设置redis key前缀\n                    .setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix()))\n                    .setTimeout(singleServerConfig.getTimeout())\n                    .setClientName(singleServerConfig.getClientName())\n                    .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout())\n                    .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())\n                    .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())\n                    .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize());\n            }\n            // 集群配置方式 参考下方注释\n            RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig();\n            if (ObjectUtil.isNotNull(clusterServersConfig)) {\n                config.useClusterServers()\n                    //设置redis key前缀\n                    .setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix()))\n                    .setTimeout(clusterServersConfig.getTimeout())\n                    .setClientName(clusterServersConfig.getClientName())\n                    .setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout())\n                    .setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize())\n                    .setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize())\n                    .setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize())\n                    .setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize())\n                    .setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize())\n                    .setReadMode(clusterServersConfig.getReadMode())\n                    .setSubscriptionMode(clusterServersConfig.getSubscriptionMode());\n            }\n            log.info(\"初始化 redis 配置\");\n        };\n    }\n\n    /**\n     * 自定义缓存管理器 整合spring-cache\n     */\n    @Bean\n    public CacheManager cacheManager() {\n        return new PlusSpringCacheManager();\n    }\n\n    /**\n     * redis集群配置 yml\n     *\n     * --- # redis 集群配置(单机与集群只能开启一个另一个需要注释掉)\n     * spring:\n     *   redis:\n     *     cluster:\n     *       nodes:\n     *         - 192.168.0.100:6379\n     *         - 192.168.0.101:6379\n     *         - 192.168.0.102:6379\n     *     # 密码\n     *     password:\n     *     # 连接超时时间\n     *     timeout: 10s\n     *     # 是否开启ssl\n     *     ssl: false\n     *\n     * redisson:\n     *   # 线程池数量\n     *   threads: 16\n     *   # Netty线程池数量\n     *   nettyThreads: 32\n     *   # 集群配置\n     *   clusterServersConfig:\n     *     # 客户端名称\n     *     clientName: ${ruoyi.name}\n     *     # master最小空闲连接数\n     *     masterConnectionMinimumIdleSize: 32\n     *     # master连接池大小\n     *     masterConnectionPoolSize: 64\n     *     # slave最小空闲连接数\n     *     slaveConnectionMinimumIdleSize: 32\n     *     # slave连接池大小\n     *     slaveConnectionPoolSize: 64\n     *     # 连接空闲超时，单位：毫秒\n     *     idleConnectionTimeout: 10000\n     *     # 命令等待超时，单位：毫秒\n     *     timeout: 3000\n     *     # 发布和订阅连接池大小\n     *     subscriptionConnectionPoolSize: 50\n     *     # 读取模式\n     *     readMode: \"SLAVE\"\n     *     # 订阅模式\n     *     subscriptionMode: \"MASTER\"\n     */\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/ResourcesConfig.java",
    "content": "package top.flya.framework.config;\n\nimport top.flya.framework.interceptor.PlusWebInvokeTimeInterceptor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.cors.CorsConfiguration;\nimport org.springframework.web.cors.UrlBasedCorsConfigurationSource;\nimport org.springframework.web.filter.CorsFilter;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n/**\n * 通用配置\n *\n * @author Lion Li\n */\n@Configuration\npublic class ResourcesConfig implements WebMvcConfigurer {\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        // 全局访问性能拦截\n        registry.addInterceptor(new PlusWebInvokeTimeInterceptor());\n    }\n\n    @Override\n    public void addResourceHandlers(ResourceHandlerRegistry registry) {\n    }\n\n    /**\n     * 跨域配置\n     */\n    @Bean\n    public CorsFilter corsFilter() {\n        CorsConfiguration config = new CorsConfiguration();\n        config.setAllowCredentials(true);\n        // 设置访问源地址\n        config.addAllowedOriginPattern(\"*\");\n        // 设置访问源请求头\n        config.addAllowedHeader(\"*\");\n        // 设置访问源请求方法\n        config.addAllowedMethod(\"*\");\n        // 有效期 1800秒\n        config.setMaxAge(1800L);\n        // 添加映射路径，拦截一切请求\n        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n        source.registerCorsConfiguration(\"/**\", config);\n        // 返回新的CorsFilter\n        return new CorsFilter(source);\n    }\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/SaTokenConfig.java",
    "content": "package top.flya.framework.config;\n\nimport cn.dev33.satoken.dao.SaTokenDao;\nimport cn.dev33.satoken.interceptor.SaInterceptor;\nimport cn.dev33.satoken.jwt.StpLogicJwtForSimple;\nimport cn.dev33.satoken.router.SaRouter;\nimport cn.dev33.satoken.stp.StpInterface;\nimport cn.dev33.satoken.stp.StpLogic;\nimport cn.dev33.satoken.stp.StpUtil;\nimport top.flya.common.utils.spring.SpringUtils;\nimport top.flya.framework.config.properties.SecurityProperties;\nimport top.flya.framework.handler.AllUrlHandler;\nimport top.flya.framework.satoken.dao.PlusSaTokenDao;\nimport top.flya.framework.satoken.service.SaPermissionImpl;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n/**\n * sa-token 配置\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Slf4j\n@Configuration\npublic class SaTokenConfig implements WebMvcConfigurer {\n\n    private final SecurityProperties securityProperties;\n\n    /**\n     * 注册sa-token的拦截器\n     */\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        // 注册路由拦截器，自定义验证规则\n        registry.addInterceptor(new SaInterceptor(handler -> {\n            AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class);\n            // 登录验证 -- 排除多个路径\n            SaRouter\n                // 获取所有的\n                .match(allUrlHandler.getUrls())\n                // 对未排除的路径进行检查\n                .check(() -> {\n                    // 检查是否登录 是否有token\n                    StpUtil.checkLogin();\n\n                    // 有效率影响 用于临时测试\n                    // if (log.isDebugEnabled()) {\n                    //     log.debug(\"剩余有效时间: {}\", StpUtil.getTokenTimeout());\n                    //     log.debug(\"临时有效时间: {}\", StpUtil.getTokenActivityTimeout());\n                    // }\n\n                });\n        })).addPathPatterns(\"/**\")\n            // 排除不需要拦截的路径\n            .excludePathPatterns(securityProperties.getExcludes());\n    }\n\n    @Bean\n    public StpLogic getStpLogicJwt() {\n        // Sa-Token 整合 jwt (简单模式)\n        return new StpLogicJwtForSimple();\n    }\n\n    /**\n     * 权限接口实现(使用bean注入方便用户替换)\n     */\n    @Bean\n    public StpInterface stpInterface() {\n        return new SaPermissionImpl();\n    }\n\n    /**\n     * 自定义dao层存储\n     */\n    @Bean\n    public SaTokenDao saTokenDao() {\n        return new PlusSaTokenDao();\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/SwaggerConfig.java",
    "content": "package top.flya.framework.config;\n\nimport top.flya.common.utils.StringUtils;\nimport top.flya.framework.config.properties.SwaggerProperties;\nimport top.flya.framework.handler.OpenApiHandler;\nimport io.swagger.v3.oas.models.OpenAPI;\nimport io.swagger.v3.oas.models.Paths;\nimport io.swagger.v3.oas.models.info.Info;\nimport io.swagger.v3.oas.models.security.SecurityRequirement;\nimport lombok.RequiredArgsConstructor;\nimport org.springdoc.core.*;\nimport org.springdoc.core.customizers.OpenApiBuilderCustomizer;\nimport org.springdoc.core.customizers.OpenApiCustomiser;\nimport org.springdoc.core.customizers.ServerBaseUrlCustomizer;\nimport org.springdoc.core.providers.JavadocProvider;\nimport org.springframework.boot.autoconfigure.AutoConfigureBefore;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.autoconfigure.web.ServerProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\n\n/**\n * Swagger 文档配置\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Configuration\n@AutoConfigureBefore(SpringDocConfiguration.class)\n@ConditionalOnProperty(name = \"springdoc.api-docs.enabled\", havingValue = \"true\", matchIfMissing = true)\npublic class SwaggerConfig {\n\n    private final SwaggerProperties swaggerProperties;\n    private final ServerProperties serverProperties;\n\n    @Bean\n    @ConditionalOnMissingBean(OpenAPI.class)\n    public OpenAPI openApi() {\n        OpenAPI openApi = new OpenAPI();\n        // 文档基本信息\n        SwaggerProperties.InfoProperties infoProperties = swaggerProperties.getInfo();\n        Info info = convertInfo(infoProperties);\n        openApi.info(info);\n        // 扩展文档信息\n        openApi.externalDocs(swaggerProperties.getExternalDocs());\n        openApi.tags(swaggerProperties.getTags());\n        openApi.paths(swaggerProperties.getPaths());\n        openApi.components(swaggerProperties.getComponents());\n        Set<String> keySet = swaggerProperties.getComponents().getSecuritySchemes().keySet();\n        List<SecurityRequirement> list = new ArrayList<>();\n        SecurityRequirement securityRequirement = new SecurityRequirement();\n        keySet.forEach(securityRequirement::addList);\n        list.add(securityRequirement);\n        openApi.security(list);\n\n        return openApi;\n    }\n\n    private Info convertInfo(SwaggerProperties.InfoProperties infoProperties) {\n        Info info = new Info();\n        info.setTitle(infoProperties.getTitle());\n        info.setDescription(infoProperties.getDescription());\n        info.setContact(infoProperties.getContact());\n        info.setLicense(infoProperties.getLicense());\n        info.setVersion(infoProperties.getVersion());\n        return info;\n    }\n\n    /**\n     * 自定义 openapi 处理器\n     */\n    @Bean\n    public OpenAPIService openApiBuilder(Optional<OpenAPI> openAPI,\n                                         SecurityService securityParser,\n                                         SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,\n                                         Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers,\n                                         Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider) {\n        return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider);\n    }\n\n    /**\n     * 对已经生成好的 OpenApi 进行自定义操作\n     */\n    @Bean\n    public OpenApiCustomiser openApiCustomiser() {\n        String contextPath = serverProperties.getServlet().getContextPath();\n        String finalContextPath;\n        if (StringUtils.isBlank(contextPath) || \"/\".equals(contextPath)) {\n            finalContextPath = \"\";\n        } else {\n            finalContextPath = contextPath;\n        }\n        // 对所有路径增加前置上下文路径\n        return openApi -> {\n            Paths oldPaths = openApi.getPaths();\n            if (oldPaths instanceof PlusPaths) {\n                return;\n            }\n            PlusPaths newPaths = new PlusPaths();\n            oldPaths.forEach((k,v) -> newPaths.addPathItem(finalContextPath + k, v));\n            openApi.setPaths(newPaths);\n        };\n    }\n\n    /**\n     * 单独使用一个类便于判断 解决springdoc路径拼接重复问题\n     *\n     * @author Lion Li\n     */\n    static class PlusPaths extends Paths {\n\n        public PlusPaths() {\n            super();\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/ThreadPoolConfig.java",
    "content": "package top.flya.framework.config;\n\nimport top.flya.common.utils.Threads;\nimport top.flya.framework.config.properties.ThreadPoolProperties;\nimport org.apache.commons.lang3.concurrent.BasicThreadFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;\n\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.ThreadPoolExecutor;\n\n/**\n * 线程池配置\n *\n * @author Lion Li\n **/\n@Configuration\npublic class ThreadPoolConfig {\n\n    /**\n     * 核心线程数 = cpu 核心数 + 1\n     */\n    private final int core = Runtime.getRuntime().availableProcessors() + 1;\n\n    @Autowired\n    private ThreadPoolProperties threadPoolProperties;\n\n    @Bean(name = \"threadPoolTaskExecutor\")\n    @ConditionalOnProperty(prefix = \"thread-pool\", name = \"enabled\", havingValue = \"true\")\n    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {\n        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();\n        executor.setCorePoolSize(core);\n        executor.setMaxPoolSize(core * 2);\n        executor.setQueueCapacity(threadPoolProperties.getQueueCapacity());\n        executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds());\n        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());\n        return executor;\n    }\n\n    /**\n     * 执行周期性或定时任务\n     */\n    @Bean(name = \"scheduledExecutorService\")\n    protected ScheduledExecutorService scheduledExecutorService() {\n        return new ScheduledThreadPoolExecutor(core,\n            new BasicThreadFactory.Builder().namingPattern(\"schedule-pool-%d\").daemon(true).build(),\n            new ThreadPoolExecutor.CallerRunsPolicy()) {\n            @Override\n            protected void afterExecute(Runnable r, Throwable t) {\n                super.afterExecute(r, t);\n                Threads.printException(r, t);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/TranslationConfig.java",
    "content": "package top.flya.framework.config;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport top.flya.common.annotation.TranslationType;\nimport top.flya.common.translation.TranslationInterface;\nimport top.flya.common.translation.handler.TranslationBeanSerializerModifier;\nimport top.flya.common.translation.handler.TranslationHandler;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\n\nimport javax.annotation.PostConstruct;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 翻译模块配置类\n *\n * @author Lion Li\n */\n@Slf4j\n@Configuration\npublic class TranslationConfig {\n\n    @Autowired\n    private List<TranslationInterface<?>> list;\n\n    @Autowired\n    private ObjectMapper objectMapper;\n\n    @PostConstruct\n    public void init() {\n        Map<String, TranslationInterface<?>> map = new HashMap<>(list.size());\n        for (TranslationInterface<?> trans : list) {\n            if (trans.getClass().isAnnotationPresent(TranslationType.class)) {\n                TranslationType annotation = trans.getClass().getAnnotation(TranslationType.class);\n                map.put(annotation.type(), trans);\n            } else {\n                log.warn(trans.getClass().getName() + \" 翻译实现类未标注 TranslationType 注解!\");\n            }\n        }\n        TranslationHandler.TRANSLATION_MAPPER.putAll(map);\n        // 设置 Bean 序列化修改器\n        objectMapper.setSerializerFactory(\n            objectMapper.getSerializerFactory()\n                .withSerializerModifier(new TranslationBeanSerializerModifier()));\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/UndertowConfig.java",
    "content": "package top.flya.framework.config;\n\nimport io.undertow.server.DefaultByteBufferPool;\nimport io.undertow.websockets.jsr.WebSocketDeploymentInfo;\nimport org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;\nimport org.springframework.boot.web.server.WebServerFactoryCustomizer;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * Undertow 自定义配置\n *\n * @author Lion Li\n */\n@Configuration\npublic class UndertowConfig implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {\n\n    /**\n     * 设置 Undertow 的 websocket 缓冲池\n     */\n    @Override\n    public void customize(UndertowServletWebServerFactory factory) {\n        // 默认不直接分配内存 如果项目中使用了 websocket 建议直接分配\n        factory.addDeploymentInfoCustomizers(deploymentInfo -> {\n            WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();\n            webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 512));\n            deploymentInfo.addServletContextAttribute(\"io.undertow.websockets.jsr.WebSocketDeploymentInfo\", webSocketDeploymentInfo);\n        });\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/ValidatorConfig.java",
    "content": "package top.flya.framework.config;\n\nimport org.hibernate.validator.HibernateValidator;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;\n\nimport javax.validation.Validator;\nimport java.util.Properties;\n\n/**\n * 校验框架配置类\n *\n * @author Lion Li\n */\n@Configuration\npublic class ValidatorConfig {\n\n    @Autowired\n    private MessageSource messageSource;\n\n    /**\n     * 配置校验框架 快速返回模式\n     */\n    @Bean\n    public Validator validator() {\n        LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();\n        // 国际化\n        factoryBean.setValidationMessageSource(messageSource);\n        // 设置使用 HibernateValidator 校验器\n        factoryBean.setProviderClass(HibernateValidator.class);\n        Properties properties = new Properties();\n        // 设置 快速异常返回\n        properties.setProperty(\"hibernate.validator.fail_fast\", \"true\");\n        factoryBean.setValidationProperties(properties);\n        // 加载配置\n        factoryBean.afterPropertiesSet();\n        return factoryBean.getValidator();\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/properties/CaptchaProperties.java",
    "content": "package top.flya.framework.config.properties;\n\nimport top.flya.common.enums.CaptchaCategory;\nimport top.flya.common.enums.CaptchaType;\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n/**\n * 验证码 配置属性\n *\n * @author Lion Li\n */\n@Data\n@Component\n@ConfigurationProperties(prefix = \"captcha\")\npublic class CaptchaProperties {\n\n    /**\n     * 验证码类型\n     */\n    private CaptchaType type;\n\n    /**\n     * 验证码类别\n     */\n    private CaptchaCategory category;\n\n    /**\n     * 数字验证码位数\n     */\n    private Integer numberLength;\n\n    /**\n     * 字符验证码长度\n     */\n    private Integer charLength;\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/properties/EncryptorProperties.java",
    "content": "package top.flya.framework.config.properties;\n\nimport top.flya.common.enums.AlgorithmType;\nimport top.flya.common.enums.EncodeType;\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n/**\n * 加解密属性配置类\n *\n * @author 老马\n * @version 4.6.0\n */\n@Data\n@Component\n@ConfigurationProperties(prefix = \"mybatis-encryptor\")\npublic class EncryptorProperties {\n\n    /**\n     * 过滤开关\n     */\n    private Boolean enable;\n\n    /**\n     * 默认算法\n     */\n    private AlgorithmType algorithm;\n\n    /**\n     * 安全秘钥\n     */\n    private String password;\n\n    /**\n     * 公钥\n     */\n    private String publicKey;\n\n    /**\n     * 私钥\n     */\n    private String privateKey;\n\n    /**\n     * 编码方式，base64/hex\n     */\n    private EncodeType encode;\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/properties/MailProperties.java",
    "content": "package top.flya.framework.config.properties;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n/**\n * JavaMail 配置属性\n *\n * @author Michelle.Chung\n */\n@Data\n@Component\n@ConfigurationProperties(prefix = \"mail\")\npublic class MailProperties {\n\n    /**\n     * 过滤开关\n     */\n    private Boolean enabled;\n\n    /**\n     * SMTP服务器域名\n     */\n    private String host;\n\n    /**\n     * SMTP服务端口\n     */\n    private Integer port;\n\n    /**\n     * 是否需要用户名密码验证\n     */\n    private Boolean auth;\n\n    /**\n     * 用户名\n     */\n    private String user;\n\n    /**\n     * 密码\n     */\n    private String pass;\n\n    /**\n     * 发送方，遵循RFC-822标准\n     */\n    private String from;\n\n    /**\n     * 使用 STARTTLS安全连接，STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接（TLS或SSL）， 而不是使用一个单独的加密通信端口。\n     */\n    private Boolean starttlsEnable;\n\n    /**\n     * 使用 SSL安全连接\n     */\n    private Boolean sslEnable;\n\n    /**\n     * SMTP超时时长，单位毫秒，缺省值不超时\n     */\n    private Long timeout;\n\n    /**\n     * Socket连接超时值，单位毫秒，缺省值不超时\n     */\n    private Long connectionTimeout;\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/properties/RedissonProperties.java",
    "content": "package top.flya.framework.config.properties;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.redisson.config.ReadMode;\nimport org.redisson.config.SubscriptionMode;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n/**\n * Redisson 配置属性\n *\n * @author Lion Li\n */\n@Data\n@Component\n@ConfigurationProperties(prefix = \"redisson\")\npublic class RedissonProperties {\n\n    /**\n     * redis缓存key前缀\n     */\n    private String keyPrefix;\n\n    /**\n     * 线程池数量,默认值 = 当前处理核数量 * 2\n     */\n    private int threads;\n\n    /**\n     * Netty线程池数量,默认值 = 当前处理核数量 * 2\n     */\n    private int nettyThreads;\n\n    /**\n     * 单机服务配置\n     */\n    private SingleServerConfig singleServerConfig;\n\n    /**\n     * 集群服务配置\n     */\n    private ClusterServersConfig clusterServersConfig;\n\n    @Data\n    @NoArgsConstructor\n    public static class SingleServerConfig {\n\n        /**\n         * 客户端名称\n         */\n        private String clientName;\n\n        /**\n         * 最小空闲连接数\n         */\n        private int connectionMinimumIdleSize;\n\n        /**\n         * 连接池大小\n         */\n        private int connectionPoolSize;\n\n        /**\n         * 连接空闲超时，单位：毫秒\n         */\n        private int idleConnectionTimeout;\n\n        /**\n         * 命令等待超时，单位：毫秒\n         */\n        private int timeout;\n\n        /**\n         * 发布和订阅连接池大小\n         */\n        private int subscriptionConnectionPoolSize;\n\n    }\n\n    @Data\n    @NoArgsConstructor\n    public static class ClusterServersConfig {\n\n        /**\n         * 客户端名称\n         */\n        private String clientName;\n\n        /**\n         * master最小空闲连接数\n         */\n        private int masterConnectionMinimumIdleSize;\n\n        /**\n         * master连接池大小\n         */\n        private int masterConnectionPoolSize;\n\n        /**\n         * slave最小空闲连接数\n         */\n        private int slaveConnectionMinimumIdleSize;\n\n        /**\n         * slave连接池大小\n         */\n        private int slaveConnectionPoolSize;\n\n        /**\n         * 连接空闲超时，单位：毫秒\n         */\n        private int idleConnectionTimeout;\n\n        /**\n         * 命令等待超时，单位：毫秒\n         */\n        private int timeout;\n\n        /**\n         * 发布和订阅连接池大小\n         */\n        private int subscriptionConnectionPoolSize;\n\n        /**\n         * 读取模式\n         */\n        private ReadMode readMode;\n\n        /**\n         * 订阅模式\n         */\n        private SubscriptionMode subscriptionMode;\n\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/properties/SecurityProperties.java",
    "content": "package top.flya.framework.config.properties;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n/**\n * Security 配置属性\n *\n * @author Lion Li\n */\n@Data\n@Component\n@ConfigurationProperties(prefix = \"security\")\npublic class SecurityProperties {\n\n    /**\n     * 排除路径\n     */\n    private String[] excludes;\n\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/properties/SwaggerProperties.java",
    "content": "package top.flya.framework.config.properties;\n\nimport io.swagger.v3.oas.models.Components;\nimport io.swagger.v3.oas.models.ExternalDocumentation;\nimport io.swagger.v3.oas.models.Paths;\nimport io.swagger.v3.oas.models.info.Contact;\nimport io.swagger.v3.oas.models.info.License;\nimport io.swagger.v3.oas.models.tags.Tag;\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.boot.context.properties.NestedConfigurationProperty;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\n\n/**\n * swagger 配置属性\n *\n * @author Lion Li\n */\n@Data\n@Component\n@ConfigurationProperties(prefix = \"swagger\")\npublic class SwaggerProperties {\n\n    /**\n     * 文档基本信息\n     */\n    @NestedConfigurationProperty\n    private InfoProperties info = new InfoProperties();\n\n    /**\n     * 扩展文档地址\n     */\n    @NestedConfigurationProperty\n    private ExternalDocumentation externalDocs;\n\n    /**\n     * 标签\n     */\n    private List<Tag> tags = null;\n\n    /**\n     * 路径\n     */\n    @NestedConfigurationProperty\n    private Paths paths = null;\n\n    /**\n     * 组件\n     */\n    @NestedConfigurationProperty\n    private Components components = null;\n\n    /**\n     * <p>\n     * 文档的基础属性信息\n     * </p>\n     *\n     * @see io.swagger.v3.oas.models.info.Info\n     *\n     * 为了 springboot 自动生产配置提示信息，所以这里复制一个类出来\n     */\n    @Data\n    public static class InfoProperties {\n\n        /**\n         * 标题\n         */\n        private String title = null;\n\n        /**\n         * 描述\n         */\n        private String description = null;\n\n        /**\n         * 联系人信息\n         */\n        @NestedConfigurationProperty\n        private Contact contact = null;\n\n        /**\n         * 许可证\n         */\n        @NestedConfigurationProperty\n        private License license = null;\n\n        /**\n         * 版本\n         */\n        private String version = null;\n\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/properties/ThreadPoolProperties.java",
    "content": "package top.flya.framework.config.properties;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n/**\n * 线程池 配置属性\n *\n * @author Lion Li\n */\n@Data\n@Component\n@ConfigurationProperties(prefix = \"thread-pool\")\npublic class ThreadPoolProperties {\n\n    /**\n     * 是否开启线程池\n     */\n    private boolean enabled;\n\n    /**\n     * 队列最大长度\n     */\n    private int queueCapacity;\n\n    /**\n     * 线程池维护线程所允许的空闲时间\n     */\n    private int keepAliveSeconds;\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/config/properties/XssProperties.java",
    "content": "package top.flya.framework.config.properties;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n/**\n * xss过滤 配置属性\n *\n * @author Lion Li\n */\n@Data\n@Component\n@ConfigurationProperties(prefix = \"xss\")\npublic class XssProperties {\n\n    /**\n     * 过滤开关\n     */\n    private String enabled;\n\n    /**\n     * 排除链接（多个用逗号分隔）\n     */\n    private String excludes;\n\n    /**\n     * 匹配链接\n     */\n    private String urlPatterns;\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/encrypt/MybatisDecryptInterceptor.java",
    "content": "package top.flya.framework.encrypt;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport top.flya.common.annotation.EncryptField;\nimport top.flya.common.encrypt.EncryptContext;\nimport top.flya.common.enums.AlgorithmType;\nimport top.flya.common.enums.EncodeType;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.framework.config.properties.EncryptorProperties;\nimport top.flya.framework.manager.EncryptorManager;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.ibatis.executor.resultset.ResultSetHandler;\nimport org.apache.ibatis.plugin.*;\n\nimport java.lang.reflect.Field;\nimport java.sql.Statement;\nimport java.util.*;\n\n/**\n * 出参解密拦截器\n *\n * @author 老马\n * @version 4.6.0\n */\n@Slf4j\n@Intercepts({@Signature(\n    type = ResultSetHandler.class,\n    method = \"handleResultSets\",\n    args = {Statement.class})\n})\n@AllArgsConstructor\npublic class MybatisDecryptInterceptor implements Interceptor {\n\n    private final EncryptorManager encryptorManager;\n    private final EncryptorProperties defaultProperties;\n\n    @Override\n    public Object intercept(Invocation invocation) throws Throwable {\n        // 获取执行mysql执行结果\n        Object result = invocation.proceed();\n        if (result == null) {\n            return null;\n        }\n        decryptHandler(result);\n        return result;\n    }\n\n    /**\n     * 解密对象\n     *\n     * @param sourceObject 待加密对象\n     */\n    private void decryptHandler(Object sourceObject) {\n        if (ObjectUtil.isNull(sourceObject)) {\n            return;\n        }\n        if (sourceObject instanceof Map<?, ?>) {\n            new HashSet<>(((Map<?, ?>) sourceObject).values()).forEach(this::decryptHandler);\n            return;\n        }\n        if (sourceObject instanceof List<?>) {\n            List<?> sourceList = (List<?>) sourceObject;\n            if(CollUtil.isEmpty(sourceList)) {\n                return;\n            }\n            // 判断第一个元素是否含有注解。如果没有直接返回，提高效率\n            Object firstItem = sourceList.get(0);\n            if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {\n                return;\n            }\n            ((List<?>) sourceObject).forEach(this::decryptHandler);\n            return;\n        }\n        Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());\n        try {\n            for (Field field : fields) {\n                field.set(sourceObject, this.decryptField(String.valueOf(field.get(sourceObject)), field));\n            }\n        } catch (Exception e) {\n            log.error(\"处理解密字段时出错\", e);\n        }\n    }\n\n    /**\n     * 字段值进行加密。通过字段的批注注册新的加密算法\n     *\n     * @param value 待加密的值\n     * @param field 待加密字段\n     * @return 加密后结果\n     */\n    private String decryptField(String value, Field field) {\n        if (ObjectUtil.isNull(value)) {\n            return null;\n        }\n        EncryptField encryptField = field.getAnnotation(EncryptField.class);\n        EncryptContext encryptContext = new EncryptContext();\n        encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());\n        encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode());\n        encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password());\n        encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey());\n        encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey());\n        return this.encryptorManager.decrypt(value, encryptContext);\n    }\n\n    @Override\n    public Object plugin(Object target) {\n        return Plugin.wrap(target, this);\n    }\n\n    @Override\n    public void setProperties(Properties properties) {\n\n    }\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/encrypt/MybatisEncryptInterceptor.java",
    "content": "package top.flya.framework.encrypt;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport top.flya.common.annotation.EncryptField;\nimport top.flya.common.encrypt.EncryptContext;\nimport top.flya.common.enums.AlgorithmType;\nimport top.flya.common.enums.EncodeType;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.framework.config.properties.EncryptorProperties;\nimport top.flya.framework.manager.EncryptorManager;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.ibatis.executor.parameter.ParameterHandler;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.apache.ibatis.plugin.Intercepts;\nimport org.apache.ibatis.plugin.Invocation;\nimport org.apache.ibatis.plugin.Signature;\n\nimport java.lang.reflect.Field;\nimport java.sql.PreparedStatement;\nimport java.util.*;\n\n/**\n * 入参加密拦截器\n *\n * @author 老马\n * @version 4.6.0\n */\n@Slf4j\n@Intercepts({@Signature(\n    type = ParameterHandler.class,\n    method = \"setParameters\",\n    args = {PreparedStatement.class})\n})\n@AllArgsConstructor\npublic class MybatisEncryptInterceptor implements Interceptor {\n\n    private final EncryptorManager encryptorManager;\n    private final EncryptorProperties defaultProperties;\n\n    @Override\n    public Object intercept(Invocation invocation) throws Throwable {\n        return invocation;\n    }\n\n    @Override\n    public Object plugin(Object target) {\n        if (target instanceof ParameterHandler) {\n            // 进行加密操作\n            ParameterHandler parameterHandler = (ParameterHandler) target;\n            Object parameterObject = parameterHandler.getParameterObject();\n            if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) {\n                this.encryptHandler(parameterObject);\n            }\n        }\n        return target;\n    }\n\n    /**\n     * 加密对象\n     *\n     * @param sourceObject 待加密对象\n     */\n    private void encryptHandler(Object sourceObject) {\n        if (ObjectUtil.isNull(sourceObject)) {\n            return;\n        }\n        if (sourceObject instanceof Map<?, ?>) {\n            new HashSet<>(((Map<?, ?>) sourceObject).values()).forEach(this::encryptHandler);\n            return;\n        }\n        if (sourceObject instanceof List<?>) {\n            List<?> sourceList = (List<?>) sourceObject;\n            if(CollUtil.isEmpty(sourceList)) {\n                return;\n            }\n            // 判断第一个元素是否含有注解。如果没有直接返回，提高效率\n            Object firstItem = sourceList.get(0);\n            if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {\n                return;\n            }\n            ((List<?>) sourceObject).forEach(this::encryptHandler);\n            return;\n        }\n        Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());\n        try {\n            for (Field field : fields) {\n                field.set(sourceObject, this.encryptField(String.valueOf(field.get(sourceObject)), field));\n            }\n        } catch (Exception e) {\n            log.error(\"处理加密字段时出错\", e);\n        }\n    }\n\n    /**\n     * 字段值进行加密。通过字段的批注注册新的加密算法\n     *\n     * @param value 待加密的值\n     * @param field 待加密字段\n     * @return 加密后结果\n     */\n    private String encryptField(String value, Field field) {\n        if (ObjectUtil.isNull(value)) {\n            return null;\n        }\n        EncryptField encryptField = field.getAnnotation(EncryptField.class);\n        EncryptContext encryptContext = new EncryptContext();\n        encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());\n        encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode());\n        encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password());\n        encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey());\n        encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey());\n        return this.encryptorManager.encrypt(value, encryptContext);\n    }\n\n\n    @Override\n    public void setProperties(Properties properties) {\n    }\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/handler/AllUrlHandler.java",
    "content": "package top.flya.framework.handler;\n\nimport cn.hutool.core.util.ReUtil;\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.Data;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.method.HandlerMethod;\nimport org.springframework.web.servlet.mvc.method.RequestMappingInfo;\nimport org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;\n\nimport java.util.*;\nimport java.util.regex.Pattern;\n\n/**\n * 获取所有Url配置\n *\n * @author Lion Li\n */\n@Data\n@Component\npublic class AllUrlHandler implements InitializingBean {\n\n    private static final Pattern PATTERN = Pattern.compile(\"\\\\{(.*?)\\\\}\");\n\n    private List<String> urls = new ArrayList<>();\n\n    @Override\n    public void afterPropertiesSet() {\n        Set<String> set = new HashSet<>();\n        RequestMappingHandlerMapping mapping = SpringUtils.getBean(\"requestMappingHandlerMapping\", RequestMappingHandlerMapping.class);\n        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();\n        map.keySet().forEach(info -> {\n            // 获取注解上边的 path 替代 path variable 为 *\n            Objects.requireNonNull(info.getPathPatternsCondition().getPatterns())\n                .forEach(url -> set.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, \"*\")));\n        });\n        urls.addAll(set);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/handler/CreateAndUpdateMetaObjectHandler.java",
    "content": "package top.flya.framework.handler;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.http.HttpStatus;\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.domain.model.LoginUser;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.StringUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.ibatis.reflection.MetaObject;\n\nimport java.util.Date;\n\n/**\n * MP注入处理器\n *\n * @author Lion Li\n * @date 2021/4/25\n */\n@Slf4j\npublic class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler {\n\n    @Override\n    public void insertFill(MetaObject metaObject) {\n        try {\n            if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity) {\n                BaseEntity baseEntity = (BaseEntity) metaObject.getOriginalObject();\n                Date current = ObjectUtil.isNotNull(baseEntity.getCreateTime())\n                    ? baseEntity.getCreateTime() : new Date();\n                baseEntity.setCreateTime(current);\n                baseEntity.setUpdateTime(current);\n\n//                String username =getLoginUsername(); // StringUtils.isNotBlank(baseEntity.getCreateBy())          ? baseEntity.getCreateBy() :\n//                // 当前已登录 且 创建人为空 则填充\n//                baseEntity.setCreateBy(username);\n//                // 当前已登录 且 更新人为空 则填充\n//                baseEntity.setUpdateBy(username);\n            }\n        } catch (Exception e) {\n            throw new ServiceException(\"自动注入异常 => \" + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);\n        }\n    }\n\n    @Override\n    public void updateFill(MetaObject metaObject) {\n        try {\n            if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity) {\n                BaseEntity baseEntity = (BaseEntity) metaObject.getOriginalObject();\n                Date current = new Date();\n                // 更新时间填充(不管为不为空)\n                baseEntity.setUpdateTime(current);\n//                String username = getLoginUsername();\n                // 当前已登录 更新人填充(不管为不为空)\n//                if (StringUtils.isNotBlank(username)) {\n//                    baseEntity.setUpdateBy(username);\n//                }\n            }\n        } catch (Exception e) {\n            throw new ServiceException(\"自动注入异常 => \" + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);\n        }\n    }\n\n    /**\n     * 获取登录用户名\n     */\n    private String getLoginUsername() {\n        LoginUser loginUser;\n        try {\n            loginUser = LoginHelper.getLoginUser();\n        } catch (Exception e) {\n            log.warn(\"自动注入警告 => 用户未登录\");\n            return null;\n        }\n        return ObjectUtil.isNotNull(loginUser) ? loginUser.getUsername() : null;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/handler/KeyPrefixHandler.java",
    "content": "package top.flya.framework.handler;\n\nimport top.flya.common.utils.StringUtils;\nimport org.redisson.api.NameMapper;\n\n/**\n * redis缓存key前缀处理\n *\n * @author ye\n * @date 2022/7/14 17:44\n * @since 4.3.0\n */\npublic class KeyPrefixHandler implements NameMapper {\n\n    private final String keyPrefix;\n\n    public KeyPrefixHandler(String keyPrefix) {\n        //前缀为空 则返回空前缀\n        this.keyPrefix = StringUtils.isBlank(keyPrefix) ? \"\" : keyPrefix + \":\";\n    }\n\n    /**\n     * 增加前缀\n     */\n    @Override\n    public String map(String name) {\n        if (StringUtils.isBlank(name)) {\n            return null;\n        }\n        if (StringUtils.isNotBlank(keyPrefix) && !name.startsWith(keyPrefix)) {\n            return keyPrefix + name;\n        }\n        return name;\n    }\n\n    /**\n     * 去除前缀\n     */\n    @Override\n    public String unmap(String name) {\n        if (StringUtils.isBlank(name)) {\n            return null;\n        }\n        if (StringUtils.isNotBlank(keyPrefix) && name.startsWith(keyPrefix)) {\n            return name.substring(keyPrefix.length());\n        }\n        return name;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/handler/OpenApiHandler.java",
    "content": "package top.flya.framework.handler;\n\nimport cn.hutool.core.io.IoUtil;\nimport io.swagger.v3.core.jackson.TypeNameResolver;\nimport io.swagger.v3.core.util.AnnotationsUtils;\nimport io.swagger.v3.oas.annotations.tags.Tags;\nimport io.swagger.v3.oas.models.Components;\nimport io.swagger.v3.oas.models.OpenAPI;\nimport io.swagger.v3.oas.models.Operation;\nimport io.swagger.v3.oas.models.Paths;\nimport io.swagger.v3.oas.models.tags.Tag;\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springdoc.core.OpenAPIService;\nimport org.springdoc.core.PropertyResolverUtils;\nimport org.springdoc.core.SecurityService;\nimport org.springdoc.core.SpringDocConfigProperties;\nimport org.springdoc.core.customizers.OpenApiBuilderCustomizer;\nimport org.springdoc.core.customizers.ServerBaseUrlCustomizer;\nimport org.springdoc.core.providers.JavadocProvider;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.annotation.AnnotatedElementUtils;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.web.method.HandlerMethod;\n\nimport java.io.StringReader;\nimport java.lang.reflect.Method;\nimport java.util.*;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * 自定义 openapi 处理器\n * 对源码功能进行修改 增强使用\n */\n@SuppressWarnings(\"all\")\npublic class OpenApiHandler extends OpenAPIService {\n\n    /**\n     * The constant LOGGER.\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(OpenAPIService.class);\n\n    /**\n     * The Context.\n     */\n    private ApplicationContext context;\n\n    /**\n     * The Security parser.\n     */\n    private final SecurityService securityParser;\n\n    /**\n     * The Mappings map.\n     */\n    private final Map<String, Object> mappingsMap = new HashMap<>();\n\n    /**\n     * The Springdoc tags.\n     */\n    private final Map<HandlerMethod, io.swagger.v3.oas.models.tags.Tag> springdocTags = new HashMap<>();\n\n    /**\n     * The Open api builder customisers.\n     */\n    private final Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers;\n\n    /**\n     * The server base URL customisers.\n     */\n    private final Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers;\n\n    /**\n     * The Spring doc config properties.\n     */\n    private final SpringDocConfigProperties springDocConfigProperties;\n\n    /**\n     * The Open api.\n     */\n    private OpenAPI openAPI;\n\n    /**\n     * The Cached open api map.\n     */\n    private final Map<String, OpenAPI> cachedOpenAPI = new HashMap<>();\n\n    /**\n     * The Is servers present.\n     */\n    private boolean isServersPresent;\n\n    /**\n     * The Server base url.\n     */\n    private String serverBaseUrl;\n\n    /**\n     * The Property resolver utils.\n     */\n    private final PropertyResolverUtils propertyResolverUtils;\n\n    /**\n     * The javadoc provider.\n     */\n    private final Optional<JavadocProvider> javadocProvider;\n\n    /**\n     * The Basic error controller.\n     */\n    private static Class<?> basicErrorController;\n\n    static {\n        try {\n            //spring-boot 2\n            basicErrorController = Class.forName(\"org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController\");\n        } catch (ClassNotFoundException e) {\n            //spring-boot 1\n            try {\n                basicErrorController = Class.forName(\"org.springframework.boot.autoconfigure.web.BasicErrorController\");\n            } catch (ClassNotFoundException classNotFoundException) {\n                //Basic error controller class not found\n                LOGGER.trace(classNotFoundException.getMessage());\n            }\n        }\n    }\n\n    /**\n     * Instantiates a new Open api builder.\n     *\n     * @param openAPI                   the open api\n     * @param securityParser            the security parser\n     * @param springDocConfigProperties the spring doc config properties\n     * @param propertyResolverUtils     the property resolver utils\n     * @param openApiBuilderCustomizers the open api builder customisers\n     * @param serverBaseUrlCustomizers  the server base url customizers\n     * @param javadocProvider           the javadoc provider\n     */\n    public OpenApiHandler(Optional<OpenAPI> openAPI, SecurityService securityParser,\n                          SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,\n                          Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,\n                          Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,\n                          Optional<JavadocProvider> javadocProvider) {\n        super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);\n        if (openAPI.isPresent()) {\n            this.openAPI = openAPI.get();\n            if (this.openAPI.getComponents() == null)\n                this.openAPI.setComponents(new Components());\n            if (this.openAPI.getPaths() == null)\n                this.openAPI.setPaths(new Paths());\n            if (!CollectionUtils.isEmpty(this.openAPI.getServers()))\n                this.isServersPresent = true;\n        }\n        this.propertyResolverUtils = propertyResolverUtils;\n        this.securityParser = securityParser;\n        this.springDocConfigProperties = springDocConfigProperties;\n        this.openApiBuilderCustomisers = openApiBuilderCustomizers;\n        this.serverBaseUrlCustomizers = serverBaseUrlCustomizers;\n        this.javadocProvider = javadocProvider;\n        if (springDocConfigProperties.isUseFqn())\n            TypeNameResolver.std.setUseFqn(true);\n    }\n\n    @Override\n    public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) {\n\n        Set<Tag> tags = new HashSet<>();\n        Set<String> tagsStr = new HashSet<>();\n\n        buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale);\n        buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale);\n\n        if (!CollectionUtils.isEmpty(tagsStr))\n            tagsStr = tagsStr.stream()\n                .map(str -> propertyResolverUtils.resolve(str, locale))\n                .collect(Collectors.toSet());\n\n        if (springdocTags.containsKey(handlerMethod)) {\n            io.swagger.v3.oas.models.tags.Tag tag = springdocTags.get(handlerMethod);\n            tagsStr.add(tag.getName());\n            if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {\n                openAPI.addTagsItem(tag);\n            }\n        }\n\n        if (!CollectionUtils.isEmpty(tagsStr)) {\n            if (CollectionUtils.isEmpty(operation.getTags()))\n                operation.setTags(new ArrayList<>(tagsStr));\n            else {\n                Set<String> operationTagsSet = new HashSet<>(operation.getTags());\n                operationTagsSet.addAll(tagsStr);\n                operation.getTags().clear();\n                operation.getTags().addAll(operationTagsSet);\n            }\n        }\n\n        if (isAutoTagClasses(operation)) {\n\n\n            if (javadocProvider.isPresent()) {\n                String description = javadocProvider.get().getClassJavadoc(handlerMethod.getBeanType());\n                if (StringUtils.isNotBlank(description)) {\n                    io.swagger.v3.oas.models.tags.Tag tag = new io.swagger.v3.oas.models.tags.Tag();\n\n                    // 自定义部分 修改使用java注释当tag名\n                    List<String> list = IoUtil.readLines(new StringReader(description), new ArrayList<>());\n                    // tag.setName(tagAutoName);\n                    tag.setName(list.get(0));\n                    operation.addTagsItem(list.get(0));\n\n                    tag.setDescription(description);\n                    if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {\n                        openAPI.addTagsItem(tag);\n                    }\n                }\n            } else {\n                String tagAutoName = splitCamelCase(handlerMethod.getBeanType().getSimpleName());\n                operation.addTagsItem(tagAutoName);\n            }\n        }\n\n        if (!CollectionUtils.isEmpty(tags)) {\n            // Existing tags\n            List<io.swagger.v3.oas.models.tags.Tag> openApiTags = openAPI.getTags();\n            if (!CollectionUtils.isEmpty(openApiTags))\n                tags.addAll(openApiTags);\n            openAPI.setTags(new ArrayList<>(tags));\n        }\n\n        // Handle SecurityRequirement at operation level\n        io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements = securityParser\n            .getSecurityRequirements(handlerMethod);\n        if (securityRequirements != null) {\n            if (securityRequirements.length == 0)\n                operation.setSecurity(Collections.emptyList());\n            else\n                securityParser.buildSecurityRequirement(securityRequirements, operation);\n        }\n\n        return operation;\n    }\n\n    private void buildTagsFromMethod(Method method, Set<io.swagger.v3.oas.models.tags.Tag> tags, Set<String> tagsStr, Locale locale) {\n        // method tags\n        Set<Tags> tagsSet = AnnotatedElementUtils\n            .findAllMergedAnnotations(method, Tags.class);\n        Set<io.swagger.v3.oas.annotations.tags.Tag> methodTags = tagsSet.stream()\n            .flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet());\n        methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class));\n        if (!CollectionUtils.isEmpty(methodTags)) {\n            tagsStr.addAll(methodTags.stream().map(tag -> propertyResolverUtils.resolve(tag.name(), locale)).collect(Collectors.toSet()));\n            List<io.swagger.v3.oas.annotations.tags.Tag> allTags = new ArrayList<>(methodTags);\n            addTags(allTags, tags, locale);\n        }\n    }\n\n    private void addTags(List<io.swagger.v3.oas.annotations.tags.Tag> sourceTags, Set<io.swagger.v3.oas.models.tags.Tag> tags, Locale locale) {\n        Optional<Set<io.swagger.v3.oas.models.tags.Tag>> optionalTagSet = AnnotationsUtils\n            .getTags(sourceTags.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true);\n        optionalTagSet.ifPresent(tagsSet -> {\n            tagsSet.forEach(tag -> {\n                tag.name(propertyResolverUtils.resolve(tag.getName(), locale));\n                tag.description(propertyResolverUtils.resolve(tag.getDescription(), locale));\n                if (tags.stream().noneMatch(t -> t.getName().equals(tag.getName())))\n                    tags.add(tag);\n            });\n        });\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/handler/PlusDataPermissionHandler.java",
    "content": "package top.flya.framework.handler;\n\nimport cn.hutool.core.annotation.AnnotationUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ConcurrentHashSet;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.ClassUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport top.flya.common.annotation.DataColumn;\nimport top.flya.common.annotation.DataPermission;\nimport top.flya.common.core.domain.dto.RoleDTO;\nimport top.flya.common.core.domain.model.LoginUser;\nimport top.flya.common.enums.DataScopeType;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.helper.DataPermissionHelper;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.StreamUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.Parenthesis;\nimport net.sf.jsqlparser.expression.operators.conditional.AndExpression;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport org.springframework.context.expression.BeanFactoryResolver;\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.ParserContext;\nimport org.springframework.expression.common.TemplateParserContext;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\n\nimport java.lang.reflect.Method;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * 数据权限过滤\n *\n * @author Lion Li\n * @version 3.5.0\n */\n@Slf4j\npublic class PlusDataPermissionHandler {\n\n    /**\n     * 方法或类(名称) 与 注解的映射关系缓存\n     */\n    private final Map<String, DataPermission> dataPermissionCacheMap = new ConcurrentHashMap<>();\n\n    /**\n     * 无效注解方法缓存用于快速返回\n     */\n    private final Set<String> invalidCacheSet = new ConcurrentHashSet<>();\n\n    /**\n     * spel 解析器\n     */\n    private final ExpressionParser parser = new SpelExpressionParser();\n    private final ParserContext parserContext = new TemplateParserContext();\n    /**\n     * bean解析器 用于处理 spel 表达式中对 bean 的调用\n     */\n    private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory());\n\n\n    public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) {\n        DataColumn[] dataColumns = findAnnotation(mappedStatementId);\n        if (ArrayUtil.isEmpty(dataColumns)) {\n            invalidCacheSet.add(mappedStatementId);\n            return where;\n        }\n        LoginUser currentUser = DataPermissionHelper.getVariable(\"user\");\n        if (ObjectUtil.isNull(currentUser)) {\n            currentUser = LoginHelper.getLoginUser();\n            DataPermissionHelper.setVariable(\"user\", currentUser);\n        }\n        // 如果是超级管理员，则不过滤数据\n        if (LoginHelper.isAdmin()) {\n            return where;\n        }\n        String dataFilterSql = buildDataFilter(dataColumns, isSelect);\n        if (StringUtils.isBlank(dataFilterSql)) {\n            return where;\n        }\n        try {\n            Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql);\n            // 数据权限使用单独的括号 防止与其他条件冲突\n            Parenthesis parenthesis = new Parenthesis(expression);\n            if (ObjectUtil.isNotNull(where)) {\n                return new AndExpression(where, parenthesis);\n            } else {\n                return parenthesis;\n            }\n        } catch (JSQLParserException e) {\n            throw new ServiceException(\"数据权限解析异常 => \" + e.getMessage());\n        }\n    }\n\n    /**\n     * 构造数据过滤sql\n     */\n    private String buildDataFilter(DataColumn[] dataColumns, boolean isSelect) {\n        // 更新或删除需满足所有条件\n        String joinStr = isSelect ? \" OR \" : \" AND \";\n        LoginUser user = DataPermissionHelper.getVariable(\"user\");\n        StandardEvaluationContext context = new StandardEvaluationContext();\n        context.setBeanResolver(beanResolver);\n        DataPermissionHelper.getContext().forEach(context::setVariable);\n        Set<String> conditions = new HashSet<>();\n        for (RoleDTO role : user.getRoles()) {\n            user.setRoleId(role.getRoleId());\n            // 获取角色权限泛型\n            DataScopeType type = DataScopeType.findCode(role.getDataScope());\n            if (ObjectUtil.isNull(type)) {\n                throw new ServiceException(\"角色数据范围异常 => \" + role.getDataScope());\n            }\n            // 全部数据权限直接返回\n            if (type == DataScopeType.ALL) {\n                return \"\";\n            }\n            boolean isSuccess = false;\n            for (DataColumn dataColumn : dataColumns) {\n                if (dataColumn.key().length != dataColumn.value().length) {\n                    throw new ServiceException(\"角色数据范围异常 => key与value长度不匹配\");\n                }\n                // 不包含 key 变量 则不处理\n                if (!StringUtils.containsAny(type.getSqlTemplate(),\n                    Arrays.stream(dataColumn.key()).map(key -> \"#\" + key).toArray(String[]::new)\n                )) {\n                    continue;\n                }\n                // 设置注解变量 key 为表达式变量 value 为变量值\n                for (int i = 0; i < dataColumn.key().length; i++) {\n                    context.setVariable(dataColumn.key()[i], dataColumn.value()[i]);\n                }\n\n                // 解析sql模板并填充\n                String sql = parser.parseExpression(type.getSqlTemplate(), parserContext).getValue(context, String.class);\n                conditions.add(joinStr + sql);\n                isSuccess = true;\n            }\n            // 未处理成功则填充兜底方案\n            if (!isSuccess && StringUtils.isNotBlank(type.getElseSql())) {\n                conditions.add(joinStr + type.getElseSql());\n            }\n        }\n\n        if (CollUtil.isNotEmpty(conditions)) {\n            String sql = StreamUtils.join(conditions, Function.identity(), \"\");\n            return sql.substring(joinStr.length());\n        }\n        return \"\";\n    }\n\n    private DataColumn[] findAnnotation(String mappedStatementId) {\n        StringBuilder sb = new StringBuilder(mappedStatementId);\n        int index = sb.lastIndexOf(\".\");\n        String clazzName = sb.substring(0, index);\n        String methodName = sb.substring(index + 1, sb.length());\n        Class<?> clazz = ClassUtil.loadClass(clazzName);\n        List<Method> methods = Arrays.stream(ClassUtil.getDeclaredMethods(clazz))\n            .filter(method -> method.getName().equals(methodName)).collect(Collectors.toList());\n        DataPermission dataPermission;\n        // 获取方法注解\n        for (Method method : methods) {\n            dataPermission = dataPermissionCacheMap.get(mappedStatementId);\n            if (ObjectUtil.isNotNull(dataPermission)) {\n                return dataPermission.value();\n            }\n            if (AnnotationUtil.hasAnnotation(method, DataPermission.class)) {\n                dataPermission = AnnotationUtil.getAnnotation(method, DataPermission.class);\n                dataPermissionCacheMap.put(mappedStatementId, dataPermission);\n                return dataPermission.value();\n            }\n        }\n        dataPermission = dataPermissionCacheMap.get(clazz.getName());\n        if (ObjectUtil.isNotNull(dataPermission)) {\n            return dataPermission.value();\n        }\n        // 获取类注解\n        if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) {\n            dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class);\n            dataPermissionCacheMap.put(clazz.getName(), dataPermission);\n            return dataPermission.value();\n        }\n        return null;\n    }\n\n    /**\n     * 是否为无效方法 无数据权限\n     */\n    public boolean isInvalid(String mappedStatementId) {\n        return invalidCacheSet.contains(mappedStatementId);\n    }\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/interceptor/PlusDataPermissionInterceptor.java",
    "content": "package top.flya.framework.interceptor;\n\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;\nimport com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;\nimport top.flya.framework.handler.PlusDataPermissionHandler;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.select.SelectBody;\nimport net.sf.jsqlparser.statement.select.SetOperationList;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.List;\n\n/**\n * 数据权限拦截器\n *\n * @author Lion Li\n * @version 3.5.0\n */\npublic class PlusDataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {\n\n    private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler();\n\n    @Override\n    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {\n        // 检查忽略注解\n        if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {\n            return;\n        }\n        // 检查是否无效 无数据权限注解\n        if (dataPermissionHandler.isInvalid(ms.getId())) {\n            return;\n        }\n        // 解析 sql 分配对应方法\n        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);\n        mpBs.sql(parserSingle(mpBs.sql(), ms.getId()));\n    }\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = mpSh.mappedStatement();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {\n            if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {\n                return;\n            }\n            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();\n            mpBs.sql(parserMulti(mpBs.sql(), ms.getId()));\n        }\n    }\n\n    @Override\n    protected void processSelect(Select select, int index, String sql, Object obj) {\n        SelectBody selectBody = select.getSelectBody();\n        if (selectBody instanceof PlainSelect) {\n            this.setWhere((PlainSelect) selectBody, (String) obj);\n        } else if (selectBody instanceof SetOperationList) {\n            SetOperationList setOperationList = (SetOperationList) selectBody;\n            List<SelectBody> selectBodyList = setOperationList.getSelects();\n            selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) obj));\n        }\n    }\n\n    @Override\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        Expression sqlSegment = dataPermissionHandler.getSqlSegment(update.getWhere(), (String) obj, false);\n        if (null != sqlSegment) {\n            update.setWhere(sqlSegment);\n        }\n    }\n\n    @Override\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        Expression sqlSegment = dataPermissionHandler.getSqlSegment(delete.getWhere(), (String) obj, false);\n        if (null != sqlSegment) {\n            delete.setWhere(sqlSegment);\n        }\n    }\n\n    /**\n     * 设置 where 条件\n     *\n     * @param plainSelect       查询对象\n     * @param mappedStatementId 执行方法id\n     */\n    protected void setWhere(PlainSelect plainSelect, String mappedStatementId) {\n        Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), mappedStatementId, true);\n        if (null != sqlSegment) {\n            plainSelect.setWhere(sqlSegment);\n        }\n    }\n\n}\n\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/interceptor/PlusWebInvokeTimeInterceptor.java",
    "content": "package top.flya.framework.interceptor;\n\nimport cn.hutool.core.io.IoUtil;\nimport cn.hutool.core.map.MapUtil;\nimport com.alibaba.ttl.TransmittableThreadLocal;\nimport top.flya.common.filter.RepeatedlyRequestWrapper;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.time.StopWatch;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.servlet.HandlerInterceptor;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.BufferedReader;\nimport java.util.Map;\n\n/**\n * web的调用时间统计拦截器\n * dev环境有效\n *\n * @author Lion Li\n * @since 3.3.0\n */\n@Slf4j\npublic class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {\n\n    private final String prodProfile = \"prod\";\n\n    private final TransmittableThreadLocal<StopWatch> invokeTimeTL = new TransmittableThreadLocal<>();\n\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {\n        if (!prodProfile.equals(SpringUtils.getActiveProfile())) {\n            String url = request.getMethod() + \" \" + request.getRequestURI();\n\n            // 打印请求参数\n            if (isJsonRequest(request)) {\n                String jsonParam = \"\";\n                if (request instanceof RepeatedlyRequestWrapper) {\n                    BufferedReader reader = request.getReader();\n                    jsonParam = IoUtil.read(reader);\n                }\n                log.debug(\"[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]\", url, jsonParam);\n            } else {\n                Map<String, String[]> parameterMap = request.getParameterMap();\n                if (MapUtil.isNotEmpty(parameterMap)) {\n                    String parameters = JsonUtils.toJsonString(parameterMap);\n                    log.debug(\"[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]\", url, parameters);\n                } else {\n                    log.debug(\"[PLUS]开始请求 => URL[{}],无参数\", url);\n                }\n            }\n\n            StopWatch stopWatch = new StopWatch();\n            invokeTimeTL.set(stopWatch);\n            stopWatch.start();\n        }\n        return true;\n    }\n\n    @Override\n    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {\n\n    }\n\n    @Override\n    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {\n        if (!prodProfile.equals(SpringUtils.getActiveProfile())) {\n            StopWatch stopWatch = invokeTimeTL.get();\n            stopWatch.stop();\n            log.debug(\"[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒\", request.getMethod() + \" \" + request.getRequestURI(), stopWatch.getTime());\n            invokeTimeTL.remove();\n        }\n    }\n\n    /**\n     * 判断本次请求的数据类型是否为json\n     *\n     * @param request request\n     * @return boolean\n     */\n    private boolean isJsonRequest(HttpServletRequest request) {\n        String contentType = request.getContentType();\n        if (contentType != null) {\n            return StringUtils.startsWithIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE);\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/jackson/BigNumberSerializer.java",
    "content": "package top.flya.framework.jackson;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.annotation.JacksonStdImpl;\nimport com.fasterxml.jackson.databind.ser.std.NumberSerializer;\n\nimport java.io.IOException;\n\n/**\n * 超出 JS 最大最小值 处理\n *\n * @author Lion Li\n */\n@JacksonStdImpl\npublic class BigNumberSerializer extends NumberSerializer {\n\n    /**\n     * 根据 JS Number.MAX_SAFE_INTEGER 与 Number.MIN_SAFE_INTEGER 得来\n     */\n    private static final long MAX_SAFE_INTEGER = 9007199254740991L;\n    private static final long MIN_SAFE_INTEGER = -9007199254740991L;\n\n    /**\n     * 提供实例\n     */\n    public static final BigNumberSerializer INSTANCE = new BigNumberSerializer(Number.class);\n\n    public BigNumberSerializer(Class<? extends Number> rawType) {\n        super(rawType);\n    }\n\n    @Override\n    public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException {\n        // 超出范围 序列化位字符串\n        if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {\n            super.serialize(value, gen, provider);\n        } else {\n            gen.writeString(value.toString());\n        }\n    }\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/listener/UserActionListener.java",
    "content": "package top.flya.framework.listener;\n\nimport cn.dev33.satoken.config.SaTokenConfig;\nimport cn.dev33.satoken.listener.SaTokenListener;\nimport cn.dev33.satoken.stp.SaLoginModel;\nimport cn.hutool.http.useragent.UserAgent;\nimport cn.hutool.http.useragent.UserAgentUtil;\nimport top.flya.common.constant.CacheConstants;\nimport top.flya.common.core.domain.dto.UserOnlineDTO;\nimport top.flya.common.core.domain.model.LoginUser;\nimport top.flya.common.enums.UserType;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.ServletUtils;\nimport top.flya.common.utils.ip.AddressUtils;\nimport top.flya.common.utils.redis.RedisUtils;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport java.time.Duration;\n\n/**\n * 用户行为 侦听器的实现\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Component\n@Slf4j\npublic class UserActionListener implements SaTokenListener {\n\n    private final SaTokenConfig tokenConfig;\n\n    /**\n     * 每次登录时触发\n     */\n    @Override\n    public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {\n        UserType userType = UserType.getUserType(loginId.toString());\n        if (userType == UserType.SYS_USER) {\n            UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader(\"User-Agent\"));\n            String ip = ServletUtils.getClientIP();\n            LoginUser user = LoginHelper.getLoginUser();\n            UserOnlineDTO dto = new UserOnlineDTO();\n            dto.setIpaddr(ip);\n            dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));\n            dto.setBrowser(userAgent.getBrowser().getName());\n            dto.setOs(userAgent.getOs().getName());\n            dto.setLoginTime(System.currentTimeMillis());\n            dto.setTokenId(tokenValue);\n            dto.setUserName(user.getUsername());\n            dto.setDeptName(user.getDeptName());\n            if(tokenConfig.getTimeout() == -1) {\n                RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);\n            } else {\n                RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout()));\n            }\n            log.info(\"user doLogin, userId:{}, token:{}\", loginId, tokenValue);\n        } else if (userType == UserType.APP_USER) {\n            // app端 自行根据业务编写\n        }\n    }\n\n    /**\n     * 每次注销时触发\n     */\n    @Override\n    public void doLogout(String loginType, Object loginId, String tokenValue) {\n        RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);\n        log.info(\"user doLogout, userId:{}, token:{}\", loginId, tokenValue);\n    }\n\n    /**\n     * 每次被踢下线时触发\n     */\n    @Override\n    public void doKickout(String loginType, Object loginId, String tokenValue) {\n        RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);\n        log.info(\"user doLogoutByLoginId, userId:{}, token:{}\", loginId, tokenValue);\n    }\n\n    /**\n     * 每次被顶下线时触发\n     */\n    @Override\n    public void doReplaced(String loginType, Object loginId, String tokenValue) {\n        RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);\n        log.info(\"user doReplaced, userId:{}, token:{}\", loginId, tokenValue);\n    }\n\n    /**\n     * 每次被封禁时触发\n     */\n    @Override\n    public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {\n    }\n\n    /**\n     * 每次被解封时触发\n     */\n    @Override\n    public void doUntieDisable(String loginType, Object loginId, String service) {\n    }\n\n    /**\n     * 每次打开二级认证时触发\n     */\n    @Override\n    public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {\n    }\n\n    /**\n     * 每次创建Session时触发\n     */\n    @Override\n    public void doCloseSafe(String loginType, String tokenValue, String service) {\n    }\n\n    /**\n     * 每次创建Session时触发\n     */\n    @Override\n    public void doCreateSession(String id) {\n    }\n\n    /**\n     * 每次注销Session时触发\n     */\n    @Override\n    public void doLogoutSession(String id) {\n    }\n\n    /**\n     * 每次Token续期时触发\n     */\n    @Override\n    public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {\n    }\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/manager/EncryptorManager.java",
    "content": "package top.flya.framework.manager;\n\nimport cn.hutool.core.util.ReflectUtil;\nimport top.flya.common.annotation.EncryptField;\nimport top.flya.common.encrypt.EncryptContext;\nimport top.flya.common.encrypt.IEncryptor;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Collectors;\n\n/**\n * 加密管理类\n *\n * @author 老马\n * @version 4.6.0\n */\n@Slf4j\npublic class EncryptorManager {\n\n    /**\n     * 缓存加密器\n     */\n    Map<EncryptContext, IEncryptor> encryptorMap = new ConcurrentHashMap<>();\n\n    /**\n     * 类加密字段缓存\n     */\n    Map<Class<?>, Set<Field>> fieldCache = new ConcurrentHashMap<>();\n\n    /**\n     * 获取类加密字段缓存\n     */\n    public Set<Field> getFieldCache(Class<?> sourceClazz) {\n        return fieldCache.computeIfAbsent(sourceClazz, clazz -> {\n            Field[] declaredFields = clazz.getDeclaredFields();\n            Set<Field> fieldSet = Arrays.stream(declaredFields).filter(field ->\n                    field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)\n                .collect(Collectors.toSet());\n            for (Field field : fieldSet) {\n                field.setAccessible(true);\n            }\n            return fieldSet;\n        });\n    }\n\n    /**\n     * 注册加密执行者到缓存\n     *\n     * @param encryptContext 加密执行者需要的相关配置参数\n     */\n    public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) {\n        if (encryptorMap.containsKey(encryptContext)) {\n            return encryptorMap.get(encryptContext);\n        }\n        IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext);\n        encryptorMap.put(encryptContext, encryptor);\n        return encryptor;\n    }\n\n    /**\n     * 移除缓存中的加密执行者\n     *\n     * @param encryptContext 加密执行者需要的相关配置参数\n     */\n    public void removeEncryptor(EncryptContext encryptContext) {\n        this.encryptorMap.remove(encryptContext);\n    }\n\n    /**\n     * 根据配置进行加密。会进行本地缓存对应的算法和对应的秘钥信息。\n     *\n     * @param value          待加密的值\n     * @param encryptContext 加密相关的配置信息\n     */\n    public String encrypt(String value, EncryptContext encryptContext) {\n        IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);\n        return encryptor.encrypt(value, encryptContext.getEncode());\n    }\n\n    /**\n     * 根据配置进行解密\n     *\n     * @param value          待解密的值\n     * @param encryptContext 加密相关的配置信息\n     */\n    public String decrypt(String value, EncryptContext encryptContext) {\n        IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);\n        return encryptor.decrypt(value);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/manager/PlusSpringCacheManager.java",
    "content": "/**\n * Copyright (c) 2013-2021 Nikita Koksharov\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage top.flya.framework.manager;\n\nimport top.flya.common.utils.redis.RedisUtils;\nimport org.redisson.api.RMap;\nimport org.redisson.api.RMapCache;\nimport org.redisson.spring.cache.CacheConfig;\nimport org.redisson.spring.cache.RedissonCache;\nimport org.springframework.boot.convert.DurationStyle;\nimport org.springframework.cache.Cache;\nimport org.springframework.cache.CacheManager;\nimport org.springframework.cache.transaction.TransactionAwareCacheDecorator;\nimport org.springframework.util.StringUtils;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\n/**\n * A {@link org.springframework.cache.CacheManager} implementation\n * backed by Redisson instance.\n * <p>\n * 修改 RedissonSpringCacheManager 源码\n * 重写 cacheName 处理方法 支持多参数\n *\n * @author Nikita Koksharov\n *\n */\n@SuppressWarnings(\"unchecked\")\npublic class PlusSpringCacheManager implements CacheManager {\n\n    private boolean dynamic = true;\n\n    private boolean allowNullValues = true;\n\n    private boolean transactionAware = true;\n\n    Map<String, CacheConfig> configMap = new ConcurrentHashMap<>();\n    ConcurrentMap<String, Cache> instanceMap = new ConcurrentHashMap<>();\n\n    /**\n     * Creates CacheManager supplied by Redisson instance\n     */\n    public PlusSpringCacheManager() {\n    }\n\n\n    /**\n     * Defines possibility of storing {@code null} values.\n     * <p>\n     * Default is <code>true</code>\n     *\n     * @param allowNullValues stores if <code>true</code>\n     */\n    public void setAllowNullValues(boolean allowNullValues) {\n        this.allowNullValues = allowNullValues;\n    }\n\n    /**\n     * Defines if cache aware of Spring-managed transactions.\n     * If {@code true} put/evict operations are executed only for successful transaction in after-commit phase.\n     * <p>\n     * Default is <code>false</code>\n     *\n     * @param transactionAware cache is transaction aware if <code>true</code>\n     */\n    public void setTransactionAware(boolean transactionAware) {\n        this.transactionAware = transactionAware;\n    }\n\n    /**\n     * Defines 'fixed' cache names.\n     * A new cache instance will not be created in dynamic for non-defined names.\n     * <p>\n     * `null` parameter setups dynamic mode\n     *\n     * @param names of caches\n     */\n    public void setCacheNames(Collection<String> names) {\n        if (names != null) {\n            for (String name : names) {\n                getCache(name);\n            }\n            dynamic = false;\n        } else {\n            dynamic = true;\n        }\n    }\n\n    /**\n     * Set cache config mapped by cache name\n     *\n     * @param config object\n     */\n    public void setConfig(Map<String, ? extends CacheConfig> config) {\n        this.configMap = (Map<String, CacheConfig>) config;\n    }\n\n    protected CacheConfig createDefaultConfig() {\n        return new CacheConfig();\n    }\n\n    @Override\n    public Cache getCache(String name) {\n        Cache cache = instanceMap.get(name);\n        if (cache != null) {\n            return cache;\n        }\n        if (!dynamic) {\n            return cache;\n        }\n\n        CacheConfig config = configMap.get(name);\n        if (config == null) {\n            config = createDefaultConfig();\n            configMap.put(name, config);\n        }\n\n        // 重写 cacheName 支持多参数\n        String[] array = StringUtils.delimitedListToStringArray(name, \"#\");\n        name = array[0];\n        if (array.length > 1) {\n            config.setTTL(DurationStyle.detectAndParse(array[1]).toMillis());\n        }\n        if (array.length > 2) {\n            config.setMaxIdleTime(DurationStyle.detectAndParse(array[2]).toMillis());\n        }\n        if (array.length > 3) {\n            config.setMaxSize(Integer.parseInt(array[3]));\n        }\n\n        if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) {\n            return createMap(name, config);\n        }\n\n        return createMapCache(name, config);\n    }\n\n    private Cache createMap(String name, CacheConfig config) {\n        RMap<Object, Object> map = RedisUtils.getClient().getMap(name);\n\n        Cache cache = new RedissonCache(map, allowNullValues);\n        if (transactionAware) {\n            cache = new TransactionAwareCacheDecorator(cache);\n        }\n        Cache oldCache = instanceMap.putIfAbsent(name, cache);\n        if (oldCache != null) {\n            cache = oldCache;\n        }\n        return cache;\n    }\n\n    private Cache createMapCache(String name, CacheConfig config) {\n        RMapCache<Object, Object> map = RedisUtils.getClient().getMapCache(name);\n\n        Cache cache = new RedissonCache(map, config, allowNullValues);\n        if (transactionAware) {\n            cache = new TransactionAwareCacheDecorator(cache);\n        }\n        Cache oldCache = instanceMap.putIfAbsent(name, cache);\n        if (oldCache != null) {\n            cache = oldCache;\n        } else {\n            map.setMaxSize(config.getMaxSize());\n        }\n        return cache;\n    }\n\n    @Override\n    public Collection<String> getCacheNames() {\n        return Collections.unmodifiableSet(configMap.keySet());\n    }\n\n\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/manager/ShutdownManager.java",
    "content": "package top.flya.framework.manager;\n\nimport top.flya.common.utils.Threads;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.PreDestroy;\nimport java.util.concurrent.ScheduledExecutorService;\n\n/**\n * 确保应用退出时能关闭后台线程\n *\n * @author Lion Li\n */\n@Slf4j\n@Component\npublic class ShutdownManager {\n\n    @Autowired\n    @Qualifier(\"scheduledExecutorService\")\n    private ScheduledExecutorService scheduledExecutorService;\n\n    @PreDestroy\n    public void destroy() {\n        shutdownAsyncManager();\n    }\n\n    /**\n     * 停止异步执行任务\n     */\n    private void shutdownAsyncManager() {\n        try {\n            log.info(\"====关闭后台任务任务线程池====\");\n            Threads.shutdownAndAwaitTermination(scheduledExecutorService);\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n    }\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/satoken/dao/PlusSaTokenDao.java",
    "content": "package top.flya.framework.satoken.dao;\n\nimport cn.dev33.satoken.dao.SaTokenDao;\nimport cn.dev33.satoken.util.SaFoxUtil;\nimport top.flya.common.utils.redis.RedisUtils;\n\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一)\n *\n * @author Lion Li\n */\npublic class PlusSaTokenDao implements SaTokenDao {\n\n    /**\n     * 获取Value，如无返空\n     */\n    @Override\n    public String get(String key) {\n        return RedisUtils.getCacheObject(key);\n    }\n\n    /**\n     * 写入Value，并设定存活时间 (单位: 秒)\n     */\n    @Override\n    public void set(String key, String value, long timeout) {\n        if (timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) {\n            return;\n        }\n        // 判断是否为永不过期\n        if (timeout == SaTokenDao.NEVER_EXPIRE) {\n            RedisUtils.setCacheObject(key, value);\n        } else {\n            RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout));\n        }\n    }\n\n    /**\n     * 修修改指定key-value键值对 (过期时间不变)\n     */\n    @Override\n    public void update(String key, String value) {\n        long expire = getTimeout(key);\n        // -2 = 无此键\n        if (expire == SaTokenDao.NOT_VALUE_EXPIRE) {\n            return;\n        }\n        this.set(key, value, expire);\n    }\n\n    /**\n     * 删除Value\n     */\n    @Override\n    public void delete(String key) {\n        RedisUtils.deleteObject(key);\n    }\n\n    /**\n     * 获取Value的剩余存活时间 (单位: 秒)\n     */\n    @Override\n    public long getTimeout(String key) {\n        long timeout = RedisUtils.getTimeToLive(key);\n        return timeout < 0 ? timeout : timeout / 1000;\n    }\n\n    /**\n     * 修改Value的剩余存活时间 (单位: 秒)\n     */\n    @Override\n    public void updateTimeout(String key, long timeout) {\n        // 判断是否想要设置为永久\n        if (timeout == SaTokenDao.NEVER_EXPIRE) {\n            long expire = getTimeout(key);\n            if (expire == SaTokenDao.NEVER_EXPIRE) {\n                // 如果其已经被设置为永久，则不作任何处理\n            } else {\n                // 如果尚未被设置为永久，那么再次set一次\n                this.set(key, this.get(key), timeout);\n            }\n            return;\n        }\n        RedisUtils.expire(key, Duration.ofSeconds(timeout));\n    }\n\n\n    /**\n     * 获取Object，如无返空\n     */\n    @Override\n    public Object getObject(String key) {\n        return RedisUtils.getCacheObject(key);\n    }\n\n    /**\n     * 写入Object，并设定存活时间 (单位: 秒)\n     */\n    @Override\n    public void setObject(String key, Object object, long timeout) {\n        if (timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) {\n            return;\n        }\n        // 判断是否为永不过期\n        if (timeout == SaTokenDao.NEVER_EXPIRE) {\n            RedisUtils.setCacheObject(key, object);\n        } else {\n            RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout));\n        }\n    }\n\n    /**\n     * 更新Object (过期时间不变)\n     */\n    @Override\n    public void updateObject(String key, Object object) {\n        long expire = getObjectTimeout(key);\n        // -2 = 无此键\n        if (expire == SaTokenDao.NOT_VALUE_EXPIRE) {\n            return;\n        }\n        this.setObject(key, object, expire);\n    }\n\n    /**\n     * 删除Object\n     */\n    @Override\n    public void deleteObject(String key) {\n        RedisUtils.deleteObject(key);\n    }\n\n    /**\n     * 获取Object的剩余存活时间 (单位: 秒)\n     */\n    @Override\n    public long getObjectTimeout(String key) {\n        long timeout = RedisUtils.getTimeToLive(key);\n        return timeout < 0 ? timeout : timeout / 1000;\n    }\n\n    /**\n     * 修改Object的剩余存活时间 (单位: 秒)\n     */\n    @Override\n    public void updateObjectTimeout(String key, long timeout) {\n        // 判断是否想要设置为永久\n        if (timeout == SaTokenDao.NEVER_EXPIRE) {\n            long expire = getObjectTimeout(key);\n            if (expire == SaTokenDao.NEVER_EXPIRE) {\n                // 如果其已经被设置为永久，则不作任何处理\n            } else {\n                // 如果尚未被设置为永久，那么再次set一次\n                this.setObject(key, this.getObject(key), timeout);\n            }\n            return;\n        }\n        RedisUtils.expire(key, Duration.ofSeconds(timeout));\n    }\n\n\n    /**\n     * 搜索数据\n     */\n    @Override\n    public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {\n        Collection<String> keys = RedisUtils.keys(prefix + \"*\" + keyword + \"*\");\n        List<String> list = new ArrayList<>(keys);\n        return SaFoxUtil.searchList(list, start, size, sortType);\n    }\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/satoken/service/SaPermissionImpl.java",
    "content": "package top.flya.framework.satoken.service;\n\nimport cn.dev33.satoken.stp.StpInterface;\nimport top.flya.common.core.domain.model.LoginUser;\nimport top.flya.common.enums.UserType;\nimport top.flya.common.helper.LoginHelper;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * sa-token 权限管理实现类\n *\n * @author Lion Li\n */\npublic class SaPermissionImpl implements StpInterface {\n\n    /**\n     * 获取菜单权限列表\n     */\n    @Override\n    public List<String> getPermissionList(Object loginId, String loginType) {\n        LoginUser loginUser = LoginHelper.getLoginUser();\n        UserType userType = UserType.getUserType(loginUser.getUserType());\n        if (userType == UserType.SYS_USER) {\n            return new ArrayList<>(loginUser.getMenuPermission());\n        } else if (userType == UserType.APP_USER) {\n            // 其他端 自行根据业务编写\n        }\n        return new ArrayList<>();\n    }\n\n    /**\n     * 获取角色权限列表\n     */\n    @Override\n    public List<String> getRoleList(Object loginId, String loginType) {\n        LoginUser loginUser = LoginHelper.getLoginUser();\n        UserType userType = UserType.getUserType(loginUser.getUserType());\n        if (userType == UserType.SYS_USER) {\n            return new ArrayList<>(loginUser.getRolePermission());\n        } else if (userType == UserType.APP_USER) {\n            // 其他端 自行根据业务编写\n        }\n        return new ArrayList<>();\n    }\n}\n"
  },
  {
    "path": "ruoyi-framework/src/main/java/top/flya/framework/web/exception/GlobalExceptionHandler.java",
    "content": "package top.flya.framework.web.exception;\n\nimport cn.dev33.satoken.exception.NotLoginException;\nimport cn.dev33.satoken.exception.NotPermissionException;\nimport cn.dev33.satoken.exception.NotRoleException;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.http.HttpStatus;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.exception.DemoModeException;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.utils.StreamUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.mybatis.spring.MyBatisSystemException;\nimport org.springframework.context.support.DefaultMessageSourceResolvable;\nimport org.springframework.dao.DuplicateKeyException;\nimport org.springframework.validation.BindException;\nimport org.springframework.web.HttpRequestMethodNotSupportedException;\nimport org.springframework.web.bind.MethodArgumentNotValidException;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.RestControllerAdvice;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.validation.ConstraintViolation;\nimport javax.validation.ConstraintViolationException;\n\n/**\n * 全局异常处理器\n *\n * @author Lion Li\n */\n@Slf4j\n@RestControllerAdvice\npublic class GlobalExceptionHandler {\n\n    /**\n     * 权限码异常\n     */\n    @ExceptionHandler(NotPermissionException.class)\n    public R<Void> handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {\n        String requestURI = request.getRequestURI();\n        log.error(\"请求地址'{}',权限码校验失败'{}'\", requestURI, e.getMessage());\n        return R.fail(HttpStatus.HTTP_FORBIDDEN, \"没有访问权限，请联系管理员授权\");\n    }\n\n    /**\n     * 角色权限异常\n     */\n    @ExceptionHandler(NotRoleException.class)\n    public R<Void> handleNotRoleException(NotRoleException e, HttpServletRequest request) {\n        String requestURI = request.getRequestURI();\n        log.error(\"请求地址'{}',角色权限校验失败'{}'\", requestURI, e.getMessage());\n        return R.fail(HttpStatus.HTTP_FORBIDDEN, \"没有访问权限，请联系管理员授权\");\n    }\n\n    /**\n     * 认证失败\n     */\n    @ExceptionHandler(NotLoginException.class)\n    public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) {\n        String requestURI = request.getRequestURI();\n        log.error(\"请求地址'{}',认证失败'{}',无法访问系统资源\", requestURI, e.getMessage());\n        return R.fail(HttpStatus.HTTP_UNAUTHORIZED, \"认证失败，无法访问系统资源\");\n    }\n\n    /**\n     * 请求方式不支持\n     */\n    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)\n    public R<Void> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,\n                                                                HttpServletRequest request) {\n        String requestURI = request.getRequestURI();\n        log.error(\"请求地址'{}',不支持'{}'请求\", requestURI, e.getMethod());\n        return R.fail(e.getMessage());\n    }\n\n    /**\n     * 主键或UNIQUE索引，数据重复异常\n     */\n    @ExceptionHandler(DuplicateKeyException.class)\n    public R<Void> handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) {\n        String requestURI = request.getRequestURI();\n        log.error(\"请求地址'{}',数据库中已存在记录'{}'\", requestURI, e.getMessage());\n        return R.fail(\"数据库中已存在该记录，请联系管理员确认\");\n    }\n\n    /**\n     * Mybatis系统异常 通用处理\n     */\n    @ExceptionHandler(MyBatisSystemException.class)\n    public R<Void> handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) {\n        String requestURI = request.getRequestURI();\n        String message = e.getMessage();\n        if (message.contains(\"CannotFindDataSourceException\")) {\n            log.error(\"请求地址'{}', 未找到数据源\", requestURI);\n            return R.fail(\"未找到数据源，请联系管理员确认\");\n        }\n        log.error(\"请求地址'{}', Mybatis系统异常\", requestURI, e);\n        return R.fail(message);\n    }\n\n    /**\n     * 业务异常\n     */\n    @ExceptionHandler(ServiceException.class)\n    public R<Void> handleServiceException(ServiceException e, HttpServletRequest request) {\n        log.error(e.getMessage(), e);\n        Integer code = e.getCode();\n        return ObjectUtil.isNotNull(code) ? R.fail(code, e.getMessage()) : R.fail(e.getMessage());\n    }\n\n    /**\n     * 拦截未知的运行时异常\n     */\n    @ExceptionHandler(RuntimeException.class)\n    public R<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request) {\n        String requestURI = request.getRequestURI();\n        log.error(\"请求地址'{}',发生未知异常.\", requestURI, e);\n        return R.fail(e.getMessage());\n    }\n\n    /**\n     * 系统异常\n     */\n    @ExceptionHandler(Exception.class)\n    public R<Void> handleException(Exception e, HttpServletRequest request) {\n        String requestURI = request.getRequestURI();\n        log.error(\"请求地址'{}',发生系统异常.\", requestURI, e);\n        return R.fail(e.getMessage());\n    }\n\n    /**\n     * 自定义验证异常\n     */\n    @ExceptionHandler(BindException.class)\n    public R<Void> handleBindException(BindException e) {\n        log.error(e.getMessage(), e);\n        String message = StreamUtils.join(e.getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, \", \");\n        return R.fail(message);\n    }\n\n    /**\n     * 自定义验证异常\n     */\n    @ExceptionHandler(ConstraintViolationException.class)\n    public R<Void> constraintViolationException(ConstraintViolationException e) {\n        log.error(e.getMessage(), e);\n        String message = StreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, \", \");\n        return R.fail(message);\n    }\n\n    /**\n     * 自定义验证异常\n     */\n    @ExceptionHandler(MethodArgumentNotValidException.class)\n    public R<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {\n        log.error(e.getMessage(), e);\n        String message = e.getBindingResult().getFieldError().getDefaultMessage();\n        return R.fail(message);\n    }\n\n    /**\n     * 演示模式异常\n     */\n    @ExceptionHandler(DemoModeException.class)\n    public R<Void> handleDemoModeException(DemoModeException e) {\n        return R.fail(\"演示模式，不允许操作\");\n    }\n}\n"
  },
  {
    "path": "ruoyi-generator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>ruoyi-vue-plus</artifactId>\n        <groupId>com.ruoyi</groupId>\n        <version>4.7.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ruoyi-generator</artifactId>\n\n    <description>\n        generator代码生成\n    </description>\n\n    <dependencies>\n\n        <!--velocity代码生成使用模板 -->\n        <dependency>\n            <groupId>org.apache.velocity</groupId>\n            <artifactId>velocity-engine-core</artifactId>\n        </dependency>\n\n        <!-- 通用工具-->\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>ruoyi-common</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "ruoyi-generator/src/main/java/top/flya/generator/config/GenConfig.java",
    "content": "package top.flya.generator.config;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.PropertySource;\nimport org.springframework.stereotype.Component;\n\n/**\n * 读取代码生成相关配置\n *\n * @author ruoyi\n */\n@Component\n@ConfigurationProperties(prefix = \"gen\")\n@PropertySource(value = {\"classpath:generator.yml\"}, encoding = \"UTF-8\")\npublic class GenConfig {\n\n    /**\n     * 作者\n     */\n    public static String author;\n\n    /**\n     * 生成包路径\n     */\n    public static String packageName;\n\n    /**\n     * 自动去除表前缀，默认是false\n     */\n    public static boolean autoRemovePre;\n\n    /**\n     * 表前缀(类名不会包含表前缀)\n     */\n    public static String tablePrefix;\n\n    public static String getAuthor() {\n        return author;\n    }\n\n    @Value(\"${author}\")\n    public void setAuthor(String author) {\n        GenConfig.author = author;\n    }\n\n    public static String getPackageName() {\n        return packageName;\n    }\n\n    @Value(\"${packageName}\")\n    public void setPackageName(String packageName) {\n        GenConfig.packageName = packageName;\n    }\n\n    public static boolean getAutoRemovePre() {\n        return autoRemovePre;\n    }\n\n    @Value(\"${autoRemovePre}\")\n    public void setAutoRemovePre(boolean autoRemovePre) {\n        GenConfig.autoRemovePre = autoRemovePre;\n    }\n\n    public static String getTablePrefix() {\n        return tablePrefix;\n    }\n\n    @Value(\"${tablePrefix}\")\n    public void setTablePrefix(String tablePrefix) {\n        GenConfig.tablePrefix = tablePrefix;\n    }\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/java/top/flya/generator/controller/GenController.java",
    "content": "package top.flya.generator.controller;\n\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.io.IoUtil;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.generator.domain.GenTable;\nimport top.flya.generator.domain.GenTableColumn;\nimport top.flya.generator.service.IGenTableService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 代码生成 操作处理\n *\n * @author Lion Li\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/tool/gen\")\npublic class GenController extends BaseController {\n\n    private final IGenTableService genTableService;\n\n    /**\n     * 查询代码生成列表\n     */\n    @SaCheckPermission(\"tool:gen:list\")\n    @GetMapping(\"/list\")\n    public TableDataInfo<GenTable> genList(GenTable genTable, PageQuery pageQuery) {\n        return genTableService.selectPageGenTableList(genTable, pageQuery);\n    }\n\n    /**\n     * 修改代码生成业务\n     *\n     * @param tableId 表ID\n     */\n    @SaCheckPermission(\"tool:gen:query\")\n    @GetMapping(value = \"/{tableId}\")\n    public R<Map<String, Object>> getInfo(@PathVariable Long tableId) {\n        GenTable table = genTableService.selectGenTableById(tableId);\n        List<GenTable> tables = genTableService.selectGenTableAll();\n        List<GenTableColumn> list = genTableService.selectGenTableColumnListByTableId(tableId);\n        Map<String, Object> map = new HashMap<String, Object>();\n        map.put(\"info\", table);\n        map.put(\"rows\", list);\n        map.put(\"tables\", tables);\n        return R.ok(map);\n    }\n\n    /**\n     * 查询数据库列表\n     */\n    @SaCheckPermission(\"tool:gen:list\")\n    @GetMapping(\"/db/list\")\n    public TableDataInfo<GenTable> dataList(GenTable genTable, PageQuery pageQuery) {\n        return genTableService.selectPageDbTableList(genTable, pageQuery);\n    }\n\n    /**\n     * 查询数据表字段列表\n     *\n     * @param tableId 表ID\n     */\n    @SaCheckPermission(\"tool:gen:list\")\n    @GetMapping(value = \"/column/{tableId}\")\n    public TableDataInfo<GenTableColumn> columnList(Long tableId) {\n        TableDataInfo<GenTableColumn> dataInfo = new TableDataInfo<>();\n        List<GenTableColumn> list = genTableService.selectGenTableColumnListByTableId(tableId);\n        dataInfo.setRows(list);\n        dataInfo.setTotal(list.size());\n        return dataInfo;\n    }\n\n    /**\n     * 导入表结构（保存）\n     *\n     * @param tables 表名串\n     */\n    @SaCheckPermission(\"tool:gen:import\")\n    @Log(title = \"代码生成\", businessType = BusinessType.IMPORT)\n    @PostMapping(\"/importTable\")\n    public R<Void> importTableSave(String tables) {\n        String[] tableNames = Convert.toStrArray(tables);\n        // 查询表信息\n        List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames);\n        genTableService.importGenTable(tableList);\n        return R.ok();\n    }\n\n    /**\n     * 修改保存代码生成业务\n     */\n    @SaCheckPermission(\"tool:gen:edit\")\n    @Log(title = \"代码生成\", businessType = BusinessType.UPDATE)\n    @PutMapping\n    public R<Void> editSave(@Validated @RequestBody GenTable genTable) {\n        genTableService.validateEdit(genTable);\n        genTableService.updateGenTable(genTable);\n        return R.ok();\n    }\n\n    /**\n     * 删除代码生成\n     *\n     * @param tableIds 表ID串\n     */\n    @SaCheckPermission(\"tool:gen:remove\")\n    @Log(title = \"代码生成\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{tableIds}\")\n    public R<Void> remove(@PathVariable Long[] tableIds) {\n        genTableService.deleteGenTableByIds(tableIds);\n        return R.ok();\n    }\n\n    /**\n     * 预览代码\n     *\n     * @param tableId 表ID\n     */\n    @SaCheckPermission(\"tool:gen:preview\")\n    @GetMapping(\"/preview/{tableId}\")\n    public R<Map<String, String>> preview(@PathVariable(\"tableId\") Long tableId) throws IOException {\n        Map<String, String> dataMap = genTableService.previewCode(tableId);\n        return R.ok(dataMap);\n    }\n\n    /**\n     * 生成代码（下载方式）\n     *\n     * @param tableName 表名\n     */\n    @SaCheckPermission(\"tool:gen:code\")\n    @Log(title = \"代码生成\", businessType = BusinessType.GENCODE)\n    @GetMapping(\"/download/{tableName}\")\n    public void download(HttpServletResponse response, @PathVariable(\"tableName\") String tableName) throws IOException {\n        byte[] data = genTableService.downloadCode(tableName);\n        genCode(response, data);\n    }\n\n    /**\n     * 生成代码（自定义路径）\n     *\n     * @param tableName 表名\n     */\n    @SaCheckPermission(\"tool:gen:code\")\n    @Log(title = \"代码生成\", businessType = BusinessType.GENCODE)\n    @GetMapping(\"/genCode/{tableName}\")\n    public R<Void> genCode(@PathVariable(\"tableName\") String tableName) {\n        genTableService.generatorCode(tableName);\n        return R.ok();\n    }\n\n    /**\n     * 同步数据库\n     *\n     * @param tableName 表名\n     */\n    @SaCheckPermission(\"tool:gen:edit\")\n    @Log(title = \"代码生成\", businessType = BusinessType.UPDATE)\n    @GetMapping(\"/synchDb/{tableName}\")\n    public R<Void> synchDb(@PathVariable(\"tableName\") String tableName) {\n        genTableService.synchDb(tableName);\n        return R.ok();\n    }\n\n    /**\n     * 批量生成代码\n     *\n     * @param tables 表名串\n     */\n    @SaCheckPermission(\"tool:gen:code\")\n    @Log(title = \"代码生成\", businessType = BusinessType.GENCODE)\n    @GetMapping(\"/batchGenCode\")\n    public void batchGenCode(HttpServletResponse response, String tables) throws IOException {\n        String[] tableNames = Convert.toStrArray(tables);\n        byte[] data = genTableService.downloadCode(tableNames);\n        genCode(response, data);\n    }\n\n    /**\n     * 生成zip文件\n     */\n    private void genCode(HttpServletResponse response, byte[] data) throws IOException {\n        response.reset();\n        response.addHeader(\"Access-Control-Allow-Origin\", \"*\");\n        response.addHeader(\"Access-Control-Expose-Headers\", \"Content-Disposition\");\n        response.setHeader(\"Content-Disposition\", \"attachment; filename=\\\"ruoyi.zip\\\"\");\n        response.addHeader(\"Content-Length\", \"\" + data.length);\n        response.setContentType(\"application/octet-stream; charset=UTF-8\");\n        IoUtil.write(response.getOutputStream(), false, data);\n    }\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/java/top/flya/generator/domain/GenTable.java",
    "content": "package top.flya.generator.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport top.flya.common.constant.GenConstants;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.utils.StringUtils;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.apache.commons.lang3.ArrayUtils;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotBlank;\nimport java.util.List;\n\n/**\n * 业务表 gen_table\n *\n * @author Lion Li\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"gen_table\")\npublic class GenTable extends BaseEntity {\n\n    /**\n     * 编号\n     */\n    @TableId(value = \"table_id\")\n    private Long tableId;\n\n    /**\n     * 表名称\n     */\n    @NotBlank(message = \"表名称不能为空\")\n    private String tableName;\n\n    /**\n     * 表描述\n     */\n    @NotBlank(message = \"表描述不能为空\")\n    private String tableComment;\n\n    /**\n     * 关联父表的表名\n     */\n    private String subTableName;\n\n    /**\n     * 本表关联父表的外键名\n     */\n    private String subTableFkName;\n\n    /**\n     * 实体类名称(首字母大写)\n     */\n    @NotBlank(message = \"实体类名称不能为空\")\n    private String className;\n\n    /**\n     * 使用的模板（crud单表操作 tree树表操作 sub主子表操作）\n     */\n    private String tplCategory;\n\n    /**\n     * 生成包路径\n     */\n    @NotBlank(message = \"生成包路径不能为空\")\n    private String packageName;\n\n    /**\n     * 生成模块名\n     */\n    @NotBlank(message = \"生成模块名不能为空\")\n    private String moduleName;\n\n    /**\n     * 生成业务名\n     */\n    @NotBlank(message = \"生成业务名不能为空\")\n    private String businessName;\n\n    /**\n     * 生成功能名\n     */\n    @NotBlank(message = \"生成功能名不能为空\")\n    private String functionName;\n\n    /**\n     * 生成作者\n     */\n    @NotBlank(message = \"作者不能为空\")\n    private String functionAuthor;\n\n    /**\n     * 生成代码方式（0zip压缩包 1自定义路径）\n     */\n    private String genType;\n\n    /**\n     * 生成路径（不填默认项目路径）\n     */\n    @TableField(updateStrategy = FieldStrategy.NOT_EMPTY)\n    private String genPath;\n\n    /**\n     * 主键信息\n     */\n    @TableField(exist = false)\n    private GenTableColumn pkColumn;\n\n    /**\n     * 子表信息\n     */\n    @TableField(exist = false)\n    private GenTable subTable;\n\n    /**\n     * 表列信息\n     */\n    @Valid\n    @TableField(exist = false)\n    private List<GenTableColumn> columns;\n\n    /**\n     * 其它生成选项\n     */\n    private String options;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 树编码字段\n     */\n    @TableField(exist = false)\n    private String treeCode;\n\n    /**\n     * 树父编码字段\n     */\n    @TableField(exist = false)\n    private String treeParentCode;\n\n    /**\n     * 树名称字段\n     */\n    @TableField(exist = false)\n    private String treeName;\n\n    /*\n     * 菜单id列表\n     */\n    @TableField(exist = false)\n    private List<Long> menuIds;\n\n    /**\n     * 上级菜单ID字段\n     */\n    @TableField(exist = false)\n    private String parentMenuId;\n\n    /**\n     * 上级菜单名称字段\n     */\n    @TableField(exist = false)\n    private String parentMenuName;\n\n    public boolean isSub() {\n        return isSub(this.tplCategory);\n    }\n\n    public static boolean isSub(String tplCategory) {\n        return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory);\n    }\n\n    public boolean isTree() {\n        return isTree(this.tplCategory);\n    }\n\n    public static boolean isTree(String tplCategory) {\n        return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory);\n    }\n\n    public boolean isCrud() {\n        return isCrud(this.tplCategory);\n    }\n\n    public static boolean isCrud(String tplCategory) {\n        return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory);\n    }\n\n    public boolean isSuperColumn(String javaField) {\n        return isSuperColumn(this.tplCategory, javaField);\n    }\n\n    public static boolean isSuperColumn(String tplCategory, String javaField) {\n        if (isTree(tplCategory)) {\n            return StringUtils.equalsAnyIgnoreCase(javaField,\n                ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY));\n        }\n        return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY);\n    }\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/java/top/flya/generator/domain/GenTableColumn.java",
    "content": "package top.flya.generator.domain;\n\nimport com.baomidou.mybatisplus.annotation.FieldStrategy;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.utils.StringUtils;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.apache.ibatis.type.JdbcType;\n\nimport javax.validation.constraints.NotBlank;\n\n/**\n * 代码生成业务字段表 gen_table_column\n *\n * @author Lion Li\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"gen_table_column\")\npublic class GenTableColumn extends BaseEntity {\n\n    /**\n     * 编号\n     */\n    @TableId(value = \"column_id\")\n    private Long columnId;\n\n    /**\n     * 归属表编号\n     */\n    private Long tableId;\n\n    /**\n     * 列名称\n     */\n    private String columnName;\n\n    /**\n     * 列描述\n     */\n    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)\n    private String columnComment;\n\n    /**\n     * 列类型\n     */\n    private String columnType;\n\n    /**\n     * JAVA类型\n     */\n    private String javaType;\n\n    /**\n     * JAVA字段名\n     */\n    @NotBlank(message = \"Java属性不能为空\")\n    private String javaField;\n\n    /**\n     * 是否主键（1是）\n     */\n    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)\n    private String isPk;\n\n    /**\n     * 是否自增（1是）\n     */\n    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)\n    private String isIncrement;\n\n    /**\n     * 是否必填（1是）\n     */\n    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)\n    private String isRequired;\n\n    /**\n     * 是否为插入字段（1是）\n     */\n    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)\n    private String isInsert;\n\n    /**\n     * 是否编辑字段（1是）\n     */\n    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)\n    private String isEdit;\n\n    /**\n     * 是否列表字段（1是）\n     */\n    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)\n    private String isList;\n\n    /**\n     * 是否查询字段（1是）\n     */\n    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)\n    private String isQuery;\n\n    /**\n     * 查询方式（EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围）\n     */\n    private String queryType;\n\n    /**\n     * 显示类型（input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件）\n     */\n    private String htmlType;\n\n    /**\n     * 字典类型\n     */\n    private String dictType;\n\n    /**\n     * 排序\n     */\n    private Integer sort;\n\n    public String getCapJavaField() {\n        return StringUtils.capitalize(javaField);\n    }\n\n    public boolean isPk() {\n        return isPk(this.isPk);\n    }\n\n    public boolean isPk(String isPk) {\n        return isPk != null && StringUtils.equals(\"1\", isPk);\n    }\n\n    public boolean isIncrement() {\n        return isIncrement(this.isIncrement);\n    }\n\n    public boolean isIncrement(String isIncrement) {\n        return isIncrement != null && StringUtils.equals(\"1\", isIncrement);\n    }\n\n    public boolean isRequired() {\n        return isRequired(this.isRequired);\n    }\n\n    public boolean isRequired(String isRequired) {\n        return isRequired != null && StringUtils.equals(\"1\", isRequired);\n    }\n\n    public boolean isInsert() {\n        return isInsert(this.isInsert);\n    }\n\n    public boolean isInsert(String isInsert) {\n        return isInsert != null && StringUtils.equals(\"1\", isInsert);\n    }\n\n    public boolean isEdit() {\n        return isInsert(this.isEdit);\n    }\n\n    public boolean isEdit(String isEdit) {\n        return isEdit != null && StringUtils.equals(\"1\", isEdit);\n    }\n\n    public boolean isList() {\n        return isList(this.isList);\n    }\n\n    public boolean isList(String isList) {\n        return isList != null && StringUtils.equals(\"1\", isList);\n    }\n\n    public boolean isQuery() {\n        return isQuery(this.isQuery);\n    }\n\n    public boolean isQuery(String isQuery) {\n        return isQuery != null && StringUtils.equals(\"1\", isQuery);\n    }\n\n    public boolean isSuperColumn() {\n        return isSuperColumn(this.javaField);\n    }\n\n    public static boolean isSuperColumn(String javaField) {\n        return StringUtils.equalsAnyIgnoreCase(javaField,\n            // BaseEntity\n            \"createBy\", \"createTime\", \"updateBy\", \"updateTime\",\n            // TreeEntity\n            \"parentName\", \"parentId\");\n    }\n\n    public boolean isUsableColumn() {\n        return isUsableColumn(javaField);\n    }\n\n    public static boolean isUsableColumn(String javaField) {\n        // isSuperColumn()中的名单用于避免生成多余Domain属性，若某些属性在生成页面时需要用到不能忽略，则放在此处白名单\n        return StringUtils.equalsAnyIgnoreCase(javaField, \"parentId\", \"orderNum\", \"remark\");\n    }\n\n    public String readConverterExp() {\n        String remarks = StringUtils.substringBetween(this.columnComment, \"（\", \"）\");\n        StringBuffer sb = new StringBuffer();\n        if (StringUtils.isNotEmpty(remarks)) {\n            for (String value : remarks.split(\" \")) {\n                if (StringUtils.isNotEmpty(value)) {\n                    Object startStr = value.subSequence(0, 1);\n                    String endStr = value.substring(1);\n                    sb.append(StringUtils.EMPTY).append(startStr).append(\"=\").append(endStr).append(StringUtils.SEPARATOR);\n                }\n            }\n            return sb.deleteCharAt(sb.length() - 1).toString();\n        } else {\n            return this.columnComment;\n        }\n    }\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/java/top/flya/generator/mapper/GenTableColumnMapper.java",
    "content": "package top.flya.generator.mapper;\n\nimport com.baomidou.mybatisplus.annotation.InterceptorIgnore;\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.generator.domain.GenTableColumn;\n\nimport java.util.List;\n\n/**\n * 业务字段 数据层\n *\n * @author Lion Li\n */\n@InterceptorIgnore(dataPermission = \"true\")\npublic interface GenTableColumnMapper extends BaseMapperPlus<GenTableColumnMapper, GenTableColumn, GenTableColumn> {\n    /**\n     * 根据表名称查询列信息\n     *\n     * @param tableName 表名称\n     * @return 列信息\n     */\n    List<GenTableColumn> selectDbTableColumnsByName(String tableName);\n\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/java/top/flya/generator/mapper/GenTableMapper.java",
    "content": "package top.flya.generator.mapper;\n\nimport com.baomidou.mybatisplus.annotation.InterceptorIgnore;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.generator.domain.GenTable;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * 业务 数据层\n *\n * @author Lion Li\n */\n@InterceptorIgnore(dataPermission = \"true\")\npublic interface GenTableMapper extends BaseMapperPlus<GenTableMapper, GenTable, GenTable> {\n\n    /**\n     * 查询据库列表\n     *\n     * @param genTable 查询条件\n     * @return 数据库表集合\n     */\n    Page<GenTable> selectPageDbTableList(@Param(\"page\") Page<GenTable> page, @Param(\"genTable\") GenTable genTable);\n\n    /**\n     * 查询据库列表\n     *\n     * @param tableNames 表名称组\n     * @return 数据库表集合\n     */\n    List<GenTable> selectDbTableListByNames(String[] tableNames);\n\n    /**\n     * 查询所有表信息\n     *\n     * @return 表信息集合\n     */\n    List<GenTable> selectGenTableAll();\n\n    /**\n     * 查询表ID业务信息\n     *\n     * @param id 业务ID\n     * @return 业务信息\n     */\n    GenTable selectGenTableById(Long id);\n\n    /**\n     * 查询表名称业务信息\n     *\n     * @param tableName 表名称\n     * @return 业务信息\n     */\n    GenTable selectGenTableByName(String tableName);\n\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/java/top/flya/generator/service/GenTableServiceImpl.java",
    "content": "package top.flya.generator.service;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.io.IoUtil;\nimport cn.hutool.core.lang.Dict;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport top.flya.common.constant.Constants;\nimport top.flya.common.constant.GenConstants;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.common.utils.StreamUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.file.FileUtils;\nimport top.flya.generator.domain.GenTable;\nimport top.flya.generator.domain.GenTableColumn;\nimport top.flya.generator.mapper.GenTableColumnMapper;\nimport top.flya.generator.mapper.GenTableMapper;\nimport top.flya.generator.util.GenUtils;\nimport top.flya.generator.util.VelocityInitializer;\nimport top.flya.generator.util.VelocityUtils;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.velocity.Template;\nimport org.apache.velocity.VelocityContext;\nimport org.apache.velocity.app.Velocity;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.StringWriter;\nimport java.nio.charset.StandardCharsets;\nimport java.util.*;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipOutputStream;\n\n/**\n * 业务 服务层实现\n *\n * @author Lion Li\n */\n@DS(\"#header.datasource\")\n@Slf4j\n@RequiredArgsConstructor\n@Service\npublic class GenTableServiceImpl implements IGenTableService {\n\n    private final GenTableMapper baseMapper;\n    private final GenTableColumnMapper genTableColumnMapper;\n    private final IdentifierGenerator identifierGenerator;\n\n    /**\n     * 查询业务字段列表\n     *\n     * @param tableId 业务字段编号\n     * @return 业务字段集合\n     */\n    @Override\n    public List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId) {\n        return genTableColumnMapper.selectList(new LambdaQueryWrapper<GenTableColumn>()\n            .eq(GenTableColumn::getTableId, tableId)\n            .orderByAsc(GenTableColumn::getSort));\n    }\n\n    /**\n     * 查询业务信息\n     *\n     * @param id 业务ID\n     * @return 业务信息\n     */\n    @Override\n    public GenTable selectGenTableById(Long id) {\n        GenTable genTable = baseMapper.selectGenTableById(id);\n        setTableFromOptions(genTable);\n        return genTable;\n    }\n\n    @Override\n    public TableDataInfo<GenTable> selectPageGenTableList(GenTable genTable, PageQuery pageQuery) {\n        Page<GenTable> page = baseMapper.selectPage(pageQuery.build(), this.buildGenTableQueryWrapper(genTable));\n        return TableDataInfo.build(page);\n    }\n\n    private QueryWrapper<GenTable> buildGenTableQueryWrapper(GenTable genTable) {\n        Map<String, Object> params = genTable.getParams();\n        QueryWrapper<GenTable> wrapper = Wrappers.query();\n        wrapper.like(StringUtils.isNotBlank(genTable.getTableName()), \"lower(table_name)\", StringUtils.lowerCase(genTable.getTableName()))\n            .like(StringUtils.isNotBlank(genTable.getTableComment()), \"lower(table_comment)\", StringUtils.lowerCase(genTable.getTableComment()))\n            .between(params.get(\"beginTime\") != null && params.get(\"endTime\") != null,\n                \"create_time\", params.get(\"beginTime\"), params.get(\"endTime\"));\n        return wrapper;\n    }\n\n\n    @Override\n    public TableDataInfo<GenTable> selectPageDbTableList(GenTable genTable, PageQuery pageQuery) {\n        Page<GenTable> page = baseMapper.selectPageDbTableList(pageQuery.build(), genTable);\n        return TableDataInfo.build(page);\n    }\n\n    /**\n     * 查询据库列表\n     *\n     * @param tableNames 表名称组\n     * @return 数据库表集合\n     */\n    @Override\n    public List<GenTable> selectDbTableListByNames(String[] tableNames) {\n        return baseMapper.selectDbTableListByNames(tableNames);\n    }\n\n    /**\n     * 查询所有表信息\n     *\n     * @return 表信息集合\n     */\n    @Override\n    public List<GenTable> selectGenTableAll() {\n        return baseMapper.selectGenTableAll();\n    }\n\n    /**\n     * 修改业务\n     *\n     * @param genTable 业务信息\n     * @return 结果\n     */\n    @Transactional(rollbackFor = Exception.class)\n    @Override\n    public void updateGenTable(GenTable genTable) {\n        String options = JsonUtils.toJsonString(genTable.getParams());\n        genTable.setOptions(options);\n        int row = baseMapper.updateById(genTable);\n        if (row > 0) {\n            for (GenTableColumn cenTableColumn : genTable.getColumns()) {\n                genTableColumnMapper.updateById(cenTableColumn);\n            }\n        }\n    }\n\n    /**\n     * 删除业务对象\n     *\n     * @param tableIds 需要删除的数据ID\n     * @return 结果\n     */\n    @Transactional(rollbackFor = Exception.class)\n    @Override\n    public void deleteGenTableByIds(Long[] tableIds) {\n        List<Long> ids = Arrays.asList(tableIds);\n        baseMapper.deleteBatchIds(ids);\n        genTableColumnMapper.delete(new LambdaQueryWrapper<GenTableColumn>().in(GenTableColumn::getTableId, ids));\n    }\n\n    /**\n     * 导入表结构\n     *\n     * @param tableList 导入表列表\n     */\n    @Transactional(rollbackFor = Exception.class)\n    @Override\n    public void importGenTable(List<GenTable> tableList) {\n        String operName = LoginHelper.getUsername();\n        try {\n            for (GenTable table : tableList) {\n                String tableName = table.getTableName();\n                GenUtils.initTable(table, operName);\n                int row = baseMapper.insert(table);\n                if (row > 0) {\n                    // 保存列信息\n                    List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);\n                    List<GenTableColumn> saveColumns = new ArrayList<>();\n                    for (GenTableColumn column : genTableColumns) {\n                        GenUtils.initColumnField(column, table);\n                        saveColumns.add(column);\n                    }\n                    if (CollUtil.isNotEmpty(saveColumns)) {\n                        genTableColumnMapper.insertBatch(saveColumns);\n                    }\n                }\n            }\n        } catch (Exception e) {\n            throw new ServiceException(\"导入失败：\" + e.getMessage());\n        }\n    }\n\n    /**\n     * 预览代码\n     *\n     * @param tableId 表编号\n     * @return 预览数据列表\n     */\n    @Override\n    public Map<String, String> previewCode(Long tableId) {\n        Map<String, String> dataMap = new LinkedHashMap<>();\n        // 查询表信息\n        GenTable table = baseMapper.selectGenTableById(tableId);\n        List<Long> menuIds = new ArrayList<>();\n        for (int i = 0; i < 6; i++) {\n            menuIds.add(identifierGenerator.nextId(null).longValue());\n        }\n        table.setMenuIds(menuIds);\n        // 设置主子表信息\n        setSubTable(table);\n        // 设置主键列信息\n        setPkColumn(table);\n        VelocityInitializer.initVelocity();\n\n        VelocityContext context = VelocityUtils.prepareContext(table);\n\n        // 获取模板列表\n        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());\n        for (String template : templates) {\n            // 渲染模板\n            StringWriter sw = new StringWriter();\n            Template tpl = Velocity.getTemplate(template, Constants.UTF8);\n            tpl.merge(context, sw);\n            dataMap.put(template, sw.toString());\n        }\n        return dataMap;\n    }\n\n    /**\n     * 生成代码（下载方式）\n     *\n     * @param tableName 表名称\n     * @return 数据\n     */\n    @Override\n    public byte[] downloadCode(String tableName) {\n        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();\n        ZipOutputStream zip = new ZipOutputStream(outputStream);\n        generatorCode(tableName, zip);\n        IoUtil.close(zip);\n        return outputStream.toByteArray();\n    }\n\n    /**\n     * 生成代码（自定义路径）\n     *\n     * @param tableName 表名称\n     */\n    @Override\n    public void generatorCode(String tableName) {\n        // 查询表信息\n        GenTable table = baseMapper.selectGenTableByName(tableName);\n        // 设置主子表信息\n        setSubTable(table);\n        // 设置主键列信息\n        setPkColumn(table);\n\n        VelocityInitializer.initVelocity();\n\n        VelocityContext context = VelocityUtils.prepareContext(table);\n\n        // 获取模板列表\n        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());\n        for (String template : templates) {\n            if (!StringUtils.containsAny(template, \"sql.vm\", \"api.js.vm\", \"index.vue.vm\", \"index-tree.vue.vm\")) {\n                // 渲染模板\n                StringWriter sw = new StringWriter();\n                Template tpl = Velocity.getTemplate(template, Constants.UTF8);\n                tpl.merge(context, sw);\n                try {\n                    String path = getGenPath(table, template);\n                    FileUtils.writeUtf8String(sw.toString(), path);\n                } catch (Exception e) {\n                    throw new ServiceException(\"渲染模板失败，表名：\" + table.getTableName());\n                }\n            }\n        }\n    }\n\n    /**\n     * 同步数据库\n     *\n     * @param tableName 表名称\n     */\n    @Transactional(rollbackFor = Exception.class)\n    @Override\n    public void synchDb(String tableName) {\n        GenTable table = baseMapper.selectGenTableByName(tableName);\n        List<GenTableColumn> tableColumns = table.getColumns();\n        Map<String, GenTableColumn> tableColumnMap = StreamUtils.toIdentityMap(tableColumns, GenTableColumn::getColumnName);\n\n        List<GenTableColumn> dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);\n        if (CollUtil.isEmpty(dbTableColumns)) {\n            throw new ServiceException(\"同步数据失败，原表结构不存在\");\n        }\n        List<String> dbTableColumnNames = StreamUtils.toList(dbTableColumns, GenTableColumn::getColumnName);\n\n        List<GenTableColumn> saveColumns = new ArrayList<>();\n        dbTableColumns.forEach(column -> {\n            GenUtils.initColumnField(column, table);\n            if (tableColumnMap.containsKey(column.getColumnName())) {\n                GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName());\n                column.setColumnId(prevColumn.getColumnId());\n                if (column.isList()) {\n                    // 如果是列表，继续保留查询方式/字典类型选项\n                    column.setDictType(prevColumn.getDictType());\n                    column.setQueryType(prevColumn.getQueryType());\n                }\n                if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk()\n                    && (column.isInsert() || column.isEdit())\n                    && ((column.isUsableColumn()) || (!column.isSuperColumn()))) {\n                    // 如果是(新增/修改&非主键/非忽略及父属性)，继续保留必填/显示类型选项\n                    column.setIsRequired(prevColumn.getIsRequired());\n                    column.setHtmlType(prevColumn.getHtmlType());\n                }\n            }\n            saveColumns.add(column);\n        });\n        if (CollUtil.isNotEmpty(saveColumns)) {\n            genTableColumnMapper.insertOrUpdateBatch(saveColumns);\n        }\n        List<GenTableColumn> delColumns = StreamUtils.filter(tableColumns, column -> !dbTableColumnNames.contains(column.getColumnName()));\n        if (CollUtil.isNotEmpty(delColumns)) {\n            List<Long> ids = StreamUtils.toList(delColumns, GenTableColumn::getColumnId);\n            genTableColumnMapper.deleteBatchIds(ids);\n        }\n    }\n\n    /**\n     * 批量生成代码（下载方式）\n     *\n     * @param tableNames 表数组\n     * @return 数据\n     */\n    @Override\n    public byte[] downloadCode(String[] tableNames) {\n        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();\n        ZipOutputStream zip = new ZipOutputStream(outputStream);\n        for (String tableName : tableNames) {\n            generatorCode(tableName, zip);\n        }\n        IoUtil.close(zip);\n        return outputStream.toByteArray();\n    }\n\n    /**\n     * 查询表信息并生成代码\n     */\n    private void generatorCode(String tableName, ZipOutputStream zip) {\n        // 查询表信息\n        GenTable table = baseMapper.selectGenTableByName(tableName);\n        List<Long> menuIds = new ArrayList<>();\n        for (int i = 0; i < 6; i++) {\n            menuIds.add(identifierGenerator.nextId(null).longValue());\n        }\n        table.setMenuIds(menuIds);\n        // 设置主子表信息\n        setSubTable(table);\n        // 设置主键列信息\n        setPkColumn(table);\n\n        VelocityInitializer.initVelocity();\n\n        VelocityContext context = VelocityUtils.prepareContext(table);\n\n        // 获取模板列表\n        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());\n        for (String template : templates) {\n            // 渲染模板\n            StringWriter sw = new StringWriter();\n            Template tpl = Velocity.getTemplate(template, Constants.UTF8);\n            tpl.merge(context, sw);\n            try {\n                // 添加到zip\n                zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table)));\n                IoUtil.write(zip, StandardCharsets.UTF_8, false, sw.toString());\n                IoUtil.close(sw);\n                zip.flush();\n                zip.closeEntry();\n            } catch (IOException e) {\n                log.error(\"渲染模板失败，表名：\" + table.getTableName(), e);\n            }\n        }\n    }\n\n    /**\n     * 修改保存参数校验\n     *\n     * @param genTable 业务信息\n     */\n    @Override\n    public void validateEdit(GenTable genTable) {\n        if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) {\n            String options = JsonUtils.toJsonString(genTable.getParams());\n            Dict paramsObj = JsonUtils.parseMap(options);\n            if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_CODE))) {\n                throw new ServiceException(\"树编码字段不能为空\");\n            } else if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_PARENT_CODE))) {\n                throw new ServiceException(\"树父编码字段不能为空\");\n            } else if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_NAME))) {\n                throw new ServiceException(\"树名称字段不能为空\");\n            } else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) {\n                if (StringUtils.isEmpty(genTable.getSubTableName())) {\n                    throw new ServiceException(\"关联子表的表名不能为空\");\n                } else if (StringUtils.isEmpty(genTable.getSubTableFkName())) {\n                    throw new ServiceException(\"子表关联的外键名不能为空\");\n                }\n            }\n        }\n    }\n\n    /**\n     * 设置主键列信息\n     *\n     * @param table 业务表信息\n     */\n    public void setPkColumn(GenTable table) {\n        for (GenTableColumn column : table.getColumns()) {\n            if (column.isPk()) {\n                table.setPkColumn(column);\n                break;\n            }\n        }\n        if (ObjectUtil.isNull(table.getPkColumn())) {\n            table.setPkColumn(table.getColumns().get(0));\n        }\n        if (GenConstants.TPL_SUB.equals(table.getTplCategory())) {\n            for (GenTableColumn column : table.getSubTable().getColumns()) {\n                if (column.isPk()) {\n                    table.getSubTable().setPkColumn(column);\n                    break;\n                }\n            }\n            if (ObjectUtil.isNull(table.getSubTable().getPkColumn())) {\n                table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0));\n            }\n        }\n    }\n\n    /**\n     * 设置主子表信息\n     *\n     * @param table 业务表信息\n     */\n    public void setSubTable(GenTable table) {\n        String subTableName = table.getSubTableName();\n        if (StringUtils.isNotEmpty(subTableName)) {\n            table.setSubTable(baseMapper.selectGenTableByName(subTableName));\n        }\n    }\n\n    /**\n     * 设置代码生成其他选项值\n     *\n     * @param genTable 设置后的生成对象\n     */\n    public void setTableFromOptions(GenTable genTable) {\n        Dict paramsObj = JsonUtils.parseMap(genTable.getOptions());\n        if (ObjectUtil.isNotNull(paramsObj)) {\n            String treeCode = paramsObj.getStr(GenConstants.TREE_CODE);\n            String treeParentCode = paramsObj.getStr(GenConstants.TREE_PARENT_CODE);\n            String treeName = paramsObj.getStr(GenConstants.TREE_NAME);\n            String parentMenuId = paramsObj.getStr(GenConstants.PARENT_MENU_ID);\n            String parentMenuName = paramsObj.getStr(GenConstants.PARENT_MENU_NAME);\n\n            genTable.setTreeCode(treeCode);\n            genTable.setTreeParentCode(treeParentCode);\n            genTable.setTreeName(treeName);\n            genTable.setParentMenuId(parentMenuId);\n            genTable.setParentMenuName(parentMenuName);\n        }\n    }\n\n    /**\n     * 获取代码生成地址\n     *\n     * @param table    业务表信息\n     * @param template 模板文件路径\n     * @return 生成地址\n     */\n    public static String getGenPath(GenTable table, String template) {\n        String genPath = table.getGenPath();\n        if (StringUtils.equals(genPath, \"/\")) {\n            return System.getProperty(\"user.dir\") + File.separator + \"src\" + File.separator + VelocityUtils.getFileName(template, table);\n        }\n        return genPath + File.separator + VelocityUtils.getFileName(template, table);\n    }\n}\n\n"
  },
  {
    "path": "ruoyi-generator/src/main/java/top/flya/generator/service/IGenTableService.java",
    "content": "package top.flya.generator.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.generator.domain.GenTable;\nimport top.flya.generator.domain.GenTableColumn;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 业务 服务层\n *\n * @author Lion Li\n */\npublic interface IGenTableService {\n\n    /**\n     * 查询业务字段列表\n     *\n     * @param tableId 业务字段编号\n     * @return 业务字段集合\n     */\n    List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId);\n\n    /**\n     * 查询业务列表\n     *\n     * @param genTable 业务信息\n     * @return 业务集合\n     */\n    TableDataInfo<GenTable> selectPageGenTableList(GenTable genTable, PageQuery pageQuery);\n\n    /**\n     * 查询据库列表\n     *\n     * @param genTable 业务信息\n     * @return 数据库表集合\n     */\n    TableDataInfo<GenTable> selectPageDbTableList(GenTable genTable, PageQuery pageQuery);\n\n    /**\n     * 查询据库列表\n     *\n     * @param tableNames 表名称组\n     * @return 数据库表集合\n     */\n    List<GenTable> selectDbTableListByNames(String[] tableNames);\n\n    /**\n     * 查询所有表信息\n     *\n     * @return 表信息集合\n     */\n    List<GenTable> selectGenTableAll();\n\n    /**\n     * 查询业务信息\n     *\n     * @param id 业务ID\n     * @return 业务信息\n     */\n    GenTable selectGenTableById(Long id);\n\n    /**\n     * 修改业务\n     *\n     * @param genTable 业务信息\n     * @return 结果\n     */\n    void updateGenTable(GenTable genTable);\n\n    /**\n     * 删除业务信息\n     *\n     * @param tableIds 需要删除的表数据ID\n     * @return 结果\n     */\n    void deleteGenTableByIds(Long[] tableIds);\n\n    /**\n     * 导入表结构\n     *\n     * @param tableList 导入表列表\n     */\n    void importGenTable(List<GenTable> tableList);\n\n    /**\n     * 预览代码\n     *\n     * @param tableId 表编号\n     * @return 预览数据列表\n     */\n    Map<String, String> previewCode(Long tableId);\n\n    /**\n     * 生成代码（下载方式）\n     *\n     * @param tableName 表名称\n     * @return 数据\n     */\n    byte[] downloadCode(String tableName);\n\n    /**\n     * 生成代码（自定义路径）\n     *\n     * @param tableName 表名称\n     * @return 数据\n     */\n    void generatorCode(String tableName);\n\n    /**\n     * 同步数据库\n     *\n     * @param tableName 表名称\n     */\n    void synchDb(String tableName);\n\n    /**\n     * 批量生成代码（下载方式）\n     *\n     * @param tableNames 表数组\n     * @return 数据\n     */\n    byte[] downloadCode(String[] tableNames);\n\n    /**\n     * 修改保存参数校验\n     *\n     * @param genTable 业务信息\n     */\n    void validateEdit(GenTable genTable);\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/java/top/flya/generator/util/GenUtils.java",
    "content": "package top.flya.generator.util;\n\nimport top.flya.common.constant.GenConstants;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.generator.config.GenConfig;\nimport top.flya.generator.domain.GenTable;\nimport top.flya.generator.domain.GenTableColumn;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport org.apache.commons.lang3.RegExUtils;\n\nimport java.util.Arrays;\n\n/**\n * 代码生成器 工具类\n *\n * @author ruoyi\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class GenUtils {\n\n    /**\n     * 初始化表信息\n     */\n    public static void initTable(GenTable genTable, String operName) {\n        genTable.setClassName(convertClassName(genTable.getTableName()));\n        genTable.setPackageName(GenConfig.getPackageName());\n        genTable.setModuleName(getModuleName(GenConfig.getPackageName()));\n        genTable.setBusinessName(getBusinessName(genTable.getTableName()));\n        genTable.setFunctionName(replaceText(genTable.getTableComment()));\n        genTable.setFunctionAuthor(GenConfig.getAuthor());\n//        genTable.setCreateBy(operName);\n    }\n\n    /**\n     * 初始化列属性字段\n     */\n    public static void initColumnField(GenTableColumn column, GenTable table) {\n        String dataType = getDbType(column.getColumnType());\n        String columnName = column.getColumnName();\n        column.setTableId(table.getTableId());\n//        column.setCreateBy(table.getCreateBy());\n        // 设置java字段名\n        column.setJavaField(StringUtils.toCamelCase(columnName));\n        // 设置默认类型\n        column.setJavaType(GenConstants.TYPE_STRING);\n        column.setQueryType(GenConstants.QUERY_EQ);\n\n        if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) {\n            // 字符串长度超过500设置为文本域\n            Integer columnLength = getColumnLength(column.getColumnType());\n            String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT;\n            column.setHtmlType(htmlType);\n        } else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) {\n            column.setJavaType(GenConstants.TYPE_DATE);\n            column.setHtmlType(GenConstants.HTML_DATETIME);\n        } else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) {\n            column.setHtmlType(GenConstants.HTML_INPUT);\n\n            // 如果是浮点型 统一用BigDecimal\n            String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), \"(\", \")\"), StringUtils.SEPARATOR);\n            if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) {\n                column.setJavaType(GenConstants.TYPE_BIGDECIMAL);\n            }\n            // 如果是整形\n            else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) {\n                column.setJavaType(GenConstants.TYPE_INTEGER);\n            }\n            // 长整形\n            else {\n                column.setJavaType(GenConstants.TYPE_LONG);\n            }\n        }\n\n        // BO对象 默认插入勾选\n        if (!arraysContains(GenConstants.COLUMNNAME_NOT_ADD, columnName) && !column.isPk()) {\n            column.setIsInsert(GenConstants.REQUIRE);\n        }\n        // BO对象 默认编辑勾选\n        if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) {\n            column.setIsEdit(GenConstants.REQUIRE);\n        }\n        // BO对象 默认是否必填勾选\n        if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) {\n            column.setIsRequired(GenConstants.REQUIRE);\n        }\n        // VO对象 默认返回勾选\n        if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName)) {\n            column.setIsList(GenConstants.REQUIRE);\n        }\n        // BO对象 默认查询勾选\n        if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) {\n            column.setIsQuery(GenConstants.REQUIRE);\n        }\n\n        // 查询字段类型\n        if (StringUtils.endsWithIgnoreCase(columnName, \"name\")) {\n            column.setQueryType(GenConstants.QUERY_LIKE);\n        }\n        // 状态字段设置单选框\n        if (StringUtils.endsWithIgnoreCase(columnName, \"status\")) {\n            column.setHtmlType(GenConstants.HTML_RADIO);\n        }\n        // 类型&性别字段设置下拉框\n        else if (StringUtils.endsWithIgnoreCase(columnName, \"type\")\n            || StringUtils.endsWithIgnoreCase(columnName, \"sex\")) {\n            column.setHtmlType(GenConstants.HTML_SELECT);\n        }\n        // 图片字段设置图片上传控件\n        else if (StringUtils.endsWithIgnoreCase(columnName, \"image\")) {\n            column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD);\n        }\n        // 文件字段设置文件上传控件\n        else if (StringUtils.endsWithIgnoreCase(columnName, \"file\")) {\n            column.setHtmlType(GenConstants.HTML_FILE_UPLOAD);\n        }\n        // 内容字段设置富文本控件\n        else if (StringUtils.endsWithIgnoreCase(columnName, \"content\")) {\n            column.setHtmlType(GenConstants.HTML_EDITOR);\n        }\n    }\n\n    /**\n     * 校验数组是否包含指定值\n     *\n     * @param arr         数组\n     * @param targetValue 值\n     * @return 是否包含\n     */\n    public static boolean arraysContains(String[] arr, String targetValue) {\n        return Arrays.asList(arr).contains(targetValue);\n    }\n\n    /**\n     * 获取模块名\n     *\n     * @param packageName 包名\n     * @return 模块名\n     */\n    public static String getModuleName(String packageName) {\n        int lastIndex = packageName.lastIndexOf(\".\");\n        int nameLength = packageName.length();\n        return StringUtils.substring(packageName, lastIndex + 1, nameLength);\n    }\n\n    /**\n     * 获取业务名\n     *\n     * @param tableName 表名\n     * @return 业务名\n     */\n    public static String getBusinessName(String tableName) {\n        int firstIndex = tableName.indexOf(\"_\");\n        int nameLength = tableName.length();\n        String businessName = StringUtils.substring(tableName, firstIndex + 1, nameLength);\n        businessName = StringUtils.toCamelCase(businessName);\n        return businessName;\n    }\n\n    /**\n     * 表名转换成Java类名\n     *\n     * @param tableName 表名称\n     * @return 类名\n     */\n    public static String convertClassName(String tableName) {\n        boolean autoRemovePre = GenConfig.getAutoRemovePre();\n        String tablePrefix = GenConfig.getTablePrefix();\n        if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) {\n            String[] searchList = StringUtils.split(tablePrefix, StringUtils.SEPARATOR);\n            tableName = replaceFirst(tableName, searchList);\n        }\n        return StringUtils.convertToCamelCase(tableName);\n    }\n\n    /**\n     * 批量替换前缀\n     *\n     * @param replacementm 替换值\n     * @param searchList   替换列表\n     * @return\n     */\n    public static String replaceFirst(String replacementm, String[] searchList) {\n        String text = replacementm;\n        for (String searchString : searchList) {\n            if (replacementm.startsWith(searchString)) {\n                text = replacementm.replaceFirst(searchString, StringUtils.EMPTY);\n                break;\n            }\n        }\n        return text;\n    }\n\n    /**\n     * 关键字替换\n     *\n     * @param text 需要被替换的名字\n     * @return 替换后的名字\n     */\n    public static String replaceText(String text) {\n        return RegExUtils.replaceAll(text, \"(?:表|若依)\", \"\");\n    }\n\n    /**\n     * 获取数据库类型字段\n     *\n     * @param columnType 列类型\n     * @return 截取后的列类型\n     */\n    public static String getDbType(String columnType) {\n        if (StringUtils.indexOf(columnType, '(') > 0) {\n            return StringUtils.substringBefore(columnType, \"(\");\n        } else {\n            return columnType;\n        }\n    }\n\n    /**\n     * 获取字段长度\n     *\n     * @param columnType 列类型\n     * @return 截取后的列类型\n     */\n    public static Integer getColumnLength(String columnType) {\n        if (StringUtils.indexOf(columnType, '(') > 0) {\n            String length = StringUtils.substringBetween(columnType, \"(\", \")\");\n            return Integer.valueOf(length);\n        } else {\n            return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/java/top/flya/generator/util/VelocityInitializer.java",
    "content": "package top.flya.generator.util;\n\nimport top.flya.common.constant.Constants;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport org.apache.velocity.app.Velocity;\n\nimport java.util.Properties;\n\n/**\n * VelocityEngine工厂\n *\n * @author ruoyi\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class VelocityInitializer {\n\n    /**\n     * 初始化vm方法\n     */\n    public static void initVelocity() {\n        Properties p = new Properties();\n        try {\n            // 加载classpath目录下的vm文件\n            p.setProperty(\"resource.loader.file.class\", \"org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader\");\n            // 定义字符集\n            p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8);\n            // 初始化Velocity引擎，指定配置Properties\n            Velocity.init(p);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/java/top/flya/generator/util/VelocityUtils.java",
    "content": "package top.flya.generator.util;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.lang.Dict;\nimport cn.hutool.core.util.ObjectUtil;\nimport top.flya.common.constant.GenConstants;\nimport top.flya.common.helper.DataBaseHelper;\nimport top.flya.common.utils.DateUtils;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.generator.domain.GenTable;\nimport top.flya.generator.domain.GenTableColumn;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport org.apache.velocity.VelocityContext;\n\nimport java.util.*;\n\n/**\n * 模板处理工具类\n *\n * @author ruoyi\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class VelocityUtils {\n\n    /**\n     * 项目空间路径\n     */\n    private static final String PROJECT_PATH = \"main/java\";\n\n    /**\n     * mybatis空间路径\n     */\n    private static final String MYBATIS_PATH = \"main/resources/mapper\";\n\n    /**\n     * 默认上级菜单，系统工具\n     */\n    private static final String DEFAULT_PARENT_MENU_ID = \"3\";\n\n    /**\n     * 设置模板变量信息\n     *\n     * @return 模板列表\n     */\n    public static VelocityContext prepareContext(GenTable genTable) {\n        String moduleName = genTable.getModuleName();\n        String businessName = genTable.getBusinessName();\n        String packageName = genTable.getPackageName();\n        String tplCategory = genTable.getTplCategory();\n        String functionName = genTable.getFunctionName();\n\n        VelocityContext velocityContext = new VelocityContext();\n        velocityContext.put(\"tplCategory\", genTable.getTplCategory());\n        velocityContext.put(\"tableName\", genTable.getTableName());\n        velocityContext.put(\"functionName\", StringUtils.isNotEmpty(functionName) ? functionName : \"【请填写功能名称】\");\n        velocityContext.put(\"ClassName\", genTable.getClassName());\n        velocityContext.put(\"className\", StringUtils.uncapitalize(genTable.getClassName()));\n        velocityContext.put(\"moduleName\", genTable.getModuleName());\n        velocityContext.put(\"BusinessName\", StringUtils.capitalize(genTable.getBusinessName()));\n        velocityContext.put(\"businessName\", genTable.getBusinessName());\n        velocityContext.put(\"basePackage\", getPackagePrefix(packageName));\n        velocityContext.put(\"packageName\", packageName);\n        velocityContext.put(\"author\", genTable.getFunctionAuthor());\n        velocityContext.put(\"datetime\", DateUtils.getDate());\n        velocityContext.put(\"pkColumn\", genTable.getPkColumn());\n        velocityContext.put(\"importList\", getImportList(genTable));\n        velocityContext.put(\"permissionPrefix\", getPermissionPrefix(moduleName, businessName));\n        velocityContext.put(\"columns\", genTable.getColumns());\n        velocityContext.put(\"table\", genTable);\n        velocityContext.put(\"dicts\", getDicts(genTable));\n        setMenuVelocityContext(velocityContext, genTable);\n        if (GenConstants.TPL_TREE.equals(tplCategory)) {\n            setTreeVelocityContext(velocityContext, genTable);\n        }\n        if (GenConstants.TPL_SUB.equals(tplCategory)) {\n            setSubVelocityContext(velocityContext, genTable);\n        }\n        return velocityContext;\n    }\n\n    public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) {\n        String options = genTable.getOptions();\n        Dict paramsObj = JsonUtils.parseMap(options);\n        String parentMenuId = getParentMenuId(paramsObj);\n        context.put(\"parentMenuId\", parentMenuId);\n    }\n\n    public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) {\n        String options = genTable.getOptions();\n        Dict paramsObj = JsonUtils.parseMap(options);\n        String treeCode = getTreecode(paramsObj);\n        String treeParentCode = getTreeParentCode(paramsObj);\n        String treeName = getTreeName(paramsObj);\n\n        context.put(\"treeCode\", treeCode);\n        context.put(\"treeParentCode\", treeParentCode);\n        context.put(\"treeName\", treeName);\n        context.put(\"expandColumn\", getExpandColumn(genTable));\n        if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) {\n            context.put(\"tree_parent_code\", paramsObj.get(GenConstants.TREE_PARENT_CODE));\n        }\n        if (paramsObj.containsKey(GenConstants.TREE_NAME)) {\n            context.put(\"tree_name\", paramsObj.get(GenConstants.TREE_NAME));\n        }\n    }\n\n    public static void setSubVelocityContext(VelocityContext context, GenTable genTable) {\n        GenTable subTable = genTable.getSubTable();\n        String subTableName = genTable.getSubTableName();\n        String subTableFkName = genTable.getSubTableFkName();\n        String subClassName = genTable.getSubTable().getClassName();\n        String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName);\n\n        context.put(\"subTable\", subTable);\n        context.put(\"subTableName\", subTableName);\n        context.put(\"subTableFkName\", subTableFkName);\n        context.put(\"subTableFkClassName\", subTableFkClassName);\n        context.put(\"subTableFkclassName\", StringUtils.uncapitalize(subTableFkClassName));\n        context.put(\"subClassName\", subClassName);\n        context.put(\"subclassName\", StringUtils.uncapitalize(subClassName));\n        context.put(\"subImportList\", getImportList(genTable.getSubTable()));\n    }\n\n    /**\n     * 获取模板信息\n     *\n     * @return 模板列表\n     */\n    public static List<String> getTemplateList(String tplCategory) {\n        List<String> templates = new ArrayList<String>();\n        templates.add(\"vm/java/domain.java.vm\");\n        templates.add(\"vm/java/vo.java.vm\");\n        templates.add(\"vm/java/bo.java.vm\");\n        templates.add(\"vm/java/mapper.java.vm\");\n        templates.add(\"vm/java/service.java.vm\");\n        templates.add(\"vm/java/serviceImpl.java.vm\");\n        templates.add(\"vm/java/controller.java.vm\");\n        templates.add(\"vm/xml/mapper.xml.vm\");\n        if (DataBaseHelper.isOracle()) {\n            templates.add(\"vm/sql/oracle/sql.vm\");\n        } else if (DataBaseHelper.isPostgerSql()) {\n            templates.add(\"vm/sql/postgres/sql.vm\");\n        } else if (DataBaseHelper.isSqlServer()) {\n            templates.add(\"vm/sql/sqlserver/sql.vm\");\n        } else {\n            templates.add(\"vm/sql/sql.vm\");\n        }\n        templates.add(\"vm/js/api.js.vm\");\n        if (GenConstants.TPL_CRUD.equals(tplCategory)) {\n            templates.add(\"vm/vue/index.vue.vm\");\n        } else if (GenConstants.TPL_TREE.equals(tplCategory)) {\n            templates.add(\"vm/vue/index-tree.vue.vm\");\n        } else if (GenConstants.TPL_SUB.equals(tplCategory)) {\n            templates.add(\"vm/vue/index.vue.vm\");\n            templates.add(\"vm/java/sub-domain.java.vm\");\n        }\n        return templates;\n    }\n\n    /**\n     * 获取文件名\n     */\n    public static String getFileName(String template, GenTable genTable) {\n        // 文件名称\n        String fileName = \"\";\n        // 包路径\n        String packageName = genTable.getPackageName();\n        // 模块名\n        String moduleName = genTable.getModuleName();\n        // 大写类名\n        String className = genTable.getClassName();\n        // 业务名称\n        String businessName = genTable.getBusinessName();\n\n        String javaPath = PROJECT_PATH + \"/\" + StringUtils.replace(packageName, \".\", \"/\");\n        String mybatisPath = MYBATIS_PATH + \"/\" + moduleName;\n        String vuePath = \"vue\";\n\n        if (template.contains(\"domain.java.vm\")) {\n            fileName = StringUtils.format(\"{}/domain/{}.java\", javaPath, className);\n        }\n        if (template.contains(\"vo.java.vm\")) {\n            fileName = StringUtils.format(\"{}/domain/vo/{}Vo.java\", javaPath, className);\n        }\n        if (template.contains(\"bo.java.vm\")) {\n            fileName = StringUtils.format(\"{}/domain/bo/{}Bo.java\", javaPath, className);\n        }\n        if (template.contains(\"sub-domain.java.vm\") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) {\n            fileName = StringUtils.format(\"{}/domain/{}.java\", javaPath, genTable.getSubTable().getClassName());\n        } else if (template.contains(\"mapper.java.vm\")) {\n            fileName = StringUtils.format(\"{}/mapper/{}Mapper.java\", javaPath, className);\n        } else if (template.contains(\"service.java.vm\")) {\n            fileName = StringUtils.format(\"{}/service/I{}Service.java\", javaPath, className);\n        } else if (template.contains(\"serviceImpl.java.vm\")) {\n            fileName = StringUtils.format(\"{}/service/impl/{}ServiceImpl.java\", javaPath, className);\n        } else if (template.contains(\"controller.java.vm\")) {\n            fileName = StringUtils.format(\"{}/controller/{}Controller.java\", javaPath, className);\n        } else if (template.contains(\"mapper.xml.vm\")) {\n            fileName = StringUtils.format(\"{}/{}Mapper.xml\", mybatisPath, className);\n        } else if (template.contains(\"sql.vm\")) {\n            fileName = businessName + \"Menu.sql\";\n        } else if (template.contains(\"api.js.vm\")) {\n            fileName = StringUtils.format(\"{}/api/{}/{}.js\", vuePath, moduleName, businessName);\n        } else if (template.contains(\"index.vue.vm\")) {\n            fileName = StringUtils.format(\"{}/views/{}/{}/index.vue\", vuePath, moduleName, businessName);\n        } else if (template.contains(\"index-tree.vue.vm\")) {\n            fileName = StringUtils.format(\"{}/views/{}/{}/index.vue\", vuePath, moduleName, businessName);\n        }\n        return fileName;\n    }\n\n    /**\n     * 获取包前缀\n     *\n     * @param packageName 包名称\n     * @return 包前缀名称\n     */\n    public static String getPackagePrefix(String packageName) {\n        int lastIndex = packageName.lastIndexOf(\".\");\n        return StringUtils.substring(packageName, 0, lastIndex);\n    }\n\n    /**\n     * 根据列类型获取导入包\n     *\n     * @param genTable 业务表对象\n     * @return 返回需要导入的包列表\n     */\n    public static HashSet<String> getImportList(GenTable genTable) {\n        List<GenTableColumn> columns = genTable.getColumns();\n        GenTable subGenTable = genTable.getSubTable();\n        HashSet<String> importList = new HashSet<>();\n        if (ObjectUtil.isNotNull(subGenTable)) {\n            importList.add(\"java.util.List\");\n        }\n        for (GenTableColumn column : columns) {\n            if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) {\n                importList.add(\"java.util.Date\");\n                importList.add(\"com.fasterxml.jackson.annotation.JsonFormat\");\n            } else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) {\n                importList.add(\"java.math.BigDecimal\");\n            }\n        }\n        return importList;\n    }\n\n    /**\n     * 根据列类型获取字典组\n     *\n     * @param genTable 业务表对象\n     * @return 返回字典组\n     */\n    public static String getDicts(GenTable genTable) {\n        List<GenTableColumn> columns = genTable.getColumns();\n        Set<String> dicts = new HashSet<String>();\n        addDicts(dicts, columns);\n        if (ObjectUtil.isNotNull(genTable.getSubTable())) {\n            List<GenTableColumn> subColumns = genTable.getSubTable().getColumns();\n            addDicts(dicts, subColumns);\n        }\n        return StringUtils.join(dicts, \", \");\n    }\n\n    /**\n     * 添加字典列表\n     *\n     * @param dicts 字典列表\n     * @param columns 列集合\n     */\n    public static void addDicts(Set<String> dicts, List<GenTableColumn> columns) {\n        for (GenTableColumn column : columns) {\n            if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny(\n                column.getHtmlType(),\n                new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })) {\n                dicts.add(\"'\" + column.getDictType() + \"'\");\n            }\n        }\n    }\n\n    /**\n     * 获取权限前缀\n     *\n     * @param moduleName   模块名称\n     * @param businessName 业务名称\n     * @return 返回权限前缀\n     */\n    public static String getPermissionPrefix(String moduleName, String businessName) {\n        return StringUtils.format(\"{}:{}\", moduleName, businessName);\n    }\n\n    /**\n     * 获取上级菜单ID字段\n     *\n     * @param paramsObj 生成其他选项\n     * @return 上级菜单ID字段\n     */\n    public static String getParentMenuId(Dict paramsObj) {\n        if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID)\n            && StringUtils.isNotEmpty(paramsObj.getStr(GenConstants.PARENT_MENU_ID))) {\n            return paramsObj.getStr(GenConstants.PARENT_MENU_ID);\n        }\n        return DEFAULT_PARENT_MENU_ID;\n    }\n\n    /**\n     * 获取树编码\n     *\n     * @param paramsObj 生成其他选项\n     * @return 树编码\n     */\n    public static String getTreecode(Map<String, Object> paramsObj) {\n        if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_CODE)) {\n            return StringUtils.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_CODE)));\n        }\n        return StringUtils.EMPTY;\n    }\n\n    /**\n     * 获取树父编码\n     *\n     * @param paramsObj 生成其他选项\n     * @return 树父编码\n     */\n    public static String getTreeParentCode(Dict paramsObj) {\n        if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) {\n            return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_PARENT_CODE));\n        }\n        return StringUtils.EMPTY;\n    }\n\n    /**\n     * 获取树名称\n     *\n     * @param paramsObj 生成其他选项\n     * @return 树名称\n     */\n    public static String getTreeName(Dict paramsObj) {\n        if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_NAME)) {\n            return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_NAME));\n        }\n        return StringUtils.EMPTY;\n    }\n\n    /**\n     * 获取需要在哪一列上面显示展开按钮\n     *\n     * @param genTable 业务表对象\n     * @return 展开按钮列序号\n     */\n    public static int getExpandColumn(GenTable genTable) {\n        String options = genTable.getOptions();\n        Dict paramsObj = JsonUtils.parseMap(options);\n        String treeName = paramsObj.getStr(GenConstants.TREE_NAME);\n        int num = 0;\n        for (GenTableColumn column : genTable.getColumns()) {\n            if (column.isList()) {\n                num++;\n                String columnName = column.getColumnName();\n                if (columnName.equals(treeName)) {\n                    break;\n                }\n            }\n        }\n        return num;\n    }\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/generator.yml",
    "content": "# 代码生成\ngen:\n  # 作者\n  author: ruoyi\n  # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool\n  packageName: top.flya.system\n  # 自动去除表前缀，默认是false\n  autoRemovePre: false\n  # 表前缀（生成类名不会包含表前缀，多个用逗号分隔）\n  tablePrefix: sys_\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.generator.mapper.GenTableColumnMapper\">\n\n    <resultMap type=\"GenTableColumn\" id=\"GenTableColumnResult\">\n        <id     property=\"columnId\"       column=\"column_id\"      />\n        <result property=\"tableId\"        column=\"table_id\"       />\n        <result property=\"columnName\"     column=\"column_name\"    />\n        <result property=\"columnComment\"  column=\"column_comment\" />\n        <result property=\"columnType\"     column=\"column_type\"    />\n        <result property=\"javaType\"       column=\"java_type\"      />\n        <result property=\"javaField\"      column=\"java_field\"     />\n        <result property=\"isPk\"           column=\"is_pk\"          />\n        <result property=\"isIncrement\"    column=\"is_increment\"   />\n        <result property=\"isRequired\"     column=\"is_required\"    />\n        <result property=\"isInsert\"       column=\"is_insert\"      />\n        <result property=\"isEdit\"         column=\"is_edit\"        />\n        <result property=\"isList\"         column=\"is_list\"        />\n        <result property=\"isQuery\"        column=\"is_query\"       />\n        <result property=\"queryType\"      column=\"query_type\"     />\n        <result property=\"htmlType\"       column=\"html_type\"      />\n        <result property=\"dictType\"       column=\"dict_type\"      />\n        <result property=\"sort\"           column=\"sort\"           />\n        <result property=\"createBy\"       column=\"create_by\"      />\n        <result property=\"createTime\"     column=\"create_time\"    />\n        <result property=\"updateBy\"       column=\"update_by\"      />\n        <result property=\"updateTime\"     column=\"update_time\"    />\n    </resultMap>\n\n    <select id=\"selectDbTableColumnsByName\" parameterType=\"String\" resultMap=\"GenTableColumnResult\">\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isMySql()\">\n            select column_name,\n                   (case when (is_nullable = 'no' <![CDATA[ && ]]> column_key != 'PRI') then '1' else null end) as is_required,\n                   (case when column_key = 'PRI' then '1' else '0' end) as is_pk,\n                   ordinal_position as sort,\n                   column_comment,\n                   (case when extra = 'auto_increment' then '1' else '0' end) as is_increment,\n                   column_type\n            from information_schema.columns where table_schema = (select database()) and table_name = (#{tableName})\n            order by ordinal_position\n        </if>\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isOracle()\">\n            select lower(temp.column_name) as column_name,\n                    (case when (temp.nullable = 'N'  and  temp.constraint_type != 'P') then '1' else null end) as is_required,\n                    (case when temp.constraint_type = 'P' then '1' else '0' end) as is_pk,\n                    temp.column_id as sort,\n                    temp.comments as column_comment,\n                    (case when temp.constraint_type = 'P' then '1' else '0' end) as is_increment,\n                    lower(temp.data_type) as column_type\n            from (\n                select col.column_id, col.column_name,col.nullable, col.data_type, colc.comments, uc.constraint_type, row_number()\n                    over (partition by col.column_name order by uc.constraint_type desc) as row_flg\n                from user_tab_columns col\n                left join user_col_comments colc on colc.table_name = col.table_name and colc.column_name = col.column_name\n                left join user_cons_columns ucc on ucc.table_name = col.table_name and ucc.column_name = col.column_name\n                left join user_constraints uc on uc.constraint_name = ucc.constraint_name\n                where col.table_name = upper(#{tableName})\n            ) temp\n            WHERE temp.row_flg = 1\n            ORDER BY temp.column_id\n        </if>\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isPostgerSql()\">\n            SELECT column_name, is_required, is_pk, sort, column_comment, is_increment, column_type\n            FROM (\n                SELECT c.relname AS table_name,\n                       a.attname AS column_name,\n                       d.description AS column_comment,\n                       CASE WHEN a.attnotnull AND con.conname IS NULL THEN 1 ELSE 0\n                       END AS is_required,\n                       CASE WHEN con.conname IS NOT NULL THEN 1 ELSE 0\n                       END AS is_pk,\n                       a.attnum AS sort,\n                       CASE WHEN \"position\"(pg_get_expr(ad.adbin, ad.adrelid),\n                           ((c.relname::text || '_'::text) || a.attname::text) || '_seq'::text) > 0 THEN 1 ELSE 0\n                       END AS is_increment,\n                       btrim(\n                           CASE WHEN t.typelem <![CDATA[ <> ]]> 0::oid AND t.typlen = '-1'::integer THEN 'ARRAY'::text ELSE\n                                CASE WHEN t.typtype = 'd'::\"char\" THEN format_type(t.typbasetype, NULL::integer)\n                                ELSE format_type(a.atttypid, NULL::integer) END\n                           END, '\"'::text\n                       ) AS column_type\n                FROM pg_attribute a\n                    JOIN (pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid) ON a.attrelid = c.oid\n                    LEFT JOIN pg_description d ON d.objoid = c.oid AND a.attnum = d.objsubid\n                    LEFT JOIN pg_constraint con ON con.conrelid = c.oid AND (a.attnum = ANY (con.conkey))\n                    LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum\n                    LEFT JOIN pg_type t ON a.atttypid = t.oid\n                WHERE (c.relkind = ANY (ARRAY ['r'::\"char\", 'p'::\"char\"]))\n                    AND a.attnum > 0\n                    AND n.nspname = 'public'::name\n                ORDER BY c.relname, a.attnum\n            ) temp\n            WHERE table_name = (#{tableName})\n                AND column_type <![CDATA[ <> ]]> '-'\n        </if>\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isSqlServer()\">\n            SELECT\n                cast(A.NAME as nvarchar) as column_name,\n                cast(B.NAME as nvarchar) + (case when B.NAME = 'numeric' then '(' + cast(A.prec as nvarchar) + ',' + cast(A.scale as nvarchar) + ')' else '' end) as column_type,\n                cast(G.[VALUE] as nvarchar) as column_comment,\n                (SELECT 1 FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE Z WHERE TABLE_NAME = D.NAME and A.NAME = Z.column_name  ) as is_pk,\n                colorder as sort\n            FROM SYSCOLUMNS A\n                LEFT JOIN SYSTYPES B ON A.XTYPE = B.XUSERTYPE\n                INNER JOIN SYSOBJECTS D ON A.ID = D.ID AND D.XTYPE='U' AND D.NAME != 'DTPROPERTIES'\n                LEFT JOIN SYS.EXTENDED_PROPERTIES G ON A.ID = G.MAJOR_ID AND A.COLID = G.MINOR_ID\n                LEFT JOIN SYS.EXTENDED_PROPERTIES F ON D.ID = F.MAJOR_ID AND F.MINOR_ID = 0\n            WHERE D.NAME = #{tableName}\n            ORDER BY A.COLORDER\n        </if>\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.generator.mapper.GenTableMapper\">\n\n    <resultMap type=\"GenTable\" id=\"GenTableResult\">\n        <id     property=\"tableId\"        column=\"table_id\"          />\n        <result property=\"tableName\"      column=\"table_name\"        />\n        <result property=\"tableComment\"   column=\"table_comment\"     />\n        <result property=\"subTableName\"   column=\"sub_table_name\"    />\n        <result property=\"subTableFkName\" column=\"sub_table_fk_name\" />\n        <result property=\"className\"      column=\"class_name\"        />\n        <result property=\"tplCategory\"    column=\"tpl_category\"      />\n        <result property=\"packageName\"    column=\"package_name\"      />\n        <result property=\"moduleName\"     column=\"module_name\"       />\n        <result property=\"businessName\"   column=\"business_name\"     />\n        <result property=\"functionName\"   column=\"function_name\"     />\n        <result property=\"functionAuthor\" column=\"function_author\"   />\n        <result property=\"genType\"        column=\"gen_type\"          />\n        <result property=\"genPath\"        column=\"gen_path\"          />\n        <result property=\"options\"        column=\"options\"           />\n        <result property=\"createBy\"       column=\"create_by\"         />\n        <result property=\"createTime\"     column=\"create_time\"       />\n        <result property=\"updateBy\"       column=\"update_by\"         />\n        <result property=\"updateTime\"     column=\"update_time\"       />\n        <result property=\"remark\"         column=\"remark\"            />\n        <collection  property=\"columns\"  javaType=\"java.util.List\"  resultMap=\"GenTableColumnResult\" />\n    </resultMap>\n\n    <resultMap type=\"GenTableColumn\" id=\"GenTableColumnResult\">\n        <id     property=\"columnId\"       column=\"column_id\"      />\n        <result property=\"tableId\"        column=\"table_id\"       />\n        <result property=\"columnName\"     column=\"column_name\"    />\n        <result property=\"columnComment\"  column=\"column_comment\" />\n        <result property=\"columnType\"     column=\"column_type\"    />\n        <result property=\"javaType\"       column=\"java_type\"      />\n        <result property=\"javaField\"      column=\"java_field\"     />\n        <result property=\"isPk\"           column=\"is_pk\"          />\n        <result property=\"isIncrement\"    column=\"is_increment\"   />\n        <result property=\"isRequired\"     column=\"is_required\"    />\n        <result property=\"isInsert\"       column=\"is_insert\"      />\n        <result property=\"isEdit\"         column=\"is_edit\"        />\n        <result property=\"isList\"         column=\"is_list\"        />\n        <result property=\"isQuery\"        column=\"is_query\"       />\n        <result property=\"queryType\"      column=\"query_type\"     />\n        <result property=\"htmlType\"       column=\"html_type\"      />\n        <result property=\"dictType\"       column=\"dict_type\"      />\n        <result property=\"sort\"           column=\"sort\"           />\n        <result property=\"createBy\"       column=\"create_by\"      />\n        <result property=\"createTime\"     column=\"create_time\"    />\n        <result property=\"updateBy\"       column=\"update_by\"      />\n        <result property=\"updateTime\"     column=\"update_time\"    />\n    </resultMap>\n\n    <select id=\"selectPageDbTableList\" resultMap=\"GenTableResult\">\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isMySql()\">\n            select table_name, table_comment, create_time, update_time\n            from information_schema.tables\n            where table_schema = (select database())\n            AND table_name NOT LIKE 'xxl_job_%' AND table_name NOT LIKE 'gen_%'\n            AND table_name NOT IN (select table_name from gen_table)\n            <if test=\"genTable.tableName != null and genTable.tableName != ''\">\n                AND lower(table_name) like lower(concat('%', #{genTable.tableName}, '%'))\n            </if>\n            <if test=\"genTable.tableComment != null and genTable.tableComment != ''\">\n                AND lower(table_comment) like lower(concat('%', #{genTable.tableComment}, '%'))\n            </if>\n            order by create_time desc\n        </if>\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isOracle()\">\n            select lower(dt.table_name) as table_name, dtc.comments as table_comment, uo.created as create_time, uo.last_ddl_time as update_time\n            from user_tables dt, user_tab_comments dtc, user_objects uo\n            where dt.table_name = dtc.table_name\n            and dt.table_name = uo.object_name\n            and uo.object_type = 'TABLE'\n            AND dt.table_name NOT LIKE 'XXL_JOB_%' AND dt.table_name NOT LIKE 'GEN_%'\n            AND lower(dt.table_name) NOT IN (select table_name from gen_table)\n            <if test=\"genTable.tableName != null and genTable.tableName != ''\">\n                AND lower(dt.table_name) like lower(concat(concat('%', #{genTable.tableName}), '%'))\n            </if>\n            <if test=\"genTable.tableComment != null and genTable.tableComment != ''\">\n                AND lower(dtc.comments) like lower(concat(concat('%', #{genTable.tableComment}), '%'))\n            </if>\n            order by create_time desc\n        </if>\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isPostgerSql()\">\n            select table_name, table_comment, create_time, update_time\n            from (\n                SELECT c.relname AS table_name,\n                        obj_description(c.oid) AS table_comment,\n                        CURRENT_TIMESTAMP AS create_time,\n                        CURRENT_TIMESTAMP AS update_time\n                FROM pg_class c\n                    LEFT JOIN pg_namespace n ON n.oid = c.relnamespace\n                WHERE (c.relkind = ANY (ARRAY ['r'::\"char\", 'p'::\"char\"]))\n                    AND c.relname != 'spatial_%'::text\n                    AND n.nspname = 'public'::name\n                    AND n.nspname <![CDATA[ <> ]]> ''::name\n            ) list_table\n            where table_name NOT LIKE 'xxl_job_%' AND table_name NOT LIKE 'gen_%'\n            AND table_name NOT IN (select table_name from gen_table)\n            <if test=\"genTable.tableName != null and genTable.tableName != ''\">\n                AND lower(table_name) like lower(concat('%', #{genTable.tableName}, '%'))\n            </if>\n            <if test=\"genTable.tableComment != null and genTable.tableComment != ''\">\n                AND lower(table_comment) like lower(concat('%', #{genTable.tableComment}, '%'))\n            </if>\n            order by create_time desc\n        </if>\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isSqlServer()\">\n            SELECT cast(D.NAME as nvarchar) as table_name,\n                   cast(F.VALUE as nvarchar) as table_comment,\n                   crdate as create_time,\n                   refdate as update_time\n            FROM SYSOBJECTS D\n                INNER JOIN SYS.EXTENDED_PROPERTIES F ON D.ID = F.MAJOR_ID\n                    AND F.MINOR_ID = 0 AND D.XTYPE = 'U' AND D.NAME != 'DTPROPERTIES'\n                    AND D.NAME NOT LIKE 'xxl_job_%' AND D.NAME NOT LIKE 'gen_%'\n                    AND D.NAME NOT IN (select table_name from gen_table)\n            <if test=\"genTable.tableName != null and genTable.tableName != ''\">\n                AND lower(D.NAME) like lower(concat(N'%', N'${genTable.tableName}', N'%'))\n            </if>\n            <if test=\"genTable.tableComment != null and genTable.tableComment != ''\">\n                AND lower(CAST(F.VALUE AS nvarchar)) like lower(concat(N'%', N'${genTable.tableComment}', N'%'))\n            </if>\n            order by crdate desc\n        </if>\n    </select>\n\n    <select id=\"selectDbTableListByNames\" resultMap=\"GenTableResult\">\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isMySql()\">\n            select table_name, table_comment, create_time, update_time from information_schema.tables\n            where table_name NOT LIKE 'xxl_job_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database())\n            and table_name in\n            <foreach collection=\"array\" item=\"name\" open=\"(\" separator=\",\" close=\")\">\n                 #{name}\n            </foreach>\n        </if>\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isOracle()\">\n            select lower(dt.table_name) as table_name, dtc.comments as table_comment, uo.created as create_time, uo.last_ddl_time as update_time\n            from user_tables dt, user_tab_comments dtc, user_objects uo\n            where dt.table_name = dtc.table_name\n            and dt.table_name = uo.object_name\n            and uo.object_type = 'TABLE'\n            AND dt.table_name NOT LIKE 'XXL_JOB_%' AND dt.table_name NOT LIKE 'GEN_%'\n            AND dt.table_name NOT IN (select table_name from gen_table)\n            and lower(dt.table_name) in\n            <foreach collection=\"array\" item=\"name\" open=\"(\" separator=\",\" close=\")\">\n                #{name}\n            </foreach>\n        </if>\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isPostgerSql()\">\n            select table_name, table_comment, create_time, update_time\n            from (\n                SELECT c.relname AS table_name,\n                        obj_description(c.oid) AS table_comment,\n                        CURRENT_TIMESTAMP AS create_time,\n                        CURRENT_TIMESTAMP AS update_time\n                FROM pg_class c\n                    LEFT JOIN pg_namespace n ON n.oid = c.relnamespace\n                WHERE (c.relkind = ANY (ARRAY ['r'::\"char\", 'p'::\"char\"]))\n                    AND c.relname != 'spatial_%'::text\n                    AND n.nspname = 'public'::name\n                    AND n.nspname <![CDATA[ <> ]]> ''::name\n            ) list_table\n            where table_name NOT LIKE 'xxl_job_%' and table_name NOT LIKE 'gen_%'\n            and table_name in\n            <foreach collection=\"array\" item=\"name\" open=\"(\" separator=\",\" close=\")\">\n                #{name}\n            </foreach>\n        </if>\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isSqlServer()\">\n            SELECT cast(D.NAME as nvarchar) as table_name,\n                   cast(F.VALUE as nvarchar) as table_comment,\n                   crdate as create_time,\n                   refdate as update_time\n            FROM SYSOBJECTS D\n                INNER JOIN SYS.EXTENDED_PROPERTIES F ON D.ID = F.MAJOR_ID\n                    AND F.MINOR_ID = 0 AND D.XTYPE = 'U' AND D.NAME != 'DTPROPERTIES'\n                    AND D.NAME NOT LIKE 'xxl_job_%' AND D.NAME NOT LIKE 'gen_%'\n                    AND D.NAME in\n            <foreach collection=\"array\" item=\"name\" open=\"(\" separator=\",\" close=\")\">\n                #{name}\n            </foreach>\n        </if>\n    </select>\n\n    <select id=\"selectTableByName\" parameterType=\"String\" resultMap=\"GenTableResult\">\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isMySql()\">\n            select table_name, table_comment, create_time, update_time from information_schema.tables\n            where table_name NOT LIKE 'xxl_job_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database())\n            and table_name = #{tableName}\n        </if>\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isOracle()\">\n            select lower(dt.table_name) as table_name, dtc.comments as table_comment, uo.created as create_time, uo.last_ddl_time as update_time\n            from user_tables dt, user_tab_comments dtc, user_objects uo\n            where dt.table_name = dtc.table_name\n            and dt.table_name = uo.object_name\n            and uo.object_type = 'TABLE'\n            AND dt.table_name NOT LIKE 'XXL_JOB_%' AND dt.table_name NOT LIKE 'GEN_%'\n            AND dt.table_name NOT IN (select table_name from gen_table)\n            and lower(dt.table_name) = #{tableName}\n        </if>\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isPostgerSql()\">\n            select table_name, table_comment, create_time, update_time\n            from (\n                SELECT c.relname AS table_name,\n                        obj_description(c.oid) AS table_comment,\n                        CURRENT_TIMESTAMP AS create_time,\n                        CURRENT_TIMESTAMP AS update_time\n                FROM pg_class c\n                    LEFT JOIN pg_namespace n ON n.oid = c.relnamespace\n                WHERE (c.relkind = ANY (ARRAY ['r'::\"char\", 'p'::\"char\"]))\n                    AND c.relname != 'spatial_%'::text\n                    AND n.nspname = 'public'::name\n                    AND n.nspname <![CDATA[ <> ]]> ''::name\n            ) list_table\n            where table_name NOT LIKE 'xxl_job_%' and table_name NOT LIKE 'gen_%'\n            and table_name = #{tableName}\n        </if>\n        <if test=\"@top.flya.common.helper.DataBaseHelper@isSqlServer()\">\n            SELECT cast(D.NAME as nvarchar) as table_name,\n                   cast(F.VALUE as nvarchar) as table_comment,\n                   crdate as create_time,\n                   refdate as update_time\n            FROM SYSOBJECTS D\n                INNER JOIN SYS.EXTENDED_PROPERTIES F ON D.ID = F.MAJOR_ID\n                    AND F.MINOR_ID = 0 AND D.XTYPE = 'U' AND D.NAME != 'DTPROPERTIES'\n                    AND D.NAME NOT LIKE 'xxl_job_%' AND D.NAME NOT LIKE 'gen_%'\n                    AND D.NAME = #{tableName}\n        </if>\n    </select>\n\n    <select id=\"selectGenTableById\" parameterType=\"Long\" resultMap=\"GenTableResult\">\n        SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,\n               c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort\n        FROM gen_table t\n             LEFT JOIN gen_table_column c ON t.table_id = c.table_id\n        where t.table_id = #{tableId} order by c.sort\n    </select>\n\n    <select id=\"selectGenTableByName\" parameterType=\"String\" resultMap=\"GenTableResult\">\n        SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,\n               c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort\n        FROM gen_table t\n             LEFT JOIN gen_table_column c ON t.table_id = c.table_id\n        where t.table_name = #{tableName} order by c.sort\n    </select>\n\n    <select id=\"selectGenTableAll\" parameterType=\"String\" resultMap=\"GenTableResult\">\n        SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.options, t.remark,\n               c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort\n        FROM gen_table t\n             LEFT JOIN gen_table_column c ON t.table_id = c.table_id\n        order by c.sort\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/mapper/package-info.md",
    "content": "java包使用 `.` 分割 resource 目录使用 `/` 分割\n<br>\n此文件目的 防止文件夹粘连找不到 `xml` 文件"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/java/bo.java.vm",
    "content": "package ${packageName}.domain.bo;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport javax.validation.constraints.*;\n\n#foreach ($import in $importList)\nimport ${import};\n#end\n#if($table.crud || $table.sub)\n#elseif($table.tree)\n#end\n\n/**\n * ${functionName}业务对象 ${tableName}\n *\n * @author ${author}\n * @date ${datetime}\n */\n#if($table.crud || $table.sub)\n#set($Entity=\"BaseEntity\")\n#elseif($table.tree)\n#set($Entity=\"TreeEntity<${ClassName}Bo>\")\n#end\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class ${ClassName}Bo extends ${Entity} {\n\n#foreach ($column in $columns)\n#if(!$table.isSuperColumn($column.javaField) && ($column.query || $column.insert || $column.edit))\n    /**\n     * $column.columnComment\n     */\n#if($column.insert && $column.edit)\n#set($Group=\"AddGroup.class, EditGroup.class\")\n#elseif($column.insert)\n#set($Group=\"AddGroup.class\")\n#elseif($column.edit)\n#set($Group=\"EditGroup.class\")\n#end\n#if($column.required)\n#if($column.javaType == 'String')\n    @NotBlank(message = \"$column.columnComment不能为空\", groups = { $Group })\n#else\n    @NotNull(message = \"$column.columnComment不能为空\", groups = { $Group })\n#end\n#end\n    private $column.javaType $column.javaField;\n\n#end\n#end\n\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/java/controller.java.vm",
    "content": "package ${packageName}.controller;\n\nimport java.util.List;\nimport java.util.Arrays;\n\nimport lombok.RequiredArgsConstructor;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.*;\nimport cn.dev33.satoken.annotation.SaCheckPermission;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.validation.annotation.Validated;\nimport top.flya.common.annotation.RepeatSubmit;\nimport top.flya.common.annotation.Log;\nimport top.flya.common.core.controller.BaseController;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport top.flya.common.enums.BusinessType;\nimport top.flya.common.utils.poi.ExcelUtil;\nimport ${packageName}.domain.vo.${ClassName}Vo;\nimport ${packageName}.domain.bo.${ClassName}Bo;\nimport ${packageName}.service.I${ClassName}Service;\n#if($table.crud || $table.sub)\nimport top.flya.common.core.page.TableDataInfo;\n#elseif($table.tree)\n#end\n\n/**\n * ${functionName}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Validated\n@RequiredArgsConstructor\n@RestController\n@RequestMapping(\"/${moduleName}/${businessName}\")\npublic class ${ClassName}Controller extends BaseController {\n\n    private final I${ClassName}Service i${ClassName}Service;\n\n    /**\n     * 查询${functionName}列表\n     */\n    @SaCheckPermission(\"${permissionPrefix}:list\")\n    @GetMapping(\"/list\")\n#if($table.crud || $table.sub)\n    public TableDataInfo<${ClassName}Vo> list(${ClassName}Bo bo, PageQuery pageQuery) {\n        return i${ClassName}Service.queryPageList(bo, pageQuery);\n    }\n#elseif($table.tree)\n    public R<List<${ClassName}Vo>> list(${ClassName}Bo bo) {\n        List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo);\n        return R.ok(list);\n    }\n#end\n\n    /**\n     * 导出${functionName}列表\n     */\n    @SaCheckPermission(\"${permissionPrefix}:export\")\n    @Log(title = \"${functionName}\", businessType = BusinessType.EXPORT)\n    @PostMapping(\"/export\")\n    public void export(${ClassName}Bo bo, HttpServletResponse response) {\n        List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo);\n        ExcelUtil.exportExcel(list, \"${functionName}\", ${ClassName}Vo.class, response);\n    }\n\n    /**\n     * 获取${functionName}详细信息\n     *\n     * @param ${pkColumn.javaField} 主键\n     */\n    @SaCheckPermission(\"${permissionPrefix}:query\")\n    @GetMapping(\"/{${pkColumn.javaField}}\")\n    public R<${ClassName}Vo> getInfo(@NotNull(message = \"主键不能为空\")\n                                     @PathVariable ${pkColumn.javaType} ${pkColumn.javaField}) {\n        return R.ok(i${ClassName}Service.queryById(${pkColumn.javaField}));\n    }\n\n    /**\n     * 新增${functionName}\n     */\n    @SaCheckPermission(\"${permissionPrefix}:add\")\n    @Log(title = \"${functionName}\", businessType = BusinessType.INSERT)\n    @RepeatSubmit()\n    @PostMapping()\n    public R<Void> add(@Validated(AddGroup.class) @RequestBody ${ClassName}Bo bo) {\n        return toAjax(i${ClassName}Service.insertByBo(bo));\n    }\n\n    /**\n     * 修改${functionName}\n     */\n    @SaCheckPermission(\"${permissionPrefix}:edit\")\n    @Log(title = \"${functionName}\", businessType = BusinessType.UPDATE)\n    @RepeatSubmit()\n    @PutMapping()\n    public R<Void> edit(@Validated(EditGroup.class) @RequestBody ${ClassName}Bo bo) {\n        return toAjax(i${ClassName}Service.updateByBo(bo));\n    }\n\n    /**\n     * 删除${functionName}\n     *\n     * @param ${pkColumn.javaField}s 主键串\n     */\n    @SaCheckPermission(\"${permissionPrefix}:remove\")\n    @Log(title = \"${functionName}\", businessType = BusinessType.DELETE)\n    @DeleteMapping(\"/{${pkColumn.javaField}s}\")\n    public R<Void> remove(@NotEmpty(message = \"主键不能为空\")\n                          @PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) {\n        return toAjax(i${ClassName}Service.deleteWithValidByIds(Arrays.asList(${pkColumn.javaField}s), true));\n    }\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/java/domain.java.vm",
    "content": "package ${packageName}.domain;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n#foreach ($import in $importList)\nimport ${import};\n#end\n#if($table.crud || $table.sub)\n#elseif($table.tree)\n#end\n\n/**\n * ${functionName}对象 ${tableName}\n *\n * @author ${author}\n * @date ${datetime}\n */\n#if($table.crud || $table.sub)\n    #set($Entity=\"BaseEntity\")\n#elseif($table.tree)\n    #set($Entity=\"TreeEntity<${ClassName}>\")\n#end\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"${tableName}\")\npublic class ${ClassName} extends ${Entity} {\n\n    private static final long serialVersionUID=1L;\n\n#foreach ($column in $columns)\n#if(!$table.isSuperColumn($column.javaField))\n    /**\n     * $column.columnComment\n     */\n#if($column.javaField=='delFlag')\n    @TableLogic\n#end\n#if($column.javaField=='version')\n    @Version\n#end\n#if($column.pk)\n    @TableId(value = \"$column.columnName\")\n#end\n    private $column.javaType $column.javaField;\n#end\n#end\n\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/java/mapper.java.vm",
    "content": "package ${packageName}.mapper;\n\nimport ${packageName}.domain.${ClassName};\nimport ${packageName}.domain.vo.${ClassName}Vo;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * ${functionName}Mapper接口\n *\n * @author ${author}\n * @date ${datetime}\n */\npublic interface ${ClassName}Mapper extends BaseMapperPlus<${ClassName}Mapper, ${ClassName}, ${ClassName}Vo> {\n\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/java/service.java.vm",
    "content": "package ${packageName}.service;\n\nimport ${packageName}.domain.${ClassName};\nimport ${packageName}.domain.vo.${ClassName}Vo;\nimport ${packageName}.domain.bo.${ClassName}Bo;\n#if($table.crud || $table.sub)\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\n#end\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ${functionName}Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\npublic interface I${ClassName}Service {\n\n    /**\n     * 查询${functionName}\n     */\n    ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField});\n\n#if($table.crud || $table.sub)\n    /**\n     * 查询${functionName}列表\n     */\n    TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery);\n#end\n\n    /**\n     * 查询${functionName}列表\n     */\n    List<${ClassName}Vo> queryList(${ClassName}Bo bo);\n\n    /**\n     * 新增${functionName}\n     */\n    Boolean insertByBo(${ClassName}Bo bo);\n\n    /**\n     * 修改${functionName}\n     */\n    Boolean updateByBo(${ClassName}Bo bo);\n\n    /**\n     * 校验并批量删除${functionName}信息\n     */\n    Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid);\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm",
    "content": "package ${packageName}.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\n    #if($table.crud || $table.sub)\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.domain.PageQuery;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\n#end\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport ${packageName}.domain.bo.${ClassName}Bo;\nimport ${packageName}.domain.vo.${ClassName}Vo;\nimport ${packageName}.domain.${ClassName};\nimport ${packageName}.mapper.${ClassName}Mapper;\nimport ${packageName}.service.I${ClassName}Service;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Collection;\n\n/**\n * ${functionName}Service业务层处理\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RequiredArgsConstructor\n@Service\npublic class ${ClassName}ServiceImpl implements I${ClassName}Service {\n\n    private final ${ClassName}Mapper baseMapper;\n\n    /**\n     * 查询${functionName}\n     */\n    @Override\n    public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}){\n        return baseMapper.selectVoById(${pkColumn.javaField});\n    }\n\n#if($table.crud || $table.sub)\n    /**\n     * 查询${functionName}列表\n     */\n    @Override\n    public TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo);\n        Page<${ClassName}Vo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(result);\n    }\n#end\n\n    /**\n     * 查询${functionName}列表\n     */\n    @Override\n    public List<${ClassName}Vo> queryList(${ClassName}Bo bo) {\n        LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo);\n        return baseMapper.selectVoList(lqw);\n    }\n\n    private LambdaQueryWrapper<${ClassName}> buildQueryWrapper(${ClassName}Bo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<${ClassName}> lqw = Wrappers.lambdaQuery();\n#foreach($column in $columns)\n#if($column.query)\n#set($queryType=$column.queryType)\n#set($javaField=$column.javaField)\n#set($javaType=$column.javaType)\n#set($columnName=$column.columnName)\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n#set($mpMethod=$column.queryType.toLowerCase())\n#if($queryType != 'BETWEEN')\n#if($javaType == 'String')\n#set($condition='StringUtils.isNotBlank(bo.get'+$AttrName+'())')\n#else\n#set($condition='bo.get'+$AttrName+'() != null')\n#end\n        lqw.$mpMethod($condition, ${ClassName}::get$AttrName, bo.get$AttrName());\n#else\n        lqw.between(params.get(\"begin$AttrName\") != null && params.get(\"end$AttrName\") != null,\n            ${ClassName}::get$AttrName ,params.get(\"begin$AttrName\"), params.get(\"end$AttrName\"));\n#end\n#end\n#end\n        return lqw;\n    }\n\n    /**\n     * 新增${functionName}\n     */\n    @Override\n    public Boolean insertByBo(${ClassName}Bo bo) {\n        ${ClassName} add = BeanUtil.toBean(bo, ${ClassName}.class);\n        validEntityBeforeSave(add);\n        boolean flag = baseMapper.insert(add) > 0;\n#set($pk=$pkColumn.javaField.substring(0,1).toUpperCase() + ${pkColumn.javaField.substring(1)})\n        if (flag) {\n            bo.set$pk(add.get$pk());\n        }\n        return flag;\n    }\n\n    /**\n     * 修改${functionName}\n     */\n    @Override\n    public Boolean updateByBo(${ClassName}Bo bo) {\n        ${ClassName} update = BeanUtil.toBean(bo, ${ClassName}.class);\n        validEntityBeforeSave(update);\n        return baseMapper.updateById(update) > 0;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(${ClassName} entity){\n        //TODO 做一些数据校验,如唯一约束\n    }\n\n    /**\n     * 批量删除${functionName}\n     */\n    @Override\n    public Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid) {\n        if(isValid){\n            //TODO 做一些业务上的校验,判断是否需要校验\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm",
    "content": "package ${packageName}.domain;\n\n#foreach ($import in $subImportList)\nimport ${import};\n#end\nimport top.flya.common.annotation.Excel;\n\n/**\n * ${subTable.functionName}对象 ${subTableName}\n *\n * @author ${author}\n * @date ${datetime}\n */\npublic class ${subClassName} extends BaseEntity\n{\n    private static final long serialVersionUID = 1L;\n\n#foreach ($column in $subTable.columns)\n#if(!$table.isSuperColumn($column.javaField))\n    /** $column.columnComment */\n#if($column.list)\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#if($parentheseIndex != -1)\n    @Excel(name = \"${comment}\", readConverterExp = \"$column.readConverterExp()\")\n#elseif($column.javaType == 'Date')\n    @JsonFormat(pattern = \"yyyy-MM-dd\")\n    @Excel(name = \"${comment}\", width = 30, dateFormat = \"yyyy-MM-dd\")\n#else\n    @Excel(name = \"${comment}\")\n#end\n#end\n    private $column.javaType $column.javaField;\n\n#end\n#end\n#foreach ($column in $subTable.columns)\n#if(!$table.isSuperColumn($column.javaField))\n#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches(\"[A-Z]\"))\n#set($AttrName=$column.javaField)\n#else\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n#end\n    public void set${AttrName}($column.javaType $column.javaField)\n    {\n        this.$column.javaField = $column.javaField;\n    }\n\n    public $column.javaType get${AttrName}()\n    {\n        return $column.javaField;\n    }\n#end\n#end\n\n    @Override\n    public String toString() {\n        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)\n#foreach ($column in $subTable.columns)\n#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches(\"[A-Z]\"))\n#set($AttrName=$column.javaField)\n#else\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n#end\n            .append(\"${column.javaField}\", get${AttrName}())\n#end\n            .toString();\n    }\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/java/vo.java.vm",
    "content": "package ${packageName}.domain.vo;\n\n#foreach ($import in $importList)\nimport ${import};\n#end\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\n\n\n/**\n * ${functionName}视图对象 ${tableName}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n@ExcelIgnoreUnannotated\npublic class ${ClassName}Vo {\n\n    private static final long serialVersionUID = 1L;\n\n#foreach ($column in $columns)\n#if($column.list)\n    /**\n     * $column.columnComment\n     */\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#if(${column.dictType} && ${column.dictType} != '')\n    @ExcelProperty(value = \"${comment}\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"${column.dictType}\")\n#elseif($parentheseIndex != -1)\n    @ExcelProperty(value = \"${comment}\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(readConverterExp = \"$column.readConverterExp()\")\n#else\n    @ExcelProperty(value = \"${comment}\")\n#end\n    private $column.javaType $column.javaField;\n\n#end\n#end\n\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/js/api.js.vm",
    "content": "import request from '@/utils/request'\n\n// 查询${functionName}列表\nexport function list${BusinessName}(query) {\n  return request({\n    url: '/${moduleName}/${businessName}/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询${functionName}详细\nexport function get${BusinessName}(${pkColumn.javaField}) {\n  return request({\n    url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},\n    method: 'get'\n  })\n}\n\n// 新增${functionName}\nexport function add${BusinessName}(data) {\n  return request({\n    url: '/${moduleName}/${businessName}',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改${functionName}\nexport function update${BusinessName}(data) {\n  return request({\n    url: '/${moduleName}/${businessName}',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除${functionName}\nexport function del${BusinessName}(${pkColumn.javaField}) {\n  return request({\n    url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/sql/oracle/sql.vm",
    "content": "-- 菜单 SQL\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate, '', null, '${functionName}菜单');\n\n-- 按钮 SQL\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1',  '#', '', 1,  0, 'F', '0', '0', '${permissionPrefix}:query',        '#', 'admin', sysdate, '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2',  '#', '', 1,  0, 'F', '0', '0', '${permissionPrefix}:add',          '#', 'admin', sysdate, '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3',  '#', '', 1,  0, 'F', '0', '0', '${permissionPrefix}:edit',         '#', 'admin', sysdate, '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4',  '#', '', 1,  0, 'F', '0', '0', '${permissionPrefix}:remove',       '#', 'admin', sysdate, '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5',  '#', '', 1,  0, 'F', '0', '0', '${permissionPrefix}:export',       '#', 'admin', sysdate, '', null, '');\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/sql/postgres/sql.vm",
    "content": "-- 菜单 SQL\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', now(), '', null, '${functionName}菜单');\n\n-- 按钮 SQL\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query',        '#', 'admin', now(), '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add',          '#', 'admin', now(), '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit',         '#', 'admin', now(), '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove',       '#', 'admin', now(), '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export',       '#', 'admin', now(), '', null, '');\n\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/sql/sql.vm",
    "content": "-- 菜单 SQL\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单');\n\n-- 按钮 SQL\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query',        '#', 'admin', sysdate(), '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add',          '#', 'admin', sysdate(), '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit',         '#', 'admin', sysdate(), '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove',       '#', 'admin', sysdate(), '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export',       '#', 'admin', sysdate(), '', null, '');\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/sql/sqlserver/sql.vm",
    "content": "-- 菜单 SQL\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', getdate(), '', null, '${functionName}菜单');\n\n-- 按钮 SQL\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query',        '#', 'admin', getdate(), '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add',          '#', 'admin', getdate(), '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit',         '#', 'admin', getdate(), '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove',       '#', 'admin', getdate(), '', null, '');\n\ninsert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)\nvalues(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export',       '#', 'admin', getdate(), '', null, '');\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n#foreach($column in $columns)\n#if($column.query)\n#set($dictType=$column.dictType)\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#if($column.htmlType == \"input\" || $column.htmlType == \"textarea\")\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-input\n          v-model=\"queryParams.${column.javaField}\"\n          placeholder=\"请输入${comment}\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n#elseif(($column.htmlType == \"select\" || $column.htmlType == \"radio\") && \"\" != $dictType)\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-select v-model=\"queryParams.${column.javaField}\" placeholder=\"请选择${comment}\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.${dictType}\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n#elseif(($column.htmlType == \"select\" || $column.htmlType == \"radio\") && $dictType)\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-select v-model=\"queryParams.${column.javaField}\" placeholder=\"请选择${comment}\" clearable>\n          <el-option label=\"请选择字典生成\" value=\"\" />\n        </el-select>\n      </el-form-item>\n#elseif($column.htmlType == \"datetime\" && $column.queryType != \"BETWEEN\")\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-date-picker clearable\n          v-model=\"queryParams.${column.javaField}\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"选择${comment}\">\n        </el-date-picker>\n      </el-form-item>\n#elseif($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n      <el-form-item label=\"${comment}\">\n        <el-date-picker\n          v-model=\"daterange${AttrName}\"\n          style=\"width: 240px\"\n          value-format=\"yyyy-MM-dd HH:mm:ss\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"['00:00:00', '23:59:59']\"\n        ></el-date-picker>\n      </el-form-item>\n#end\n#end\n#end\n      <el-form-item>\n\t    <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['${moduleName}:${businessName}:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"info\"\n          plain\n          icon=\"el-icon-sort\"\n          size=\"mini\"\n          @click=\"toggleExpandAll\"\n        >展开/折叠</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table\n      v-if=\"refreshTable\"\n      v-loading=\"loading\"\n      :data=\"${businessName}List\"\n      row-key=\"${treeCode}\"\n      :default-expand-all=\"isExpandAll\"\n      :tree-props=\"{children: 'children', hasChildren: 'hasChildren'}\"\n    >\n#foreach($column in $columns)\n#set($javaField=$column.javaField)\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#if($column.pk)\n#elseif($column.list && $column.htmlType == \"datetime\")\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n#elseif($column.list && $column.htmlType == \"imageUpload\")\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" width=\"100\">\n          <template #default=\"scope\">\n              <image-preview :src=\"scope.row.${javaField}\" :width=\"50\" :height=\"50\"/>\n          </template>\n      </el-table-column>\n#elseif($column.list && $column.dictType && \"\" != $column.dictType)\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\">\n        <template slot-scope=\"scope\">\n#if($column.htmlType == \"checkbox\")\n          <dict-tag :options=\"dict.type.${column.dictType}\" :value=\"scope.row.${javaField} ? scope.row.${javaField}.split(',') : []\"/>\n#else\n          <dict-tag :options=\"dict.type.${column.dictType}\" :value=\"scope.row.${javaField}\"/>\n#end\n        </template>\n      </el-table-column>\n#elseif($column.list && \"\" != $javaField)\n#if(${foreach.index} == 1)\n      <el-table-column label=\"${comment}\" prop=\"${javaField}\" />\n#else\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" />\n#end\n#end\n#end\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['${moduleName}:${businessName}:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-plus\"\n            @click=\"handleAdd(scope.row)\"\n            v-hasPermi=\"['${moduleName}:${businessName}:add']\"\n          >新增</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['${moduleName}:${businessName}:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <!-- 添加或修改${functionName}对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n#foreach($column in $columns)\n#set($field=$column.javaField)\n#if($column.insert && !$column.pk)\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#set($dictType=$column.dictType)\n#if(\"\" != $treeParentCode && $column.javaField == $treeParentCode)\n        <el-form-item label=\"${comment}\" prop=\"${treeParentCode}\">\n          <treeselect v-model=\"form.${treeParentCode}\" :options=\"${businessName}Options\" :normalizer=\"normalizer\" placeholder=\"请选择${comment}\" />\n        </el-form-item>\n#elseif($column.htmlType == \"input\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-input v-model=\"form.${field}\" placeholder=\"请输入${comment}\" />\n        </el-form-item>\n#elseif($column.htmlType == \"imageUpload\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <image-upload v-model=\"form.${field}\"/>\n        </el-form-item>\n#elseif($column.htmlType == \"fileUpload\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <file-upload v-model=\"form.${field}\"/>\n        </el-form-item>\n#elseif($column.htmlType == \"editor\")\n        <el-form-item label=\"${comment}\">\n          <editor v-model=\"form.${field}\" :min-height=\"192\"/>\n        </el-form-item>\n#elseif($column.htmlType == \"select\" && \"\" != $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-select v-model=\"form.${field}\" placeholder=\"请选择${comment}\">\n            <el-option\n              v-for=\"dict in dict.type.${dictType}\"\n              :key=\"dict.value\"\n              :label=\"dict.label\"\n#if($column.javaType == \"Integer\" || $column.javaType == \"Long\")\n              :value=\"parseInt(dict.value)\"\n#else\n              :value=\"dict.value\"\n#end\n            ></el-option>\n          </el-select>\n        </el-form-item>\n#elseif($column.htmlType == \"select\" && $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-select v-model=\"form.${field}\" placeholder=\"请选择${comment}\">\n            <el-option label=\"请选择字典生成\" value=\"\" />\n          </el-select>\n        </el-form-item>\n#elseif($column.htmlType == \"checkbox\" && \"\" != $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-checkbox-group v-model=\"form.${field}\">\n            <el-checkbox\n              v-for=\"dict in dict.type.${dictType}\"\n              :key=\"dict.value\"\n              :label=\"dict.value\">\n              {{dict.label}}\n            </el-checkbox>\n          </el-checkbox-group>\n        </el-form-item>\n#elseif($column.htmlType == \"checkbox\" && $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-checkbox-group v-model=\"form.${field}\">\n            <el-checkbox>请选择字典生成</el-checkbox>\n          </el-checkbox-group>\n        </el-form-item>\n#elseif($column.htmlType == \"radio\" && \"\" != $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-radio-group v-model=\"form.${field}\">\n            <el-radio\n              v-for=\"dict in dict.type.${dictType}\"\n              :key=\"dict.value\"\n#if($column.javaType == \"Integer\" || $column.javaType == \"Long\")\n              :label=\"parseInt(dict.value)\"\n#else\n              :label=\"dict.value\"\n#end\n            >{{dict.label}}</el-radio>\n          </el-radio-group>\n        </el-form-item>\n#elseif($column.htmlType == \"radio\" && $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-radio-group v-model=\"form.${field}\">\n            <el-radio label=\"1\">请选择字典生成</el-radio>\n          </el-radio-group>\n        </el-form-item>\n#elseif($column.htmlType == \"datetime\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-date-picker clearable\n            v-model=\"form.${field}\"\n            type=\"datetime\"\n            value-format=\"yyyy-MM-dd HH:mm:ss\"\n            placeholder=\"选择${comment}\">\n          </el-date-picker>\n        </el-form-item>\n#elseif($column.htmlType == \"textarea\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-input v-model=\"form.${field}\" type=\"textarea\" placeholder=\"请输入内容\" />\n        </el-form-item>\n#end\n#end\n#end\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from \"@/api/${moduleName}/${businessName}\";\nimport Treeselect from \"@riophae/vue-treeselect\";\nimport \"@riophae/vue-treeselect/dist/vue-treeselect.css\";\n\nexport default {\n  name: \"${BusinessName}\",\n#if(${dicts} != '')\n  dicts: [${dicts}],\n#end\n  components: {\n    Treeselect\n  },\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 显示搜索条件\n      showSearch: true,\n      // ${functionName}表格数据\n      ${businessName}List: [],\n      // ${functionName}树选项\n      ${businessName}Options: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 是否展开，默认全部展开\n      isExpandAll: true,\n      // 重新渲染表格状态\n      refreshTable: true,\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n      // $comment时间范围\n      daterange${AttrName}: [],\n#end\n#end\n      // 查询参数\n      queryParams: {\n#foreach ($column in $columns)\n#if($column.query)\n        $column.javaField: undefined#if($foreach.count != $columns.size()),#end\n#end\n#end\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n#foreach ($column in $columns)\n#if($column.required)\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n        $column.javaField: [\n          { required: true, message: \"$comment不能为空\", trigger: #if($column.htmlType == \"select\" || $column.htmlType == \"radio\")\"change\"#else\"blur\"#end }\n        ]#if($foreach.count != $columns.size()),#end\n#end\n#end\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询${functionName}列表 */\n    getList() {\n      this.loading = true;\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n      this.queryParams.params = {};\n#break\n#end\n#end\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n      if (null != this.daterange${AttrName} && '' != this.daterange${AttrName}) {\n        this.queryParams.params[\"begin${AttrName}\"] = this.daterange${AttrName}[0];\n        this.queryParams.params[\"end${AttrName}\"] = this.daterange${AttrName}[1];\n      }\n#end\n#end\n      list${BusinessName}(this.queryParams).then(response => {\n        this.${businessName}List = this.handleTree(response.data, \"${treeCode}\", \"${treeParentCode}\");\n        this.loading = false;\n      });\n    },\n    /** 转换${functionName}数据结构 */\n    normalizer(node) {\n      if (node.children && !node.children.length) {\n        delete node.children;\n      }\n      return {\n        id: node.${treeCode},\n        label: node.${treeName},\n        children: node.children\n      };\n    },\n\t/** 查询${functionName}下拉树结构 */\n    getTreeselect() {\n      list${BusinessName}().then(response => {\n        this.${businessName}Options = [];\n        const data = { ${treeCode}: 0, ${treeName}: '顶级节点', children: [] };\n        data.children = this.handleTree(response.data, \"${treeCode}\", \"${treeParentCode}\");\n        this.${businessName}Options.push(data);\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n#foreach ($column in $columns)\n#if($column.htmlType == \"checkbox\")\n        $column.javaField: []#if($foreach.count != $columns.size()),#end\n#else\n        $column.javaField: null#if($foreach.count != $columns.size()),#end\n#end\n#end\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n      this.daterange${AttrName} = [];\n#end\n#end\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 新增按钮操作 */\n    handleAdd(row) {\n      this.reset();\n      this.getTreeselect();\n      if (row != null && row.${treeCode}) {\n        this.form.${treeParentCode} = row.${treeCode};\n      } else {\n        this.form.${treeParentCode} = 0;\n      }\n      this.open = true;\n      this.title = \"添加${functionName}\";\n    },\n    /** 展开/折叠操作 */\n    toggleExpandAll() {\n      this.refreshTable = false;\n      this.isExpandAll = !this.isExpandAll;\n      this.$nextTick(() => {\n        this.refreshTable = true;\n      });\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n\t  this.loading = true;\n      this.reset();\n      this.getTreeselect();\n      if (row != null) {\n        this.form.${treeParentCode} = row.${treeCode};\n      }\n      get${BusinessName}(row.${pkColumn.javaField}).then(response => {\n\t    this.loading = false;\n        this.form = response.data;\n#foreach ($column in $columns)\n#if($column.htmlType == \"checkbox\")\n        this.form.$column.javaField = this.form.${column.javaField}.split(\",\");\n#end\n#end\n        this.open = true;\n        this.title = \"修改${functionName}\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.#[[$]]#refs[\"form\"].validate(valid => {\n        if (valid) {\n\t\t  this.buttonLoading = true;\n#foreach ($column in $columns)\n#if($column.htmlType == \"checkbox\")\n          this.form.$column.javaField = this.form.${column.javaField}.join(\",\");\n#end\n#end\n          if (this.form.${pkColumn.javaField} != null) {\n            update${BusinessName}(this.form).then(response => {\n              this.#[[$modal]]#.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            add${BusinessName}(this.form).then(response => {\n              this.#[[$modal]]#.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      this.#[[$modal]]#.confirm('是否确认删除${functionName}编号为\"' + row.${pkColumn.javaField} + '\"的数据项？').then(() => {\n        this.loading = true;\n        return del${BusinessName}(row.${pkColumn.javaField});\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.#[[$modal]]#.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/vue/index.vue.vm",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n#foreach($column in $columns)\n#if($column.query)\n#set($dictType=$column.dictType)\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#if($column.htmlType == \"input\" || $column.htmlType == \"textarea\")\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-input\n          v-model=\"queryParams.${column.javaField}\"\n          placeholder=\"请输入${comment}\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n#elseif(($column.htmlType == \"select\" || $column.htmlType == \"radio\") && \"\" != $dictType)\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-select v-model=\"queryParams.${column.javaField}\" placeholder=\"请选择${comment}\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.${dictType}\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n#elseif(($column.htmlType == \"select\" || $column.htmlType == \"radio\") && $dictType)\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-select v-model=\"queryParams.${column.javaField}\" placeholder=\"请选择${comment}\" clearable>\n          <el-option label=\"请选择字典生成\" value=\"\" />\n        </el-select>\n      </el-form-item>\n#elseif($column.htmlType == \"datetime\" && $column.queryType != \"BETWEEN\")\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-date-picker clearable\n          v-model=\"queryParams.${column.javaField}\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择${comment}\">\n        </el-date-picker>\n      </el-form-item>\n#elseif($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n      <el-form-item label=\"${comment}\">\n        <el-date-picker\n          v-model=\"daterange${AttrName}\"\n          style=\"width: 240px\"\n          value-format=\"yyyy-MM-dd HH:mm:ss\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"['00:00:00', '23:59:59']\"\n        ></el-date-picker>\n      </el-form-item>\n#end\n#end\n#end\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['${moduleName}:${businessName}:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['${moduleName}:${businessName}:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['${moduleName}:${businessName}:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['${moduleName}:${businessName}:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"${businessName}List\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n#foreach($column in $columns)\n#set($javaField=$column.javaField)\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#if($column.pk)\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" v-if=\"${column.list}\"/>\n#elseif($column.list && $column.htmlType == \"datetime\")\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n#elseif($column.list && $column.htmlType == \"imageUpload\")\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <image-preview :src=\"scope.row.${javaField}\" :width=\"50\" :height=\"50\"/>\n        </template>\n      </el-table-column>\n#elseif($column.list && $column.dictType && \"\" != $column.dictType)\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\">\n        <template slot-scope=\"scope\">\n#if($column.htmlType == \"checkbox\")\n          <dict-tag :options=\"dict.type.${column.dictType}\" :value=\"scope.row.${javaField} ? scope.row.${javaField}.split(',') : []\"/>\n#else\n          <dict-tag :options=\"dict.type.${column.dictType}\" :value=\"scope.row.${javaField}\"/>\n#end\n        </template>\n      </el-table-column>\n#elseif($column.list && \"\" != $javaField)\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" />\n#end\n#end\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['${moduleName}:${businessName}:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['${moduleName}:${businessName}:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改${functionName}对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n#foreach($column in $columns)\n#set($field=$column.javaField)\n#if($column.insert && !$column.pk)\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#set($dictType=$column.dictType)\n#if($column.htmlType == \"input\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-input v-model=\"form.${field}\" placeholder=\"请输入${comment}\" />\n        </el-form-item>\n#elseif($column.htmlType == \"imageUpload\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <image-upload v-model=\"form.${field}\"/>\n        </el-form-item>\n#elseif($column.htmlType == \"fileUpload\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <file-upload v-model=\"form.${field}\"/>\n        </el-form-item>\n#elseif($column.htmlType == \"editor\")\n        <el-form-item label=\"${comment}\">\n          <editor v-model=\"form.${field}\" :min-height=\"192\"/>\n        </el-form-item>\n#elseif($column.htmlType == \"select\" && \"\" != $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-select v-model=\"form.${field}\" placeholder=\"请选择${comment}\">\n            <el-option\n              v-for=\"dict in dict.type.${dictType}\"\n              :key=\"dict.value\"\n              :label=\"dict.label\"\n#if($column.javaType == \"Integer\" || $column.javaType == \"Long\")\n              :value=\"parseInt(dict.value)\"\n#else\n              :value=\"dict.value\"\n#end\n            ></el-option>\n          </el-select>\n        </el-form-item>\n#elseif($column.htmlType == \"select\" && $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-select v-model=\"form.${field}\" placeholder=\"请选择${comment}\">\n            <el-option label=\"请选择字典生成\" value=\"\" />\n          </el-select>\n        </el-form-item>\n#elseif($column.htmlType == \"checkbox\" && \"\" != $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-checkbox-group v-model=\"form.${field}\">\n            <el-checkbox\n              v-for=\"dict in dict.type.${dictType}\"\n              :key=\"dict.value\"\n              :label=\"dict.value\">\n              {{dict.label}}\n            </el-checkbox>\n          </el-checkbox-group>\n        </el-form-item>\n#elseif($column.htmlType == \"checkbox\" && $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-checkbox-group v-model=\"form.${field}\">\n            <el-checkbox>请选择字典生成</el-checkbox>\n          </el-checkbox-group>\n        </el-form-item>\n#elseif($column.htmlType == \"radio\" && \"\" != $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-radio-group v-model=\"form.${field}\">\n            <el-radio\n              v-for=\"dict in dict.type.${dictType}\"\n              :key=\"dict.value\"\n#if($column.javaType == \"Integer\" || $column.javaType == \"Long\")\n              :label=\"parseInt(dict.value)\"\n#else\n              :label=\"dict.value\"\n#end\n            >{{dict.label}}</el-radio>\n          </el-radio-group>\n        </el-form-item>\n#elseif($column.htmlType == \"radio\" && $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-radio-group v-model=\"form.${field}\">\n            <el-radio label=\"1\">请选择字典生成</el-radio>\n          </el-radio-group>\n        </el-form-item>\n#elseif($column.htmlType == \"datetime\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-date-picker clearable\n            v-model=\"form.${field}\"\n            type=\"datetime\"\n            value-format=\"yyyy-MM-dd HH:mm:ss\"\n            placeholder=\"请选择${comment}\">\n          </el-date-picker>\n        </el-form-item>\n#elseif($column.htmlType == \"textarea\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-input v-model=\"form.${field}\" type=\"textarea\" placeholder=\"请输入内容\" />\n        </el-form-item>\n#end\n#end\n#end\n#if($table.sub)\n        <el-divider content-position=\"center\">${subTable.functionName}信息</el-divider>\n        <el-row :gutter=\"10\" class=\"mb8\">\n          <el-col :span=\"1.5\">\n            <el-button type=\"primary\" icon=\"el-icon-plus\" size=\"mini\" @click=\"handleAdd${subClassName}\">添加</el-button>\n          </el-col>\n          <el-col :span=\"1.5\">\n            <el-button type=\"danger\" icon=\"el-icon-delete\" size=\"mini\" @click=\"handleDelete${subClassName}\">删除</el-button>\n          </el-col>\n        </el-row>\n        <el-table :data=\"${subclassName}List\" :row-class-name=\"row${subClassName}Index\" @selection-change=\"handle${subClassName}SelectionChange\" ref=\"${subclassName}\">\n          <el-table-column type=\"selection\" width=\"50\" align=\"center\" />\n          <el-table-column label=\"序号\" align=\"center\" prop=\"index\" width=\"50\"/>\n#foreach($column in $subTable.columns)\n#set($javaField=$column.javaField)\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#if($column.pk || $javaField == ${subTableFkclassName})\n#elseif($column.list && $column.htmlType == \"input\")\n          <el-table-column label=\"$comment\" prop=\"${javaField}\" width=\"150\">\n            <template slot-scope=\"scope\">\n              <el-input v-model=\"scope.row.$javaField\" placeholder=\"请输入$comment\" />\n            </template>\n          </el-table-column>\n#elseif($column.list && $column.htmlType == \"datetime\")\n          <el-table-column label=\"$comment\" prop=\"${javaField}\" width=\"240\">\n            <template slot-scope=\"scope\">\n              <el-date-picker clearable v-model=\"scope.row.$javaField\" type=\"date\" value-format=\"yyyy-MM-dd\" placeholder=\"请选择$comment\" />\n            </template>\n          </el-table-column>\n#elseif($column.list && ($column.htmlType == \"select\" || $column.htmlType == \"radio\") && \"\" != $column.dictType)\n          <el-table-column label=\"$comment\" prop=\"${javaField}\" width=\"150\">\n            <template slot-scope=\"scope\">\n              <el-select v-model=\"scope.row.$javaField\" placeholder=\"请选择$comment\">\n                <el-option\n                  v-for=\"dict in dict.type.$column.dictType\"\n                  :key=\"dict.value\"\n                  :label=\"dict.label\"\n                  :value=\"dict.value\"\n                ></el-option>\n              </el-select>\n            </template>\n          </el-table-column>\n#elseif($column.list && ($column.htmlType == \"select\" || $column.htmlType == \"radio\") && \"\" == $column.dictType)\n          <el-table-column label=\"$comment\" prop=\"${javaField}\" width=\"150\">\n            <template slot-scope=\"scope\">\n              <el-select v-model=\"scope.row.$javaField\" placeholder=\"请选择$comment\">\n                <el-option label=\"请选择字典生成\" value=\"\" />\n              </el-select>\n            </template>\n          </el-table-column>\n#end\n#end\n        </el-table>\n#end\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from \"@/api/${moduleName}/${businessName}\";\n\nexport default {\n  name: \"${BusinessName}\",\n#if(${dicts} != '')\n  dicts: [${dicts}],\n#end\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n#if($table.sub)\n      // 子表选中数据\n      checked${subClassName}: [],\n#end\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // ${functionName}表格数据\n      ${businessName}List: [],\n#if($table.sub)\n      // ${subTable.functionName}表格数据\n      ${subclassName}List: [],\n#end\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n      // $comment时间范围\n      daterange${AttrName}: [],\n#end\n#end\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n#foreach ($column in $columns)\n#if($column.query)\n        $column.javaField: undefined#if($foreach.count != $columns.size()),#end\n#end\n#end\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n#foreach ($column in $columns)\n#if($column.required)\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n        $column.javaField: [\n          { required: true, message: \"$comment不能为空\", trigger: #if($column.htmlType == \"select\" || $column.htmlType == \"radio\")\"change\"#else\"blur\"#end }\n        ]#if($foreach.count != $columns.size()),#end\n#end\n#end\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询${functionName}列表 */\n    getList() {\n      this.loading = true;\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n      this.queryParams.params = {};\n#break\n#end\n#end\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n      if (null != this.daterange${AttrName} && '' != this.daterange${AttrName}) {\n        this.queryParams.params[\"begin${AttrName}\"] = this.daterange${AttrName}[0];\n        this.queryParams.params[\"end${AttrName}\"] = this.daterange${AttrName}[1];\n      }\n#end\n#end\n      list${BusinessName}(this.queryParams).then(response => {\n        this.${businessName}List = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n#foreach ($column in $columns)\n#if($column.htmlType == \"checkbox\")\n        $column.javaField: []#if($foreach.count != $columns.size()),#end\n#else\n        $column.javaField: undefined#if($foreach.count != $columns.size()),#end\n#end\n#end\n      };\n#if($table.sub)\n      this.${subclassName}List = [];\n#end\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n      this.daterange${AttrName} = [];\n#end\n#end\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.${pkColumn.javaField})\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加${functionName}\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const ${pkColumn.javaField} = row.${pkColumn.javaField} || this.ids\n      get${BusinessName}(${pkColumn.javaField}).then(response => {\n        this.loading = false;\n        this.form = response.data;\n#foreach ($column in $columns)\n#if($column.htmlType == \"checkbox\")\n        this.form.$column.javaField = this.form.${column.javaField}.split(\",\");\n#end\n#end\n#if($table.sub)\n        this.${subclassName}List = response.data.${subclassName}List;\n#end\n        this.open = true;\n        this.title = \"修改${functionName}\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.#[[$]]#refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n#foreach ($column in $columns)\n#if($column.htmlType == \"checkbox\")\n          this.form.$column.javaField = this.form.${column.javaField}.join(\",\");\n#end\n#end\n#if($table.sub)\n          this.form.${subclassName}List = this.${subclassName}List;\n#end\n          if (this.form.${pkColumn.javaField} != null) {\n            update${BusinessName}(this.form).then(response => {\n              this.#[[$modal]]#.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            add${BusinessName}(this.form).then(response => {\n              this.#[[$modal]]#.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const ${pkColumn.javaField}s = row.${pkColumn.javaField} || this.ids;\n      this.#[[$modal]]#.confirm('是否确认删除${functionName}编号为\"' + ${pkColumn.javaField}s + '\"的数据项？').then(() => {\n        this.loading = true;\n        return del${BusinessName}(${pkColumn.javaField}s);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.#[[$modal]]#.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n#if($table.sub)\n    /** ${subTable.functionName}序号 */\n    row${subClassName}Index({ row, rowIndex }) {\n      row.index = rowIndex + 1;\n    },\n    /** ${subTable.functionName}添加按钮操作 */\n    handleAdd${subClassName}() {\n      let obj = {};\n#foreach($column in $subTable.columns)\n#if($column.pk || $column.javaField == ${subTableFkclassName})\n#elseif($column.list && \"\" != $javaField)\n      obj.$column.javaField = \"\";\n#end\n#end\n      this.${subclassName}List.push(obj);\n    },\n    /** ${subTable.functionName}删除按钮操作 */\n    handleDelete${subClassName}() {\n      if (this.checked${subClassName}.length == 0) {\n        this.#[[$modal]]#.msgError(\"请先选择要删除的${subTable.functionName}数据\");\n      } else {\n        const ${subclassName}List = this.${subclassName}List;\n        const checked${subClassName} = this.checked${subClassName};\n        this.${subclassName}List = ${subclassName}List.filter(function(item) {\n          return checked${subClassName}.indexOf(item.index) == -1\n        });\n      }\n    },\n    /** 复选框选中数据 */\n    handle${subClassName}SelectionChange(selection) {\n      this.checked${subClassName} = selection.map(item => item.index)\n    },\n#end\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('${moduleName}/${businessName}/export', {\n        ...this.queryParams\n      }, `${businessName}_#[[${new Date().getTime()}]]#.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryRef\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n#foreach($column in $columns)\n#if($column.query)\n#set($dictType=$column.dictType)\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#if($column.htmlType == \"input\" || $column.htmlType == \"textarea\")\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-input\n          v-model=\"queryParams.${column.javaField}\"\n          placeholder=\"请输入${comment}\"\n          clearable\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n#elseif(($column.htmlType == \"select\" || $column.htmlType == \"radio\") && \"\" != $dictType)\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-select v-model=\"queryParams.${column.javaField}\" placeholder=\"请选择${comment}\" clearable>\n          <el-option\n            v-for=\"dict in ${dictType}\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n#elseif(($column.htmlType == \"select\" || $column.htmlType == \"radio\") && $dictType)\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-select v-model=\"queryParams.${column.javaField}\" placeholder=\"请选择${comment}\" clearable>\n          <el-option label=\"请选择字典生成\" value=\"\" />\n        </el-select>\n      </el-form-item>\n#elseif($column.htmlType == \"datetime\" && $column.queryType != \"BETWEEN\")\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-date-picker clearable\n          v-model=\"queryParams.${column.javaField}\"\n          type=\"date\"\n          value-format=\"YYYY-MM-DD\"\n          placeholder=\"选择${comment}\">\n        </el-date-picker>\n      </el-form-item>\n#elseif($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n      <el-form-item label=\"${comment}\" style=\"width: 308px\">\n        <el-date-picker\n          v-model=\"daterange${AttrName}\"\n          value-format=\"YYYY-MM-DD HH:mm:ss\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]\"\n        ></el-date-picker>\n      </el-form-item>\n#end\n#end\n#end\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"Refresh\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"Plus\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['${moduleName}:${businessName}:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"info\"\n          plain\n          icon=\"Sort\"\n          @click=\"toggleExpandAll\"\n        >展开/折叠</el-button>\n      </el-col>\n      <right-toolbar v-model:showSearch=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table\n      v-if=\"refreshTable\"\n      v-loading=\"loading\"\n      :data=\"${businessName}List\"\n      row-key=\"${treeCode}\"\n      :default-expand-all=\"isExpandAll\"\n      :tree-props=\"{children: 'children', hasChildren: 'hasChildren'}\"\n    >\n#foreach($column in $columns)\n#set($javaField=$column.javaField)\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#if($column.pk)\n#elseif($column.list && $column.htmlType == \"datetime\")\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" width=\"180\">\n        <template #default=\"scope\">\n          <span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n#elseif($column.list && $column.htmlType == \"imageUpload\")\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" width=\"100\">\n          <template #default=\"scope\">\n              <image-preview :src=\"scope.row.${javaField}\" :width=\"50\" :height=\"50\"/>\n          </template>\n      </el-table-column>\n#elseif($column.list && $column.dictType && \"\" != $column.dictType)\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\">\n        <template #default=\"scope\">\n#if($column.htmlType == \"checkbox\")\n          <dict-tag :options=\"${column.dictType}\" :value=\"scope.row.${javaField} ? scope.row.${javaField}.split(',') : []\"/>\n#else\n          <dict-tag :options=\"${column.dictType}\" :value=\"scope.row.${javaField}\"/>\n#end\n        </template>\n      </el-table-column>\n#elseif($column.list && \"\" != $javaField)\n#if(${foreach.index} == 1)\n      <el-table-column label=\"${comment}\" prop=\"${javaField}\" />\n#else\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" />\n#end\n#end\n#end\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n          <template #default=\"scope\">\n              <el-button link type=\"primary\" icon=\"Edit\" @click=\"handleUpdate(scope.row)\" v-hasPermi=\"['${moduleName}:${businessName}:edit']\">修改</el-button>\n              <el-button link type=\"primary\" icon=\"Plus\" @click=\"handleAdd(scope.row)\" v-hasPermi=\"['${moduleName}:${businessName}:add']\">新增</el-button>\n              <el-button link type=\"primary\" icon=\"Delete\" @click=\"handleDelete(scope.row)\" v-hasPermi=\"['${moduleName}:${businessName}:remove']\">删除</el-button>\n          </template>\n      </el-table-column>\n    </el-table>\n\n    <!-- 添加或修改${functionName}对话框 -->\n    <el-dialog :title=\"title\" v-model=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"${businessName}Ref\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n#foreach($column in $columns)\n#set($field=$column.javaField)\n#if($column.insert && !$column.pk)\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#set($dictType=$column.dictType)\n#if(\"\" != $treeParentCode && $column.javaField == $treeParentCode)\n        <el-form-item label=\"${comment}\" prop=\"${treeParentCode}\">\n          <el-tree-select\n            v-model=\"form.${treeParentCode}\"\n            :data=\"${businessName}Options\"\n            :props=\"{ value: '${treeCode}', label: '${treeName}', children: 'children' }\"\n            value-key=\"${treeCode}\"\n            placeholder=\"请选择${comment}\"\n            check-strictly\n          />\n        </el-form-item>\n#elseif($column.htmlType == \"input\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-input v-model=\"form.${field}\" placeholder=\"请输入${comment}\" />\n        </el-form-item>\n#elseif($column.htmlType == \"imageUpload\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <image-upload v-model=\"form.${field}\"/>\n        </el-form-item>\n#elseif($column.htmlType == \"fileUpload\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <file-upload v-model=\"form.${field}\"/>\n        </el-form-item>\n#elseif($column.htmlType == \"editor\")\n        <el-form-item label=\"${comment}\">\n          <editor v-model=\"form.${field}\" :min-height=\"192\"/>\n        </el-form-item>\n#elseif($column.htmlType == \"select\" && \"\" != $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-select v-model=\"form.${field}\" placeholder=\"请选择${comment}\">\n            <el-option\n              v-for=\"dict in ${dictType}\"\n              :key=\"dict.value\"\n              :label=\"dict.label\"\n#if($column.javaType == \"Integer\" || $column.javaType == \"Long\")\n              :value=\"parseInt(dict.value)\"\n#else\n              :value=\"dict.value\"\n#end\n            ></el-option>\n          </el-select>\n        </el-form-item>\n#elseif($column.htmlType == \"select\" && $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-select v-model=\"form.${field}\" placeholder=\"请选择${comment}\">\n            <el-option label=\"请选择字典生成\" value=\"\" />\n          </el-select>\n        </el-form-item>\n#elseif($column.htmlType == \"checkbox\" && \"\" != $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-checkbox-group v-model=\"form.${field}\">\n            <el-checkbox\n              v-for=\"dict in ${dictType}\"\n              :key=\"dict.value\"\n              :label=\"dict.value\">\n              {{dict.label}}\n            </el-checkbox>\n          </el-checkbox-group>\n        </el-form-item>\n#elseif($column.htmlType == \"checkbox\" && $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-checkbox-group v-model=\"form.${field}\">\n            <el-checkbox>请选择字典生成</el-checkbox>\n          </el-checkbox-group>\n        </el-form-item>\n#elseif($column.htmlType == \"radio\" && \"\" != $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-radio-group v-model=\"form.${field}\">\n            <el-radio\n              v-for=\"dict in ${dictType}\"\n              :key=\"dict.value\"\n#if($column.javaType == \"Integer\" || $column.javaType == \"Long\")\n              :label=\"parseInt(dict.value)\"\n#else\n              :label=\"dict.value\"\n#end\n            >{{dict.label}}</el-radio>\n          </el-radio-group>\n        </el-form-item>\n#elseif($column.htmlType == \"radio\" && $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-radio-group v-model=\"form.${field}\">\n            <el-radio label=\"1\">请选择字典生成</el-radio>\n          </el-radio-group>\n        </el-form-item>\n#elseif($column.htmlType == \"datetime\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-date-picker clearable\n            v-model=\"form.${field}\"\n            type=\"datetime\"\n            value-format=\"YYYY-MM-DD HH:mm:ss\"\n            placeholder=\"选择${comment}\">\n          </el-date-picker>\n        </el-form-item>\n#elseif($column.htmlType == \"textarea\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-input v-model=\"form.${field}\" type=\"textarea\" placeholder=\"请输入内容\" />\n        </el-form-item>\n#end\n#end\n#end\n      </el-form>\n      <template #footer>\n        <div class=\"dialog-footer\">\n          <el-button type=\"primary\" @click=\"submitForm\">确 定</el-button>\n          <el-button @click=\"cancel\">取 消</el-button>\n        </div>\n      </template>\n    </el-dialog>\n  </div>\n</template>\n\n<script setup name=\"${BusinessName}\">\nimport { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from \"@/api/${moduleName}/${businessName}\";\n\nconst { proxy } = getCurrentInstance();\n#if(${dicts} != '')\n#set($dictsNoSymbol=$dicts.replace(\"'\", \"\"))\nconst { ${dictsNoSymbol} } = proxy.useDict(${dicts});\n#end\n\nconst ${businessName}List = ref([]);\nconst ${businessName}Options = ref([]);\nconst open = ref(false);\nconst buttonLoading = ref(false);\nconst loading = ref(true);\nconst showSearch = ref(true);\nconst title = ref(\"\");\nconst isExpandAll = ref(true);\nconst refreshTable = ref(true);\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\nconst daterange${AttrName} = ref([]);\n#end\n#end\n\nconst data = reactive({\n  form: {},\n  queryParams: {\n#foreach ($column in $columns)\n#if($column.query)\n    $column.javaField: undefined#if($foreach.count != $columns.size()),#end\n#end\n#end\n  },\n  rules: {\n#foreach ($column in $columns)\n#if($column.required)\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n    $column.javaField: [\n      { required: true, message: \"$comment不能为空\", trigger: #if($column.htmlType == \"select\" || $column.htmlType == \"radio\")\"change\"#else\"blur\"#end }\n    ]#if($foreach.count != $columns.size()),#end\n#end\n#end\n  }\n});\n\nconst { queryParams, form, rules } = toRefs(data);\n\n/** 查询${functionName}列表 */\nfunction getList() {\n  loading.value = true;\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n  queryParams.value.params = {};\n#break\n#end\n#end\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n  if (null != daterange${AttrName} && '' != daterange${AttrName}) {\n    queryParams.value.params[\"begin${AttrName}\"] = daterange${AttrName}.value[0];\n    queryParams.value.params[\"end${AttrName}\"] = daterange${AttrName}.value[1];\n  }\n#end\n#end\n  list${BusinessName}(queryParams.value).then(response => {\n    ${businessName}List.value = proxy.handleTree(response.data, \"${treeCode}\", \"${treeParentCode}\");\n    loading.value = false;\n  });\n}\n\n/** 查询${functionName}下拉树结构 */\nfunction getTreeselect() {\n  list${BusinessName}().then(response => {\n    ${businessName}Options.value = [];\n    const data = { ${treeCode}: 0, ${treeName}: '顶级节点', children: [] };\n    data.children = proxy.handleTree(response.data, \"${treeCode}\", \"${treeParentCode}\");\n    ${businessName}Options.value.push(data);\n  });\n}\n\n// 取消按钮\nfunction cancel() {\n  open.value = false;\n  reset();\n}\n\n// 表单重置\nfunction reset() {\n  form.value = {\n#foreach ($column in $columns)\n#if($column.htmlType == \"checkbox\")\n    $column.javaField: []#if($foreach.count != $columns.size()),#end\n#else\n    $column.javaField: null#if($foreach.count != $columns.size()),#end\n#end\n#end\n  };\n  proxy.resetForm(\"${businessName}Ref\");\n}\n\n/** 搜索按钮操作 */\nfunction handleQuery() {\n  getList();\n}\n\n/** 重置按钮操作 */\nfunction resetQuery() {\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n  daterange${AttrName}.value = [];\n#end\n#end\n  proxy.resetForm(\"queryRef\");\n  handleQuery();\n}\n\n/** 新增按钮操作 */\nfunction handleAdd(row) {\n  reset();\n  getTreeselect();\n  if (row != null && row.${treeCode}) {\n    form.value.${treeParentCode} = row.${treeCode};\n  } else {\n    form.value.${treeParentCode} = 0;\n  }\n  open.value = true;\n  title.value = \"添加${functionName}\";\n}\n\n/** 展开/折叠操作 */\nfunction toggleExpandAll() {\n  refreshTable.value = false;\n  isExpandAll.value = !isExpandAll.value;\n  nextTick(() => {\n    refreshTable.value = true;\n  });\n}\n\n/** 修改按钮操作 */\nasync function handleUpdate(row) {\n  loading.value = true;\n  reset();\n  await getTreeselect();\n  if (row != null) {\n    form.value.${treeParentCode} = row.${treeCode};\n  }\n  get${BusinessName}(row.${pkColumn.javaField}).then(response => {\n    loading.value = false;\n    form.value = response.data;\n#foreach ($column in $columns)\n#if($column.htmlType == \"checkbox\")\n    form.value.$column.javaField = form.value.${column.javaField}.split(\",\");\n#end\n#end\n    open.value = true;\n    title.value = \"修改${functionName}\";\n  });\n}\n\n/** 提交按钮 */\nfunction submitForm() {\n  proxy.#[[$]]#refs[\"${businessName}Ref\"].validate(valid => {\n    if (valid) {\n      buttonLoading.value = true;\n#foreach ($column in $columns)\n#if($column.htmlType == \"checkbox\")\n      form.value.$column.javaField = form.value.${column.javaField}.join(\",\");\n#end\n#end\n      if (form.value.${pkColumn.javaField} != null) {\n        update${BusinessName}(form.value).then(response => {\n          proxy.#[[$modal]]#.msgSuccess(\"修改成功\");\n          open.value = false;\n          getList();\n        }).finally(() => {\n          buttonLoading.value = false;\n        });\n      } else {\n        add${BusinessName}(form.value).then(response => {\n          proxy.#[[$modal]]#.msgSuccess(\"新增成功\");\n          open.value = false;\n          getList();\n        }).finally(() => {\n          buttonLoading.value = false;\n        });\n      }\n    }\n  });\n}\n\n/** 删除按钮操作 */\nfunction handleDelete(row) {\n  proxy.#[[$modal]]#.confirm('是否确认删除${functionName}编号为\"' + row.${pkColumn.javaField} + '\"的数据项？').then(function() {\n    loading.value = true;\n    return del${BusinessName}(row.${pkColumn.javaField});\n  }).then(() => {\n    loading.value = false;\n    getList();\n    proxy.#[[$modal]]#.msgSuccess(\"删除成功\");\n  }).catch(() => {\n  }).finally(() => {\n    loading.value = false;\n  });\n}\n\ngetList();\n</script>\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryRef\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n#foreach($column in $columns)\n#if($column.query)\n#set($dictType=$column.dictType)\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#if($column.htmlType == \"input\" || $column.htmlType == \"textarea\")\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-input\n          v-model=\"queryParams.${column.javaField}\"\n          placeholder=\"请输入${comment}\"\n          clearable\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n#elseif(($column.htmlType == \"select\" || $column.htmlType == \"radio\") && \"\" != $dictType)\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-select v-model=\"queryParams.${column.javaField}\" placeholder=\"请选择${comment}\" clearable>\n          <el-option\n            v-for=\"dict in ${dictType}\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n#elseif(($column.htmlType == \"select\" || $column.htmlType == \"radio\") && $dictType)\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-select v-model=\"queryParams.${column.javaField}\" placeholder=\"请选择${comment}\" clearable>\n          <el-option label=\"请选择字典生成\" value=\"\" />\n        </el-select>\n      </el-form-item>\n#elseif($column.htmlType == \"datetime\" && $column.queryType != \"BETWEEN\")\n      <el-form-item label=\"${comment}\" prop=\"${column.javaField}\">\n        <el-date-picker clearable\n          v-model=\"queryParams.${column.javaField}\"\n          type=\"date\"\n          value-format=\"YYYY-MM-DD\"\n          placeholder=\"请选择${comment}\">\n        </el-date-picker>\n      </el-form-item>\n#elseif($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n      <el-form-item label=\"${comment}\" style=\"width: 308px\">\n        <el-date-picker\n          v-model=\"daterange${AttrName}\"\n          value-format=\"YYYY-MM-DD HH:mm:ss\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          :default-time=\"[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]\"\n        ></el-date-picker>\n      </el-form-item>\n#end\n#end\n#end\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"Refresh\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"Plus\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['${moduleName}:${businessName}:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"Edit\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['${moduleName}:${businessName}:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"Delete\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['${moduleName}:${businessName}:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"Download\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['${moduleName}:${businessName}:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar v-model:showSearch=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"${businessName}List\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n#foreach($column in $columns)\n#set($javaField=$column.javaField)\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#if($column.pk)\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" v-if=\"${column.list}\"/>\n#elseif($column.list && $column.htmlType == \"datetime\")\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" width=\"180\">\n        <template #default=\"scope\">\n          <span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n#elseif($column.list && $column.htmlType == \"imageUpload\")\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" width=\"100\">\n          <template #default=\"scope\">\n              <image-preview :src=\"scope.row.${javaField}\" :width=\"50\" :height=\"50\"/>\n          </template>\n      </el-table-column>\n#elseif($column.list && $column.dictType && \"\" != $column.dictType)\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\">\n        <template #default=\"scope\">\n#if($column.htmlType == \"checkbox\")\n          <dict-tag :options=\"${column.dictType}\" :value=\"scope.row.${javaField} ? scope.row.${javaField}.split(',') : []\"/>\n#else\n          <dict-tag :options=\"${column.dictType}\" :value=\"scope.row.${javaField}\"/>\n#end\n        </template>\n      </el-table-column>\n#elseif($column.list && \"\" != $javaField)\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" />\n#end\n#end\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n          <template #default=\"scope\">\n              <el-button link type=\"primary\" icon=\"Edit\" @click=\"handleUpdate(scope.row)\" v-hasPermi=\"['${moduleName}:${businessName}:edit']\">修改</el-button>\n              <el-button link type=\"primary\" icon=\"Delete\" @click=\"handleDelete(scope.row)\" v-hasPermi=\"['${moduleName}:${businessName}:remove']\">删除</el-button>\n          </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      v-model:page=\"queryParams.pageNum\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改${functionName}对话框 -->\n    <el-dialog :title=\"title\" v-model=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"${businessName}Ref\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n#foreach($column in $columns)\n#set($field=$column.javaField)\n#if($column.insert && !$column.pk)\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#set($dictType=$column.dictType)\n#if($column.htmlType == \"input\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-input v-model=\"form.${field}\" placeholder=\"请输入${comment}\" />\n        </el-form-item>\n#elseif($column.htmlType == \"imageUpload\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <image-upload v-model=\"form.${field}\"/>\n        </el-form-item>\n#elseif($column.htmlType == \"fileUpload\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <file-upload v-model=\"form.${field}\"/>\n        </el-form-item>\n#elseif($column.htmlType == \"editor\")\n        <el-form-item label=\"${comment}\">\n          <editor v-model=\"form.${field}\" :min-height=\"192\"/>\n        </el-form-item>\n#elseif($column.htmlType == \"select\" && \"\" != $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-select v-model=\"form.${field}\" placeholder=\"请选择${comment}\">\n            <el-option\n              v-for=\"dict in ${dictType}\"\n              :key=\"dict.value\"\n              :label=\"dict.label\"\n#if($column.javaType == \"Integer\" || $column.javaType == \"Long\")\n              :value=\"parseInt(dict.value)\"\n#else\n              :value=\"dict.value\"\n#end\n            ></el-option>\n          </el-select>\n        </el-form-item>\n#elseif($column.htmlType == \"select\" && $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-select v-model=\"form.${field}\" placeholder=\"请选择${comment}\">\n            <el-option label=\"请选择字典生成\" value=\"\" />\n          </el-select>\n        </el-form-item>\n#elseif($column.htmlType == \"checkbox\" && \"\" != $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-checkbox-group v-model=\"form.${field}\">\n            <el-checkbox\n              v-for=\"dict in ${dictType}\"\n              :key=\"dict.value\"\n              :label=\"dict.value\">\n              {{dict.label}}\n            </el-checkbox>\n          </el-checkbox-group>\n        </el-form-item>\n#elseif($column.htmlType == \"checkbox\" && $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-checkbox-group v-model=\"form.${field}\">\n            <el-checkbox>请选择字典生成</el-checkbox>\n          </el-checkbox-group>\n        </el-form-item>\n#elseif($column.htmlType == \"radio\" && \"\" != $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-radio-group v-model=\"form.${field}\">\n            <el-radio\n              v-for=\"dict in ${dictType}\"\n              :key=\"dict.value\"\n#if($column.javaType == \"Integer\" || $column.javaType == \"Long\")\n              :label=\"parseInt(dict.value)\"\n#else\n              :label=\"dict.value\"\n#end\n            >{{dict.label}}</el-radio>\n          </el-radio-group>\n        </el-form-item>\n#elseif($column.htmlType == \"radio\" && $dictType)\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-radio-group v-model=\"form.${field}\">\n            <el-radio label=\"1\">请选择字典生成</el-radio>\n          </el-radio-group>\n        </el-form-item>\n#elseif($column.htmlType == \"datetime\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-date-picker clearable\n            v-model=\"form.${field}\"\n            type=\"datetime\"\n            value-format=\"YYYY-MM-DD HH:mm:ss\"\n            placeholder=\"请选择${comment}\">\n          </el-date-picker>\n        </el-form-item>\n#elseif($column.htmlType == \"textarea\")\n        <el-form-item label=\"${comment}\" prop=\"${field}\">\n          <el-input v-model=\"form.${field}\" type=\"textarea\" placeholder=\"请输入内容\" />\n        </el-form-item>\n#end\n#end\n#end\n#if($table.sub)\n        <el-divider content-position=\"center\">${subTable.functionName}信息</el-divider>\n        <el-row :gutter=\"10\" class=\"mb8\">\n          <el-col :span=\"1.5\">\n            <el-button type=\"primary\" icon=\"Plus\" @click=\"handleAdd${subClassName}\">添加</el-button>\n          </el-col>\n          <el-col :span=\"1.5\">\n            <el-button type=\"danger\" icon=\"Delete\" @click=\"handleDelete${subClassName}\">删除</el-button>\n          </el-col>\n        </el-row>\n        <el-table :data=\"${subclassName}List\" :row-class-name=\"row${subClassName}Index\" @selection-change=\"handle${subClassName}SelectionChange\" ref=\"${subclassName}\">\n          <el-table-column type=\"selection\" width=\"50\" align=\"center\" />\n          <el-table-column label=\"序号\" align=\"center\" prop=\"index\" width=\"50\"/>\n#foreach($column in $subTable.columns)\n#set($javaField=$column.javaField)\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n#if($column.pk || $javaField == ${subTableFkclassName})\n#elseif($column.list && $column.htmlType == \"input\")\n          <el-table-column label=\"$comment\" prop=\"${javaField}\" width=\"150\">\n            <template #default=\"scope\">\n              <el-input v-model=\"scope.row.$javaField\" placeholder=\"请输入$comment\" />\n            </template>\n          </el-table-column>\n#elseif($column.list && $column.htmlType == \"datetime\")\n          <el-table-column label=\"$comment\" prop=\"${javaField}\" width=\"240\">\n            <template #default=\"scope\">\n              <el-date-picker clearable\n                v-model=\"scope.row.$javaField\"\n                type=\"date\"\n                value-format=\"YYYY-MM-DD\"\n                placeholder=\"请选择$comment\">\n              </el-date-picker>\n            </template>\n          </el-table-column>\n#elseif($column.list && ($column.htmlType == \"select\" || $column.htmlType == \"radio\") && \"\" != $column.dictType)\n          <el-table-column label=\"$comment\" prop=\"${javaField}\" width=\"150\">\n            <template #default=\"scope\">\n              <el-select v-model=\"scope.row.$javaField\" placeholder=\"请选择$comment\">\n                <el-option\n                  v-for=\"dict in $column.dictType\"\n                  :key=\"dict.value\"\n                  :label=\"dict.label\"\n                  :value=\"dict.value\"\n                ></el-option>\n              </el-select>\n            </template>\n          </el-table-column>\n#elseif($column.list && ($column.htmlType == \"select\" || $column.htmlType == \"radio\") && \"\" == $column.dictType)\n          <el-table-column label=\"$comment\" prop=\"${javaField}\" width=\"150\">\n            <template #default=\"scope\">\n              <el-select v-model=\"scope.row.$javaField\" placeholder=\"请选择$comment\">\n                <el-option label=\"请选择字典生成\" value=\"\" />\n              </el-select>\n            </template>\n          </el-table-column>\n#end\n#end\n        </el-table>\n#end\n      </el-form>\n      <template #footer>\n        <div class=\"dialog-footer\">\n          <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n          <el-button @click=\"cancel\">取 消</el-button>\n        </div>\n      </template>\n    </el-dialog>\n  </div>\n</template>\n\n<script setup name=\"${BusinessName}\">\nimport { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from \"@/api/${moduleName}/${businessName}\";\n\nconst { proxy } = getCurrentInstance();\n#if(${dicts} != '')\n#set($dictsNoSymbol=$dicts.replace(\"'\", \"\"))\nconst { ${dictsNoSymbol} } = proxy.useDict(${dicts});\n#end\n\nconst ${businessName}List = ref([]);\n#if($table.sub)\nconst ${subclassName}List = ref([]);\n#end\nconst open = ref(false);\nconst buttonLoading = ref(false);\nconst loading = ref(true);\nconst showSearch = ref(true);\nconst ids = ref([]);\n#if($table.sub)\nconst checked${subClassName} = ref([]);\n#end\nconst single = ref(true);\nconst multiple = ref(true);\nconst total = ref(0);\nconst title = ref(\"\");\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\nconst daterange${AttrName} = ref([]);\n#end\n#end\n\nconst data = reactive({\n  form: {},\n  queryParams: {\n    pageNum: 1,\n    pageSize: 10,\n#foreach ($column in $columns)\n#if($column.query)\n    $column.javaField: undefined#if($foreach.count != $columns.size()),#end\n#end\n#end\n  },\n  rules: {\n#foreach ($column in $columns)\n#if($column.required)\n#set($parentheseIndex=$column.columnComment.indexOf(\"（\"))\n#if($parentheseIndex != -1)\n#set($comment=$column.columnComment.substring(0, $parentheseIndex))\n#else\n#set($comment=$column.columnComment)\n#end\n    $column.javaField: [\n      { required: true, message: \"$comment不能为空\", trigger: #if($column.htmlType == \"select\" || $column.htmlType == \"radio\")\"change\"#else\"blur\"#end }\n    ]#if($foreach.count != $columns.size()),#end\n#end\n#end\n  }\n});\n\nconst { queryParams, form, rules } = toRefs(data);\n\n/** 查询${functionName}列表 */\nfunction getList() {\n  loading.value = true;\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n  queryParams.value.params = {};\n#break\n#end\n#end\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n  if (null != daterange${AttrName} && '' != daterange${AttrName}) {\n    queryParams.value.params[\"begin${AttrName}\"] = daterange${AttrName}.value[0];\n    queryParams.value.params[\"end${AttrName}\"] = daterange${AttrName}.value[1];\n  }\n#end\n#end\n  list${BusinessName}(queryParams.value).then(response => {\n    ${businessName}List.value = response.rows;\n    total.value = response.total;\n    loading.value = false;\n  });\n}\n\n// 取消按钮\nfunction cancel() {\n  open.value = false;\n  reset();\n}\n\n// 表单重置\nfunction reset() {\n  form.value = {\n#foreach ($column in $columns)\n#if($column.htmlType == \"checkbox\")\n    $column.javaField: []#if($foreach.count != $columns.size()),#end\n#else\n    $column.javaField: null#if($foreach.count != $columns.size()),#end\n#end\n#end\n  };\n#if($table.sub)\n  ${subclassName}List.value = [];\n#end\n  proxy.resetForm(\"${businessName}Ref\");\n}\n\n/** 搜索按钮操作 */\nfunction handleQuery() {\n  queryParams.value.pageNum = 1;\n  getList();\n}\n\n/** 重置按钮操作 */\nfunction resetQuery() {\n#foreach ($column in $columns)\n#if($column.htmlType == \"datetime\" && $column.queryType == \"BETWEEN\")\n#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n  daterange${AttrName}.value = [];\n#end\n#end\n  proxy.resetForm(\"queryRef\");\n  handleQuery();\n}\n\n// 多选框选中数据\nfunction handleSelectionChange(selection) {\n  ids.value = selection.map(item => item.${pkColumn.javaField});\n  single.value = selection.length != 1;\n  multiple.value = !selection.length;\n}\n\n/** 新增按钮操作 */\nfunction handleAdd() {\n  reset();\n  open.value = true;\n  title.value = \"添加${functionName}\";\n}\n\n/** 修改按钮操作 */\nfunction handleUpdate(row) {\n  loading.value = true\n  reset();\n  const _${pkColumn.javaField} = row.${pkColumn.javaField} || ids.value\n  get${BusinessName}(_${pkColumn.javaField}).then(response => {\n    loading.value = false;\n    form.value = response.data;\n#foreach ($column in $columns)\n#if($column.htmlType == \"checkbox\")\n    form.value.$column.javaField = form.value.${column.javaField}.split(\",\");\n#end\n#end\n#if($table.sub)\n    ${subclassName}List.value = response.data.${subclassName}List;\n#end\n    open.value = true;\n    title.value = \"修改${functionName}\";\n  });\n}\n\n/** 提交按钮 */\nfunction submitForm() {\n  proxy.#[[$]]#refs[\"${businessName}Ref\"].validate(valid => {\n    if (valid) {\n      buttonLoading.value = true;\n#foreach ($column in $columns)\n#if($column.htmlType == \"checkbox\")\n      form.value.$column.javaField = form.value.${column.javaField}.join(\",\");\n#end\n#end\n#if($table.sub)\n      form.value.${subclassName}List = ${subclassName}List.value;\n#end\n      if (form.value.${pkColumn.javaField} != null) {\n        update${BusinessName}(form.value).then(response => {\n          proxy.#[[$modal]]#.msgSuccess(\"修改成功\");\n          open.value = false;\n          getList();\n        }).finally(() => {\n          buttonLoading.value = false;\n        });\n      } else {\n        add${BusinessName}(form.value).then(response => {\n          proxy.#[[$modal]]#.msgSuccess(\"新增成功\");\n          open.value = false;\n          getList();\n        }).finally(() => {\n          buttonLoading.value = false;\n        });\n      }\n    }\n  });\n}\n\n/** 删除按钮操作 */\nfunction handleDelete(row) {\n  const _${pkColumn.javaField}s = row.${pkColumn.javaField} || ids.value;\n  proxy.#[[$modal]]#.confirm('是否确认删除${functionName}编号为\"' + _${pkColumn.javaField}s + '\"的数据项？').then(function() {\n    loading.value = true;\n    return del${BusinessName}(_${pkColumn.javaField}s);\n  }).then(() => {\n    loading.value = true;\n    getList();\n    proxy.#[[$modal]]#.msgSuccess(\"删除成功\");\n  }).catch(() => {\n  }).finally(() => {\n    loading.value = false;\n  });\n}\n\n#if($table.sub)\n/** ${subTable.functionName}序号 */\nfunction row${subClassName}Index({ row, rowIndex }) {\n  row.index = rowIndex + 1;\n}\n\n/** ${subTable.functionName}添加按钮操作 */\nfunction handleAdd${subClassName}() {\n  let obj = {};\n#foreach($column in $subTable.columns)\n#if($column.pk || $column.javaField == ${subTableFkclassName})\n#elseif($column.list && \"\" != $javaField)\n  obj.$column.javaField = \"\";\n#end\n#end\n  ${subclassName}List.value.push(obj);\n}\n\n/** ${subTable.functionName}删除按钮操作 */\nfunction handleDelete${subClassName}() {\n  if (checked${subClassName}.value.length == 0) {\n    proxy.#[[$modal]]#.msgError(\"请先选择要删除的${subTable.functionName}数据\");\n  } else {\n    const ${subclassName}s = ${subclassName}List.value;\n    const checked${subClassName}s = checked${subClassName}.value;\n    ${subclassName}List.value = ${subclassName}s.filter(function(item) {\n      return checked${subClassName}s.indexOf(item.index) == -1\n    });\n  }\n}\n\n/** 复选框选中数据 */\nfunction handle${subClassName}SelectionChange(selection) {\n  checked${subClassName}.value = selection.map(item => item.index)\n}\n\n#end\n/** 导出按钮操作 */\nfunction handleExport() {\n  proxy.download('${moduleName}/${businessName}/export', {\n    ...queryParams.value\n  }, `${businessName}_#[[${new Date().getTime()}]]#.xlsx`)\n}\n\ngetList();\n</script>\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt",
    "content": "如果使用的是Vue3前端，那么需要覆盖一下此目录的模板index.vue.vm、index-tree.vue.vm文件到上级vue目录。\n"
  },
  {
    "path": "ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"${packageName}.mapper.${ClassName}Mapper\">\n\n    <resultMap type=\"${packageName}.domain.${ClassName}\" id=\"${ClassName}Result\">\n#foreach ($column in $columns)\n        <result property=\"${column.javaField}\" column=\"${column.columnName}\"/>\n#end\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-job/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>ruoyi-vue-plus</artifactId>\n        <groupId>com.ruoyi</groupId>\n        <version>4.7.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>jar</packaging>\n    <artifactId>ruoyi-job</artifactId>\n\n    <description>\n        任务调度\n    </description>\n\n    <dependencies>\n\n        <!-- 通用工具-->\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>ruoyi-common</artifactId>\n        </dependency>\n\n        <!-- xxl-job-core -->\n        <dependency>\n            <groupId>com.xuxueli</groupId>\n            <artifactId>xxl-job-core</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n\n"
  },
  {
    "path": "ruoyi-job/src/main/java/top/flya/job/config/XxlJobConfig.java",
    "content": "package top.flya.job.config;\n\nimport top.flya.job.config.properties.XxlJobProperties;\nimport com.xxl.job.core.executor.impl.XxlJobSpringExecutor;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * xxl-job config\n *\n * @author Lion Li\n */\n@Slf4j\n@Configuration\n@EnableConfigurationProperties(XxlJobProperties.class)\n@AllArgsConstructor\n@ConditionalOnProperty(prefix = \"xxl.job\", name = \"enabled\", havingValue = \"true\")\npublic class XxlJobConfig {\n\n    private final XxlJobProperties xxlJobProperties;\n\n    @Bean\n    public XxlJobSpringExecutor xxlJobExecutor() {\n        log.info(\">>>>>>>>>>> xxl-job config init.\");\n        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();\n        xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdminAddresses());\n        xxlJobSpringExecutor.setAccessToken(xxlJobProperties.getAccessToken());\n        XxlJobProperties.Executor executor = xxlJobProperties.getExecutor();\n        xxlJobSpringExecutor.setAppname(executor.getAppname());\n        xxlJobSpringExecutor.setAddress(executor.getAddress());\n        xxlJobSpringExecutor.setIp(executor.getIp());\n        xxlJobSpringExecutor.setPort(executor.getPort());\n        xxlJobSpringExecutor.setLogPath(executor.getLogPath());\n        xxlJobSpringExecutor.setLogRetentionDays(executor.getLogRetentionDays());\n        return xxlJobSpringExecutor;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-job/src/main/java/top/flya/job/config/properties/XxlJobProperties.java",
    "content": "package top.flya.job.config.properties;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\n\n/**\n * xxljob配置类\n *\n * @author Lion Li\n */\n@Data\n@ConfigurationProperties(prefix = \"xxl.job\")\npublic class XxlJobProperties {\n\n    private Boolean enabled;\n\n    private String adminAddresses;\n\n    private String accessToken;\n\n    private Executor executor;\n\n    @Data\n    @NoArgsConstructor\n    public static class Executor {\n\n        private String appname;\n\n        private String address;\n\n        private String ip;\n\n        private int port;\n\n        private String logPath;\n\n        private int logRetentionDays;\n    }\n}\n"
  },
  {
    "path": "ruoyi-job/src/main/java/top/flya/job/service/SampleService.java",
    "content": "package top.flya.job.service;\n\nimport com.xxl.job.core.context.XxlJobHelper;\nimport com.xxl.job.core.handler.annotation.XxlJob;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedReader;\nimport java.io.DataOutputStream;\nimport java.io.InputStreamReader;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.util.Arrays;\n\n/**\n * XxlJob开发示例（Bean模式）\n * <p>\n * 开发步骤：\n * 1、任务开发：在Spring Bean实例中，开发Job方法；\n * 2、注解配置：为Job方法添加注解 \"@XxlJob(value=\"自定义jobhandler名称\", init = \"JobHandler初始化方法\", destroy = \"JobHandler销毁方法\")\"，注解value值对应的是调度中心新建任务的JobHandler属性的值。\n * 3、执行日志：需要通过 \"XxlJobHelper.log\" 打印执行日志；\n * 4、任务结果：默认任务结果为 \"成功\" 状态，不需要主动设置；如有诉求，比如设置任务结果为失败，可以通过 \"XxlJobHelper.handleFail/handleSuccess\" 自主设置任务结果；\n *\n * @author xuxueli 2019-12-11 21:52:51\n */\n@Slf4j\n@Service\npublic class SampleService {\n\n\n    /**\n     * 1、简单任务示例（Bean模式）\n     */\n    @XxlJob(\"demoJobHandler\")\n    public void demoJobHandler() throws Exception {\n        XxlJobHelper.log(\"XXL-JOB, Hello World.\");\n\n        for (int i = 0; i < 5; i++) {\n            XxlJobHelper.log(\"beat at:\" + i);\n        }\n        // default success\n    }\n\n\n    /**\n     * 2、分片广播任务\n     */\n    @XxlJob(\"shardingJobHandler\")\n    public void shardingJobHandler() throws Exception {\n\n        // 分片参数\n        int shardIndex = XxlJobHelper.getShardIndex();\n        int shardTotal = XxlJobHelper.getShardTotal();\n\n        XxlJobHelper.log(\"分片参数：当前分片序号 = {}, 总分片数 = {}\", shardIndex, shardTotal);\n\n        // 业务逻辑\n        for (int i = 0; i < shardTotal; i++) {\n            if (i == shardIndex) {\n                XxlJobHelper.log(\"第 {} 片, 命中分片开始处理\", i);\n            } else {\n                XxlJobHelper.log(\"第 {} 片, 忽略\", i);\n            }\n        }\n\n    }\n\n\n    /**\n     * 3、命令行任务\n     */\n    @XxlJob(\"commandJobHandler\")\n    public void commandJobHandler() throws Exception {\n        String command = XxlJobHelper.getJobParam();\n        int exitValue = -1;\n\n        BufferedReader bufferedReader = null;\n        try {\n            // command process\n            ProcessBuilder processBuilder = new ProcessBuilder();\n            processBuilder.command(command);\n            processBuilder.redirectErrorStream(true);\n\n            Process process = processBuilder.start();\n            //Process process = Runtime.getRuntime().exec(command);\n\n            BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream());\n            bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream));\n\n            // command log\n            String line;\n            while ((line = bufferedReader.readLine()) != null) {\n                XxlJobHelper.log(line);\n            }\n\n            // command exit\n            process.waitFor();\n            exitValue = process.exitValue();\n        } catch (Exception e) {\n            XxlJobHelper.log(e);\n        } finally {\n            if (bufferedReader != null) {\n                bufferedReader.close();\n            }\n        }\n\n        if (exitValue == 0) {\n            // default success\n        } else {\n            XxlJobHelper.handleFail(\"command exit value(\" + exitValue + \") is failed\");\n        }\n\n    }\n\n\n    /**\n     * 4、跨平台Http任务\n     * 参数示例：\n     * \"url: http://www.baidu.com\\n\" +\n     * \"method: get\\n\" +\n     * \"data: content\\n\";\n     */\n    @XxlJob(\"httpJobHandler\")\n    public void httpJobHandler() throws Exception {\n\n        // param parse\n        String param = XxlJobHelper.getJobParam();\n        if (param == null || param.trim().length() == 0) {\n            XxlJobHelper.log(\"param[\" + param + \"] invalid.\");\n\n            XxlJobHelper.handleFail();\n            return;\n        }\n\n        String[] httpParams = param.split(\"\\n\");\n        String url = null;\n        String method = null;\n        String data = null;\n        for (String httpParam : httpParams) {\n            if (httpParam.startsWith(\"url:\")) {\n                url = httpParam.substring(httpParam.indexOf(\"url:\") + 4).trim();\n            }\n            if (httpParam.startsWith(\"method:\")) {\n                method = httpParam.substring(httpParam.indexOf(\"method:\") + 7).trim().toUpperCase();\n            }\n            if (httpParam.startsWith(\"data:\")) {\n                data = httpParam.substring(httpParam.indexOf(\"data:\") + 5).trim();\n            }\n        }\n\n        // param valid\n        if (url == null || url.trim().length() == 0) {\n            XxlJobHelper.log(\"url[\" + url + \"] invalid.\");\n\n            XxlJobHelper.handleFail();\n            return;\n        }\n        if (method == null || !Arrays.asList(\"GET\", \"POST\").contains(method)) {\n            XxlJobHelper.log(\"method[\" + method + \"] invalid.\");\n\n            XxlJobHelper.handleFail();\n            return;\n        }\n        boolean isPostMethod = method.equals(\"POST\");\n\n        // request\n        HttpURLConnection connection = null;\n        BufferedReader bufferedReader = null;\n        try {\n            // connection\n            URL realUrl = new URL(url);\n            connection = (HttpURLConnection) realUrl.openConnection();\n\n            // connection setting\n            connection.setRequestMethod(method);\n            connection.setDoOutput(isPostMethod);\n            connection.setDoInput(true);\n            connection.setUseCaches(false);\n            connection.setReadTimeout(5 * 1000);\n            connection.setConnectTimeout(3 * 1000);\n            connection.setRequestProperty(\"connection\", \"Keep-Alive\");\n            connection.setRequestProperty(\"Content-Type\", \"application/json;charset=UTF-8\");\n            connection.setRequestProperty(\"Accept-Charset\", \"application/json;charset=UTF-8\");\n\n            // do connection\n            connection.connect();\n\n            // data\n            if (isPostMethod && data != null && data.trim().length() > 0) {\n                DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());\n                dataOutputStream.write(data.getBytes(\"UTF-8\"));\n                dataOutputStream.flush();\n                dataOutputStream.close();\n            }\n\n            // valid StatusCode\n            int statusCode = connection.getResponseCode();\n            if (statusCode != 200) {\n                throw new RuntimeException(\"Http Request StatusCode(\" + statusCode + \") Invalid.\");\n            }\n\n            // result\n            bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), \"UTF-8\"));\n            StringBuilder result = new StringBuilder();\n            String line;\n            while ((line = bufferedReader.readLine()) != null) {\n                result.append(line);\n            }\n            String responseMsg = result.toString();\n\n            XxlJobHelper.log(responseMsg);\n\n            return;\n        } catch (Exception e) {\n            XxlJobHelper.log(e);\n\n            XxlJobHelper.handleFail();\n            return;\n        } finally {\n            try {\n                if (bufferedReader != null) {\n                    bufferedReader.close();\n                }\n                if (connection != null) {\n                    connection.disconnect();\n                }\n            } catch (Exception e2) {\n                XxlJobHelper.log(e2);\n            }\n        }\n\n    }\n\n    /**\n     * 5、生命周期任务示例：任务初始化与销毁时，支持自定义相关逻辑；\n     */\n    @XxlJob(value = \"demoJobHandler2\", init = \"init\", destroy = \"destroy\")\n    public void demoJobHandler2() throws Exception {\n        XxlJobHelper.log(\"XXL-JOB, Hello World.\");\n    }\n\n    public void init() {\n        log.info(\"init\");\n    }\n\n    public void destroy() {\n        log.info(\"destory\");\n    }\n\n\n}\n"
  },
  {
    "path": "ruoyi-oss/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>ruoyi-vue-plus</artifactId>\n        <groupId>com.ruoyi</groupId>\n        <version>4.7.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ruoyi-oss</artifactId>\n\n    <description>\n        OSS对象存储模块\n    </description>\n\n    <dependencies>\n\n        <!-- 通用工具-->\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>ruoyi-common</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.amazonaws</groupId>\n            <artifactId>aws-java-sdk-s3</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "ruoyi-oss/src/main/java/top/flya/oss/constant/OssConstant.java",
    "content": "package top.flya.oss.constant;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 对象存储常量\n *\n * @author Lion Li\n */\npublic interface OssConstant {\n\n    /**\n     * 默认配置KEY\n     */\n    String DEFAULT_CONFIG_KEY = \"sys_oss:default_config\";\n\n    /**\n     * 预览列表资源开关Key\n     */\n    String PEREVIEW_LIST_RESOURCE_KEY = \"sys.oss.previewListResource\";\n\n    /**\n     * 系统数据ids\n     */\n    List<Long> SYSTEM_DATA_IDS = Arrays.asList(1L, 2L, 3L, 4L);\n\n    /**\n     * 云服务商\n     */\n    String[] CLOUD_SERVICE = new String[] {\"aliyun\", \"qcloud\", \"qiniu\", \"obs\"};\n\n    /**\n     * https 状态\n     */\n    String IS_HTTPS = \"Y\";\n\n}\n"
  },
  {
    "path": "ruoyi-oss/src/main/java/top/flya/oss/core/OssClient.java",
    "content": "package top.flya.oss.core;\n\nimport cn.hutool.core.io.IoUtil;\nimport cn.hutool.core.util.IdUtil;\nimport com.amazonaws.ClientConfiguration;\nimport com.amazonaws.HttpMethod;\nimport com.amazonaws.Protocol;\nimport com.amazonaws.auth.AWSCredentials;\nimport com.amazonaws.auth.AWSCredentialsProvider;\nimport com.amazonaws.auth.AWSStaticCredentialsProvider;\nimport com.amazonaws.auth.BasicAWSCredentials;\nimport com.amazonaws.client.builder.AwsClientBuilder;\nimport com.amazonaws.services.s3.AmazonS3;\nimport com.amazonaws.services.s3.AmazonS3Client;\nimport com.amazonaws.services.s3.AmazonS3ClientBuilder;\nimport com.amazonaws.services.s3.model.*;\nimport top.flya.common.utils.DateUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.oss.constant.OssConstant;\nimport top.flya.oss.entity.UploadResult;\nimport top.flya.oss.enumd.AccessPolicyType;\nimport top.flya.oss.enumd.PolicyType;\nimport top.flya.oss.exception.OssException;\nimport top.flya.oss.properties.OssProperties;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.util.Date;\n\n/**\n * S3 存储协议 所有兼容S3协议的云厂商均支持\n * 阿里云 腾讯云 七牛云 minio\n *\n * @author Lion Li\n */\npublic class OssClient {\n\n    private final String configKey;\n\n    private final OssProperties properties;\n\n    private final AmazonS3 client;\n\n    public OssClient(String configKey, OssProperties ossProperties) {\n        this.configKey = configKey;\n        this.properties = ossProperties;\n        try {\n            AwsClientBuilder.EndpointConfiguration endpointConfig =\n                new AwsClientBuilder.EndpointConfiguration(properties.getEndpoint(), properties.getRegion());\n\n            AWSCredentials credentials = new BasicAWSCredentials(properties.getAccessKey(), properties.getSecretKey());\n            AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials);\n            ClientConfiguration clientConfig = new ClientConfiguration();\n            if (OssConstant.IS_HTTPS.equals(properties.getIsHttps())) {\n                clientConfig.setProtocol(Protocol.HTTPS);\n            } else {\n                clientConfig.setProtocol(Protocol.HTTP);\n            }\n            AmazonS3ClientBuilder build = AmazonS3Client.builder()\n                .withEndpointConfiguration(endpointConfig)\n                .withClientConfiguration(clientConfig)\n                .withCredentials(credentialsProvider)\n                .disableChunkedEncoding();\n            if (!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)) {\n                // minio 使用https限制使用域名访问 需要此配置 站点填域名\n                build.enablePathStyleAccess();\n            }\n            this.client = build.build();\n\n            createBucket();\n        } catch (Exception e) {\n            if (e instanceof OssException) {\n                throw e;\n            }\n            throw new OssException(\"配置错误! 请检查系统配置:[\" + e.getMessage() + \"]\");\n        }\n    }\n\n    public void createBucket() {\n        try {\n            String bucketName = properties.getBucketName();\n            if (client.doesBucketExistV2(bucketName)) {\n                return;\n            }\n            CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);\n            AccessPolicyType accessPolicy = getAccessPolicy();\n            createBucketRequest.setCannedAcl(accessPolicy.getAcl());\n            client.createBucket(createBucketRequest);\n            client.setBucketPolicy(bucketName, getPolicy(bucketName, accessPolicy.getPolicyType()));\n        } catch (Exception e) {\n            throw new OssException(\"创建Bucket失败, 请核对配置信息:[\" + e.getMessage() + \"]\");\n        }\n    }\n\n    public UploadResult upload(byte[] data, String path, String contentType) {\n        return upload(new ByteArrayInputStream(data), path, contentType);\n    }\n\n    public UploadResult upload(InputStream inputStream, String path, String contentType) {\n        if (!(inputStream instanceof ByteArrayInputStream)) {\n            inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream));\n        }\n        try {\n            ObjectMetadata metadata = new ObjectMetadata();\n            metadata.setContentType(contentType);\n            metadata.setContentLength(inputStream.available());\n            PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata);\n            // 设置上传对象的 Acl 为公共读\n            putObjectRequest.setCannedAcl(getAccessPolicy().getAcl());\n            client.putObject(putObjectRequest);\n        } catch (Exception e) {\n            throw new OssException(\"上传文件失败，请检查配置信息:[\" + e.getMessage() + \"]\");\n        }\n        return UploadResult.builder().url(getUrl() + \"/\" + path).filename(path).build();\n    }\n\n    public void delete(String path) {\n        path = path.replace(getUrl() + \"/\", \"\");\n        try {\n            client.deleteObject(properties.getBucketName(), path);\n        } catch (Exception e) {\n            throw new OssException(\"删除文件失败，请检查配置信息:[\" + e.getMessage() + \"]\");\n        }\n    }\n\n    public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {\n        return upload(data, getPath(properties.getPrefix(), suffix), contentType);\n    }\n\n    public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {\n        return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);\n    }\n\n    /**\n     * 获取文件元数据\n     *\n     * @param path 完整文件路径\n     */\n    public ObjectMetadata getObjectMetadata(String path) {\n        path = path.replace(getUrl() + \"/\", \"\");\n        S3Object object = client.getObject(properties.getBucketName(), path);\n        return object.getObjectMetadata();\n    }\n\n    public InputStream getObjectContent(String path) {\n        path = path.replace(getUrl() + \"/\", \"\");\n        S3Object object = client.getObject(properties.getBucketName(), path);\n        return object.getObjectContent();\n    }\n\n    public String getUrl() {\n        String domain = properties.getDomain();\n        String endpoint = properties.getEndpoint();\n        String header = OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? \"https://\" : \"http://\";\n        // 云服务商直接返回\n        if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) {\n            if (StringUtils.isNotBlank(domain)) {\n                return header + domain;\n            }\n            return header + properties.getBucketName() + \".\" + endpoint;\n        }\n        // minio 单独处理\n        if (StringUtils.isNotBlank(domain)) {\n            return header + domain + \"/\" + properties.getBucketName();\n        }\n        return header + endpoint + \"/\" + properties.getBucketName();\n    }\n\n    public String getPath(String prefix, String suffix) {\n        // 生成uuid\n        String uuid = IdUtil.fastSimpleUUID();\n        // 文件路径\n        String path = DateUtils.datePath() + \"/\" + uuid;\n        if (StringUtils.isNotBlank(prefix)) {\n            path = prefix + \"/\" + path;\n        }\n        return path + suffix;\n    }\n\n\n    public String getConfigKey() {\n        return configKey;\n    }\n\n    /**\n     * 获取私有URL链接\n     *\n     * @param objectKey 对象KEY\n     * @param second    授权时间\n     */\n    public String getPrivateUrl(String objectKey, Integer second) {\n        GeneratePresignedUrlRequest generatePresignedUrlRequest =\n            new GeneratePresignedUrlRequest(properties.getBucketName(), objectKey)\n                .withMethod(HttpMethod.GET)\n                .withExpiration(new Date(System.currentTimeMillis() + 1000L * second));\n        URL url = client.generatePresignedUrl(generatePresignedUrlRequest);\n        return url.toString();\n    }\n\n    /**\n     * 检查配置是否相同\n     */\n    public boolean checkPropertiesSame(OssProperties properties) {\n        return this.properties.equals(properties);\n    }\n\n    /**\n     * 获取当前桶权限类型\n     *\n     * @return 当前桶权限类型code\n     */\n    public AccessPolicyType getAccessPolicy() {\n        return AccessPolicyType.getByType(properties.getAccessPolicy());\n    }\n\n    private static String getPolicy(String bucketName, PolicyType policyType) {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"{\\n\\\"Statement\\\": [\\n{\\n\\\"Action\\\": [\\n\");\n        if (policyType == PolicyType.WRITE) {\n            builder.append(\"\\\"s3:GetBucketLocation\\\",\\n\\\"s3:ListBucketMultipartUploads\\\"\\n\");\n        } else if (policyType == PolicyType.READ_WRITE) {\n            builder.append(\"\\\"s3:GetBucketLocation\\\",\\n\\\"s3:ListBucket\\\",\\n\\\"s3:ListBucketMultipartUploads\\\"\\n\");\n        } else {\n            builder.append(\"\\\"s3:GetBucketLocation\\\"\\n\");\n        }\n        builder.append(\"],\\n\\\"Effect\\\": \\\"Allow\\\",\\n\\\"Principal\\\": \\\"*\\\",\\n\\\"Resource\\\": \\\"arn:aws:s3:::\");\n        builder.append(bucketName);\n        builder.append(\"\\\"\\n},\\n\");\n        if (policyType == PolicyType.READ) {\n            builder.append(\"{\\n\\\"Action\\\": [\\n\\\"s3:ListBucket\\\"\\n],\\n\\\"Effect\\\": \\\"Deny\\\",\\n\\\"Principal\\\": \\\"*\\\",\\n\\\"Resource\\\": \\\"arn:aws:s3:::\");\n            builder.append(bucketName);\n            builder.append(\"\\\"\\n},\\n\");\n        }\n        builder.append(\"{\\n\\\"Action\\\": \");\n        switch (policyType) {\n            case WRITE:\n                builder.append(\"[\\n\\\"s3:AbortMultipartUpload\\\",\\n\\\"s3:DeleteObject\\\",\\n\\\"s3:ListMultipartUploadParts\\\",\\n\\\"s3:PutObject\\\"\\n],\\n\");\n                break;\n            case READ_WRITE:\n                builder.append(\"[\\n\\\"s3:AbortMultipartUpload\\\",\\n\\\"s3:DeleteObject\\\",\\n\\\"s3:GetObject\\\",\\n\\\"s3:ListMultipartUploadParts\\\",\\n\\\"s3:PutObject\\\"\\n],\\n\");\n                break;\n            default:\n                builder.append(\"\\\"s3:GetObject\\\",\\n\");\n                break;\n        }\n        builder.append(\"\\\"Effect\\\": \\\"Allow\\\",\\n\\\"Principal\\\": \\\"*\\\",\\n\\\"Resource\\\": \\\"arn:aws:s3:::\");\n        builder.append(bucketName);\n        builder.append(\"/*\\\"\\n}\\n],\\n\\\"Version\\\": \\\"2012-10-17\\\"\\n}\\n\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-oss/src/main/java/top/flya/oss/entity/UploadResult.java",
    "content": "package top.flya.oss.entity;\n\nimport lombok.Builder;\nimport lombok.Data;\n\n/**\n * 上传返回体\n *\n * @author Lion Li\n */\n@Data\n@Builder\npublic class UploadResult {\n\n    /**\n     * 文件路径\n     */\n    private String url;\n\n    /**\n     * 文件名\n     */\n    private String filename;\n}\n"
  },
  {
    "path": "ruoyi-oss/src/main/java/top/flya/oss/enumd/AccessPolicyType.java",
    "content": "package top.flya.oss.enumd;\n\nimport com.amazonaws.services.s3.model.CannedAccessControlList;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 桶访问策略配置\n *\n * @author 陈賝\n */\n@Getter\n@AllArgsConstructor\npublic enum AccessPolicyType {\n\n    /**\n     * private\n     */\n    PRIVATE(\"0\", CannedAccessControlList.Private, PolicyType.WRITE),\n\n    /**\n     * public\n     */\n    PUBLIC(\"1\", CannedAccessControlList.PublicRead, PolicyType.READ),\n\n    /**\n     * custom\n     */\n    CUSTOM(\"2\",CannedAccessControlList.PublicRead, PolicyType.READ);\n\n    /**\n     * 桶 权限类型\n     */\n    private final String type;\n\n    /**\n     * 文件对象 权限类型\n     */\n    private final CannedAccessControlList acl;\n\n    /**\n     * 桶策略类型\n     */\n    private final PolicyType policyType;\n\n    public static AccessPolicyType getByType(String type) {\n        for (AccessPolicyType value : values()) {\n            if (value.getType().equals(type)) {\n                return value;\n            }\n        }\n        throw new RuntimeException(\"'type' not found By \" + type);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-oss/src/main/java/top/flya/oss/enumd/PolicyType.java",
    "content": "package top.flya.oss.enumd;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * minio策略配置\n *\n * @author Lion Li\n */\n@Getter\n@AllArgsConstructor\npublic enum PolicyType {\n\n    /**\n     * 只读\n     */\n    READ(\"read-only\"),\n\n    /**\n     * 只写\n     */\n    WRITE(\"write-only\"),\n\n    /**\n     * 读写\n     */\n    READ_WRITE(\"read-write\");\n\n    /**\n     * 类型\n     */\n    private final String type;\n\n}\n"
  },
  {
    "path": "ruoyi-oss/src/main/java/top/flya/oss/exception/OssException.java",
    "content": "package top.flya.oss.exception;\n\n/**\n * OSS异常类\n *\n * @author Lion Li\n */\npublic class OssException extends RuntimeException {\n\n    private static final long serialVersionUID = 1L;\n\n    public OssException(String msg) {\n        super(msg);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-oss/src/main/java/top/flya/oss/factory/OssFactory.java",
    "content": "package top.flya.oss.factory;\n\nimport top.flya.common.constant.CacheNames;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.redis.CacheUtils;\nimport top.flya.common.utils.redis.RedisUtils;\nimport top.flya.oss.constant.OssConstant;\nimport top.flya.oss.core.OssClient;\nimport top.flya.oss.exception.OssException;\nimport top.flya.oss.properties.OssProperties;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 文件上传Factory\n *\n * @author Lion Li\n */\n@Slf4j\npublic class OssFactory {\n\n    private static final Map<String, OssClient> CLIENT_CACHE = new ConcurrentHashMap<>();\n\n    /**\n     * 获取默认实例\n     */\n    public static OssClient instance() {\n        // 获取redis 默认类型\n        String configKey = RedisUtils.getCacheObject(OssConstant.DEFAULT_CONFIG_KEY);\n        if (StringUtils.isEmpty(configKey)) {\n            throw new OssException(\"文件存储服务类型无法找到!\");\n        }\n        return instance(configKey);\n    }\n\n    /**\n     * 根据类型获取实例\n     */\n    public static OssClient instance(String configKey) {\n        String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey);\n        if (json == null) {\n            throw new OssException(\"系统异常, '\" + configKey + \"'配置信息不存在!\");\n        }\n        OssProperties properties = JsonUtils.parseObject(json, OssProperties.class);\n        OssClient client = CLIENT_CACHE.get(configKey);\n        if (client == null) {\n            CLIENT_CACHE.put(configKey, new OssClient(configKey, properties));\n            log.info(\"创建OSS实例 key => {}\", configKey);\n            return CLIENT_CACHE.get(configKey);\n        }\n        // 配置不相同则重新构建\n        if (!client.checkPropertiesSame(properties)) {\n            CLIENT_CACHE.put(configKey, new OssClient(configKey, properties));\n            log.info(\"重载OSS实例 key => {}\", configKey);\n            return CLIENT_CACHE.get(configKey);\n        }\n        return client;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-oss/src/main/java/top/flya/oss/properties/OssProperties.java",
    "content": "package top.flya.oss.properties;\n\nimport lombok.Data;\n\n/**\n * OSS对象存储 配置属性\n *\n * @author Lion Li\n */\n@Data\npublic class OssProperties {\n\n    /**\n     * 访问站点\n     */\n    private String endpoint;\n\n    /**\n     * 自定义域名\n     */\n    private String domain;\n\n    /**\n     * 前缀\n     */\n    private String prefix;\n\n    /**\n     * ACCESS_KEY\n     */\n    private String accessKey;\n\n    /**\n     * SECRET_KEY\n     */\n    private String secretKey;\n\n    /**\n     * 存储空间名\n     */\n    private String bucketName;\n\n    /**\n     * 存储区域\n     */\n    private String region;\n\n    /**\n     * 是否https（Y=是,N=否）\n     */\n    private String isHttps;\n\n    /**\n     * 桶权限类型(0private 1public 2custom)\n     */\n    private String accessPolicy;\n\n}\n"
  },
  {
    "path": "ruoyi-sms/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>ruoyi-vue-plus</artifactId>\n        <groupId>com.ruoyi</groupId>\n        <version>4.7.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ruoyi-sms</artifactId>\n\n    <description>\n        SMS短信模块\n    </description>\n\n    <dependencies>\n\n        <!-- 通用工具-->\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>ruoyi-common</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.aliyun</groupId>\n            <artifactId>dysmsapi20170525</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <dependency>\n            <groupId>com.tencentcloudapi</groupId>\n            <artifactId>tencentcloud-sdk-java-sms</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "ruoyi-sms/src/main/java/top/flya/sms/config/SmsConfig.java",
    "content": "package top.flya.sms.config;\n\nimport top.flya.sms.config.properties.SmsProperties;\nimport top.flya.sms.core.AliyunSmsTemplate;\nimport top.flya.sms.core.SmsTemplate;\nimport top.flya.sms.core.TencentSmsTemplate;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * 短信配置类\n *\n * @author Lion Li\n * @version 4.2.0\n */\n@Configuration\npublic class SmsConfig {\n\n    @Configuration\n    @ConditionalOnProperty(value = \"sms.enabled\", havingValue = \"true\")\n    @ConditionalOnClass(com.aliyun.dysmsapi20170525.Client.class)\n    static class AliyunSmsConfig {\n\n        @Bean\n        public SmsTemplate aliyunSmsTemplate(SmsProperties smsProperties) {\n            return new AliyunSmsTemplate(smsProperties);\n        }\n\n    }\n\n    @Configuration\n    @ConditionalOnProperty(value = \"sms.enabled\", havingValue = \"true\")\n    @ConditionalOnClass(com.tencentcloudapi.sms.v20190711.SmsClient.class)\n    static class TencentSmsConfig {\n\n        @Bean\n        public SmsTemplate tencentSmsTemplate(SmsProperties smsProperties) {\n            return new TencentSmsTemplate(smsProperties);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-sms/src/main/java/top/flya/sms/config/properties/SmsProperties.java",
    "content": "package top.flya.sms.config.properties;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n/**\n * SMS短信 配置属性\n *\n * @author Lion Li\n * @version 4.2.0\n */\n@Data\n@Component\n@ConfigurationProperties(prefix = \"sms\")\npublic class SmsProperties {\n\n    private Boolean enabled;\n\n    /**\n     * 配置节点\n     * 阿里云 dysmsapi.aliyuncs.com\n     * 腾讯云 sms.tencentcloudapi.com\n     */\n    private String endpoint;\n\n    /**\n     * key\n     */\n    private String accessKeyId;\n\n    /**\n     * 密匙\n     */\n    private String accessKeySecret;\n\n    /*\n     * 短信签名\n     */\n    private String signName;\n\n    /**\n     * 短信应用ID (腾讯专属)\n     */\n    private String sdkAppId;\n\n}\n"
  },
  {
    "path": "ruoyi-sms/src/main/java/top/flya/sms/core/AliyunSmsTemplate.java",
    "content": "package top.flya.sms.core;\n\nimport com.aliyun.dysmsapi20170525.Client;\nimport com.aliyun.dysmsapi20170525.models.SendSmsRequest;\nimport com.aliyun.dysmsapi20170525.models.SendSmsResponse;\nimport com.aliyun.teaopenapi.models.Config;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.sms.config.properties.SmsProperties;\nimport top.flya.sms.entity.SmsResult;\nimport top.flya.sms.exception.SmsException;\nimport lombok.SneakyThrows;\n\nimport java.util.Map;\n\n/**\n * Aliyun 短信模板\n *\n * @author Lion Li\n * @version 4.2.0\n */\npublic class AliyunSmsTemplate implements SmsTemplate {\n\n    private SmsProperties properties;\n\n    private Client client;\n\n    @SneakyThrows(Exception.class)\n    public AliyunSmsTemplate(SmsProperties smsProperties) {\n        this.properties = smsProperties;\n        Config config = new Config()\n            // 您的AccessKey ID\n            .setAccessKeyId(smsProperties.getAccessKeyId())\n            // 您的AccessKey Secret\n            .setAccessKeySecret(smsProperties.getAccessKeySecret())\n            // 访问的域名\n            .setEndpoint(smsProperties.getEndpoint());\n        this.client = new Client(config);\n    }\n\n    @Override\n    public SmsResult send(String phones, String templateId, Map<String, String> param) {\n        if (StringUtils.isBlank(phones)) {\n            throw new SmsException(\"手机号不能为空\");\n        }\n        if (StringUtils.isBlank(templateId)) {\n            throw new SmsException(\"模板ID不能为空\");\n        }\n        SendSmsRequest req = new SendSmsRequest()\n            .setPhoneNumbers(phones)\n            .setSignName(properties.getSignName())\n            .setTemplateCode(templateId)\n            .setTemplateParam(JsonUtils.toJsonString(param));\n        try {\n            SendSmsResponse resp = client.sendSms(req);\n            return SmsResult.builder()\n                .isSuccess(\"OK\".equals(resp.getBody().getCode()))\n                .message(resp.getBody().getMessage())\n                .response(JsonUtils.toJsonString(resp))\n                .build();\n        } catch (Exception e) {\n            throw new SmsException(e.getMessage());\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-sms/src/main/java/top/flya/sms/core/SmsTemplate.java",
    "content": "package top.flya.sms.core;\n\nimport top.flya.sms.entity.SmsResult;\n\nimport java.util.Map;\n\n/**\n * 短信模板\n *\n * @author Lion Li\n * @version 4.2.0\n */\npublic interface SmsTemplate {\n\n    /**\n     * 发送短信\n     *\n     * @param phones     电话号(多个逗号分割)\n     * @param templateId 模板id\n     * @param param      模板对应参数\n     *                   阿里 需使用 模板变量名称对应内容 例如: code=1234\n     *                   腾讯 需使用 模板变量顺序对应内容 例如: 1=1234, 1为模板内第一个参数\n     */\n    SmsResult send(String phones, String templateId, Map<String, String> param);\n\n}\n"
  },
  {
    "path": "ruoyi-sms/src/main/java/top/flya/sms/core/TencentSmsTemplate.java",
    "content": "package top.flya.sms.core;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.sms.config.properties.SmsProperties;\nimport top.flya.sms.entity.SmsResult;\nimport top.flya.sms.exception.SmsException;\nimport com.tencentcloudapi.common.Credential;\nimport com.tencentcloudapi.common.profile.ClientProfile;\nimport com.tencentcloudapi.common.profile.HttpProfile;\nimport com.tencentcloudapi.sms.v20190711.SmsClient;\nimport com.tencentcloudapi.sms.v20190711.models.SendSmsRequest;\nimport com.tencentcloudapi.sms.v20190711.models.SendSmsResponse;\nimport com.tencentcloudapi.sms.v20190711.models.SendStatus;\nimport lombok.SneakyThrows;\n\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n/**\n * Tencent 短信模板\n *\n * @author Lion Li\n * @version 4.2.0\n */\npublic class TencentSmsTemplate implements SmsTemplate {\n\n    private SmsProperties properties;\n\n    private SmsClient client;\n\n    @SneakyThrows(Exception.class)\n    public TencentSmsTemplate(SmsProperties smsProperties) {\n        this.properties = smsProperties;\n        Credential credential = new Credential(smsProperties.getAccessKeyId(), smsProperties.getAccessKeySecret());\n        HttpProfile httpProfile = new HttpProfile();\n        httpProfile.setEndpoint(smsProperties.getEndpoint());\n        ClientProfile clientProfile = new ClientProfile();\n        clientProfile.setHttpProfile(httpProfile);\n        this.client = new SmsClient(credential, \"\", clientProfile);\n    }\n\n    @Override\n    public SmsResult send(String phones, String templateId, Map<String, String> param) {\n        if (StringUtils.isBlank(phones)) {\n            throw new SmsException(\"手机号不能为空\");\n        }\n        if (StringUtils.isBlank(templateId)) {\n            throw new SmsException(\"模板ID不能为空\");\n        }\n        SendSmsRequest req = new SendSmsRequest();\n        Set<String> set = Arrays.stream(phones.split(StringUtils.SEPARATOR)).map(p -> \"+86\" + p).collect(Collectors.toSet());\n        req.setPhoneNumberSet(ArrayUtil.toArray(set, String.class));\n        if (CollUtil.isNotEmpty(param)) {\n            req.setTemplateParamSet(ArrayUtil.toArray(param.values(), String.class));\n        }\n        req.setTemplateID(templateId);\n        req.setSign(properties.getSignName());\n        req.setSmsSdkAppid(properties.getSdkAppId());\n        try {\n            SendSmsResponse resp = client.SendSms(req);\n            SmsResult.SmsResultBuilder builder = SmsResult.builder()\n                .isSuccess(true)\n                .message(\"send success\")\n                .response(JsonUtils.toJsonString(resp));\n            for (SendStatus sendStatus : resp.getSendStatusSet()) {\n                if (!\"Ok\".equals(sendStatus.getCode())) {\n                    builder.isSuccess(false).message(sendStatus.getMessage());\n                    break;\n                }\n            }\n            return builder.build();\n        } catch (Exception e) {\n            throw new SmsException(e.getMessage());\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-sms/src/main/java/top/flya/sms/entity/SmsResult.java",
    "content": "package top.flya.sms.entity;\n\nimport lombok.Builder;\nimport lombok.Data;\n\n/**\n * 上传返回体\n *\n * @author Lion Li\n */\n@Data\n@Builder\npublic class SmsResult {\n\n    /**\n     * 是否成功\n     */\n    private boolean isSuccess;\n\n    /**\n     * 响应消息\n     */\n    private String message;\n\n    /**\n     * 实际响应体\n     * <p>\n     * 可自行转换为 SDK 对应的 SendSmsResponse\n     */\n    private String response;\n}\n"
  },
  {
    "path": "ruoyi-sms/src/main/java/top/flya/sms/exception/SmsException.java",
    "content": "package top.flya.sms.exception;\n\n/**\n * Sms异常类\n *\n * @author Lion Li\n */\npublic class SmsException extends RuntimeException {\n\n    private static final long serialVersionUID = 1L;\n\n    public SmsException(String msg) {\n        super(msg);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-system/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>ruoyi-vue-plus</artifactId>\n        <groupId>com.ruoyi</groupId>\n        <version>4.7.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ruoyi-system</artifactId>\n\n    <description>\n        system系统模块\n    </description>\n\n    <dependencies>\n\n        <!-- 通用工具-->\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>ruoyi-common</artifactId>\n        </dependency>\n\n        <!-- OSS功能模块 -->\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>ruoyi-oss</artifactId>\n        </dependency>\n\n        <!-- SMS功能模块 -->\n        <dependency>\n            <groupId>com.ruoyi</groupId>\n            <artifactId>ruoyi-sms</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/SysCache.java",
    "content": "package top.flya.system.domain;\n\nimport top.flya.common.utils.StringUtils;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * 缓存信息\n *\n * @author Lion Li\n */\n@Data\n@NoArgsConstructor\npublic class SysCache {\n\n    /**\n     * 缓存名称\n     */\n    private String cacheName = \"\";\n\n    /**\n     * 缓存键名\n     */\n    private String cacheKey = \"\";\n\n    /**\n     * 缓存内容\n     */\n    private String cacheValue = \"\";\n\n    /**\n     * 备注\n     */\n    private String remark = \"\";\n\n    public SysCache(String cacheName, String remark) {\n        this.cacheName = cacheName;\n        this.remark = remark;\n    }\n\n    public SysCache(String cacheName, String cacheKey, String cacheValue) {\n        this.cacheName = StringUtils.replace(cacheName, \":\", \"\");\n        this.cacheKey = StringUtils.replace(cacheKey, cacheName, \"\");\n        this.cacheValue = cacheValue;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/SysConfig.java",
    "content": "package top.flya.system.domain;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport top.flya.common.core.domain.BaseEntity;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.Size;\n\n/**\n * 参数配置表 sys_config\n *\n * @author Lion Li\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"sys_config\")\n@ExcelIgnoreUnannotated\npublic class SysConfig extends BaseEntity {\n\n    /**\n     * 参数主键\n     */\n    @ExcelProperty(value = \"参数主键\")\n    @TableId(value = \"config_id\")\n    private Long configId;\n\n    /**\n     * 参数名称\n     */\n    @ExcelProperty(value = \"参数名称\")\n    @NotBlank(message = \"参数名称不能为空\")\n    @Size(min = 0, max = 100, message = \"参数名称不能超过{max}个字符\")\n    private String configName;\n\n    /**\n     * 参数键名\n     */\n    @ExcelProperty(value = \"参数键名\")\n    @NotBlank(message = \"参数键名长度不能为空\")\n    @Size(min = 0, max = 100, message = \"参数键名长度不能超过{max}个字符\")\n    private String configKey;\n\n    /**\n     * 参数键值\n     */\n    @ExcelProperty(value = \"参数键值\")\n    @NotBlank(message = \"参数键值不能为空\")\n    @Size(min = 0, max = 500, message = \"参数键值长度不能超过{max}个字符\")\n    private String configValue;\n\n    /**\n     * 系统内置（Y是 N否）\n     */\n    @ExcelProperty(value = \"系统内置\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"sys_yes_no\")\n    private String configType;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/SysLogininfor.java",
    "content": "package top.flya.system.domain;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 系统访问记录表 sys_logininfor\n *\n * @author Lion Li\n */\n\n@Data\n@TableName(\"sys_logininfor\")\n@ExcelIgnoreUnannotated\npublic class SysLogininfor implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * ID\n     */\n    @ExcelProperty(value = \"序号\")\n    @TableId(value = \"info_id\")\n    private Long infoId;\n\n    /**\n     * 用户账号\n     */\n    @ExcelProperty(value = \"用户账号\")\n    private String userName;\n\n    /**\n     * 登录状态 0成功 1失败\n     */\n    @ExcelProperty(value = \"登录状态\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"sys_common_status\")\n    private String status;\n\n    /**\n     * 登录IP地址\n     */\n    @ExcelProperty(value = \"登录地址\")\n    private String ipaddr;\n\n    /**\n     * 登录地点\n     */\n    @ExcelProperty(value = \"登录地点\")\n    private String loginLocation;\n\n    /**\n     * 浏览器类型\n     */\n    @ExcelProperty(value = \"浏览器\")\n    private String browser;\n\n    /**\n     * 操作系统\n     */\n    @ExcelProperty(value = \"操作系统\")\n    private String os;\n\n    /**\n     * 提示消息\n     */\n    @ExcelProperty(value = \"提示消息\")\n    private String msg;\n\n    /**\n     * 访问时间\n     */\n    @ExcelProperty(value = \"访问时间\")\n    private Date loginTime;\n\n    /**\n     * 请求参数\n     */\n    @TableField(exist = false)\n    private Map<String, Object> params = new HashMap<>();\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/SysNotice.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.xss.Xss;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.Size;\n\n\n/**\n * 通知公告表 sys_notice\n *\n * @author Lion Li\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"sys_notice\")\npublic class SysNotice extends BaseEntity {\n\n    /**\n     * 公告ID\n     */\n    @TableId(value = \"notice_id\")\n    private Long noticeId;\n\n    /**\n     * 公告标题\n     */\n    @Xss(message = \"公告标题不能包含脚本字符\")\n    @NotBlank(message = \"公告标题不能为空\")\n    @Size(min = 0, max = 50, message = \"公告标题不能超过{max}个字符\")\n    private String noticeTitle;\n\n    /**\n     * 公告类型（1通知 2公告）\n     */\n    private String noticeType;\n\n    /**\n     * 公告内容\n     */\n    private String noticeContent;\n\n    /**\n     * 公告状态（0正常 1关闭）\n     */\n    private String status;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/SysOperLog.java",
    "content": "package top.flya.system.domain;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 操作日志记录表 oper_log\n *\n * @author Lion Li\n */\n\n@Data\n@TableName(\"sys_oper_log\")\n@ExcelIgnoreUnannotated\npublic class SysOperLog implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 日志主键\n     */\n    @ExcelProperty(value = \"日志主键\")\n    @TableId(value = \"oper_id\")\n    private Long operId;\n\n    /**\n     * 操作模块\n     */\n    @ExcelProperty(value = \"操作模块\")\n    private String title;\n\n    /**\n     * 业务类型（0其它 1新增 2修改 3删除）\n     */\n    @ExcelProperty(value = \"业务类型\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"sys_oper_type\")\n    private Integer businessType;\n\n    /**\n     * 业务类型数组\n     */\n    @TableField(exist = false)\n    private Integer[] businessTypes;\n\n    /**\n     * 请求方法\n     */\n    @ExcelProperty(value = \"请求方法\")\n    private String method;\n\n    /**\n     * 请求方式\n     */\n    @ExcelProperty(value = \"请求方式\")\n    private String requestMethod;\n\n    /**\n     * 操作类别（0其它 1后台用户 2手机端用户）\n     */\n    @ExcelProperty(value = \"操作类别\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(readConverterExp = \"0=其它,1=后台用户,2=手机端用户\")\n    private Integer operatorType;\n\n    /**\n     * 操作人员\n     */\n    @ExcelProperty(value = \"操作人员\")\n    private String operName;\n\n    /**\n     * 部门名称\n     */\n    @ExcelProperty(value = \"部门名称\")\n    private String deptName;\n\n    /**\n     * 请求url\n     */\n    @ExcelProperty(value = \"请求地址\")\n    private String operUrl;\n\n    /**\n     * 操作地址\n     */\n    @ExcelProperty(value = \"操作地址\")\n    private String operIp;\n\n    /**\n     * 操作地点\n     */\n    @ExcelProperty(value = \"操作地点\")\n    private String operLocation;\n\n    /**\n     * 请求参数\n     */\n    @ExcelProperty(value = \"请求参数\")\n    private String operParam;\n\n    /**\n     * 返回参数\n     */\n    @ExcelProperty(value = \"返回参数\")\n    private String jsonResult;\n\n    /**\n     * 操作状态（0正常 1异常）\n     */\n    @ExcelProperty(value = \"状态\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"sys_common_status\")\n    private Integer status;\n\n    /**\n     * 错误消息\n     */\n    @ExcelProperty(value = \"错误消息\")\n    private String errorMsg;\n\n    /**\n     * 操作时间\n     */\n    @ExcelProperty(value = \"操作时间\")\n    private Date operTime;\n\n    /**\n     * 请求参数\n     */\n    @TableField(exist = false)\n    private Map<String, Object> params = new HashMap<>();\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/SysOss.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport top.flya.common.core.domain.BaseEntity;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n/**\n * OSS对象存储对象\n *\n * @author Lion Li\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"sys_oss\")\npublic class SysOss extends BaseEntity {\n\n    /**\n     * 对象存储主键\n     */\n    @TableId(value = \"oss_id\")\n    private Long ossId;\n\n    /**\n     * 文件名\n     */\n    private String fileName;\n\n    /**\n     * 原名\n     */\n    private String originalName;\n\n    /**\n     * 文件后缀名\n     */\n    private String fileSuffix;\n\n    /**\n     * URL地址\n     */\n    private String url;\n\n    /**\n     * 服务商\n     */\n    private String service;\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/SysOssConfig.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport top.flya.common.core.domain.BaseEntity;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n/**\n * 对象存储配置对象 sys_oss_config\n *\n * @author Lion Li\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"sys_oss_config\")\npublic class SysOssConfig extends BaseEntity {\n\n    /**\n     * 主建\n     */\n    @TableId(value = \"oss_config_id\")\n    private Long ossConfigId;\n\n    /**\n     * 配置key\n     */\n    private String configKey;\n\n    /**\n     * accessKey\n     */\n    private String accessKey;\n\n    /**\n     * 秘钥\n     */\n    private String secretKey;\n\n    /**\n     * 桶名称\n     */\n    private String bucketName;\n\n    /**\n     * 前缀\n     */\n    private String prefix;\n\n    /**\n     * 访问站点\n     */\n    private String endpoint;\n\n    /**\n     * 自定义域名\n     */\n    private String domain;\n\n    /**\n     * 是否https（0否 1是）\n     */\n    private String isHttps;\n\n    /**\n     * 域\n     */\n    private String region;\n\n    /**\n     * 是否默认（0=是,1=否）\n     */\n    private String status;\n\n    /**\n     * 扩展字段\n     */\n    private String ext1;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 桶权限类型(0private 1public 2custom)\n     */\n    private String accessPolicy;\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/SysPost.java",
    "content": "package top.flya.system.domain;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport top.flya.common.core.domain.BaseEntity;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\n\n/**\n * 岗位表 sys_post\n *\n * @author Lion Li\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\n@TableName(\"sys_post\")\n@ExcelIgnoreUnannotated\npublic class SysPost extends BaseEntity {\n\n    /**\n     * 岗位序号\n     */\n    @ExcelProperty(value = \"岗位序号\")\n    @TableId(value = \"post_id\")\n    private Long postId;\n\n    /**\n     * 岗位编码\n     */\n    @ExcelProperty(value = \"岗位编码\")\n    @NotBlank(message = \"岗位编码不能为空\")\n    @Size(min = 0, max = 64, message = \"岗位编码长度不能超过{max}个字符\")\n    private String postCode;\n\n    /**\n     * 岗位名称\n     */\n    @ExcelProperty(value = \"岗位名称\")\n    @NotBlank(message = \"岗位名称不能为空\")\n    @Size(min = 0, max = 50, message = \"岗位名称长度不能超过{max}个字符\")\n    private String postName;\n\n    /**\n     * 岗位排序\n     */\n    @ExcelProperty(value = \"岗位排序\")\n    @NotNull(message = \"显示顺序不能为空\")\n    private Integer postSort;\n\n    /**\n     * 状态（0正常 1停用）\n     */\n    @ExcelProperty(value = \"状态\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"sys_normal_disable\")\n    private String status;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 用户是否存在此岗位标识 默认不存在\n     */\n    @TableField(exist = false)\n    private boolean flag = false;\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/SysRoleDept.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * 角色和部门关联 sys_role_dept\n *\n * @author Lion Li\n */\n\n@Data\n@TableName(\"sys_role_dept\")\npublic class SysRoleDept {\n\n    /**\n     * 角色ID\n     */\n    @TableId(type = IdType.INPUT)\n    private Long roleId;\n\n    /**\n     * 部门ID\n     */\n    private Long deptId;\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/SysRoleMenu.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * 角色和菜单关联 sys_role_menu\n *\n * @author Lion Li\n */\n\n@Data\n@TableName(\"sys_role_menu\")\npublic class SysRoleMenu {\n\n    /**\n     * 角色ID\n     */\n    @TableId(type = IdType.INPUT)\n    private Long roleId;\n\n    /**\n     * 菜单ID\n     */\n    private Long menuId;\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/SysUserOnline.java",
    "content": "package top.flya.system.domain;\n\nimport lombok.Data;\n\n/**\n * 当前在线会话\n *\n * @author Lion Li\n */\n\n@Data\npublic class SysUserOnline {\n\n    /**\n     * 会话编号\n     */\n    private String tokenId;\n\n    /**\n     * 部门名称\n     */\n    private String deptName;\n\n    /**\n     * 用户名称\n     */\n    private String userName;\n\n    /**\n     * 登录IP地址\n     */\n    private String ipaddr;\n\n    /**\n     * 登录地址\n     */\n    private String loginLocation;\n\n    /**\n     * 浏览器类型\n     */\n    private String browser;\n\n    /**\n     * 操作系统\n     */\n    private String os;\n\n    /**\n     * 登录时间\n     */\n    private Long loginTime;\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/SysUserPost.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * 用户和岗位关联 sys_user_post\n *\n * @author Lion Li\n */\n\n@Data\n@TableName(\"sys_user_post\")\npublic class SysUserPost {\n\n    /**\n     * 用户ID\n     */\n    @TableId(type = IdType.INPUT)\n    private Long userId;\n\n    /**\n     * 岗位ID\n     */\n    private Long postId;\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/SysUserRole.java",
    "content": "package top.flya.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * 用户和角色关联 sys_user_role\n *\n * @author Lion Li\n */\n\n@Data\n@TableName(\"sys_user_role\")\npublic class SysUserRole {\n\n    /**\n     * 用户ID\n     */\n    @TableId(type = IdType.INPUT)\n    private Long userId;\n\n    /**\n     * 角色ID\n     */\n    private Long roleId;\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/bo/SysOssBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport top.flya.common.core.domain.BaseEntity;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n/**\n * OSS对象存储分页查询对象 sys_oss\n *\n * @author Lion Li\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class SysOssBo extends BaseEntity {\n\n    /**\n     * ossId\n     */\n    private Long ossId;\n\n    /**\n     * 文件名\n     */\n    private String fileName;\n\n    /**\n     * 原名\n     */\n    private String originalName;\n\n    /**\n     * 文件后缀名\n     */\n    private String fileSuffix;\n\n    /**\n     * URL地址\n     */\n    private String url;\n\n    /**\n     * 服务商\n     */\n    private String service;\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/bo/SysOssConfigBo.java",
    "content": "package top.flya.system.domain.bo;\n\nimport top.flya.common.core.domain.BaseEntity;\nimport top.flya.common.core.validate.AddGroup;\nimport top.flya.common.core.validate.EditGroup;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\n\n/**\n * 对象存储配置业务对象 sys_oss_config\n *\n * @author Lion Li\n * @author 孤舟烟雨\n * @date 2021-08-13\n */\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class SysOssConfigBo extends BaseEntity {\n\n    /**\n     * 主建\n     */\n    @NotNull(message = \"主建不能为空\", groups = {EditGroup.class})\n    private Long ossConfigId;\n\n    /**\n     * 配置key\n     */\n    @NotBlank(message = \"配置key不能为空\", groups = {AddGroup.class, EditGroup.class})\n    @Size(min = 2, max = 100, message = \"configKey长度必须介于{min}和{max} 之间\")\n    private String configKey;\n\n    /**\n     * accessKey\n     */\n    @NotBlank(message = \"accessKey不能为空\", groups = {AddGroup.class, EditGroup.class})\n    @Size(min = 2, max = 100, message = \"accessKey长度必须介于{min}和{max} 之间\")\n    private String accessKey;\n\n    /**\n     * 秘钥\n     */\n    @NotBlank(message = \"secretKey不能为空\", groups = {AddGroup.class, EditGroup.class})\n    @Size(min = 2, max = 100, message = \"secretKey长度必须介于{min}和{max} 之间\")\n    private String secretKey;\n\n    /**\n     * 桶名称\n     */\n    @NotBlank(message = \"桶名称不能为空\", groups = {AddGroup.class, EditGroup.class})\n    @Size(min = 2, max = 100, message = \"bucketName长度必须介于{min}和{max}之间\")\n    private String bucketName;\n\n    /**\n     * 前缀\n     */\n    private String prefix;\n\n    /**\n     * 访问站点\n     */\n    @NotBlank(message = \"访问站点不能为空\", groups = {AddGroup.class, EditGroup.class})\n    @Size(min = 2, max = 100, message = \"endpoint长度必须介于{min}和{max}之间\")\n    private String endpoint;\n\n    /**\n     * 自定义域名\n     */\n    private String domain;\n\n    /**\n     * 是否https（Y=是,N=否）\n     */\n    private String isHttps;\n\n    /**\n     * 是否默认（0=是,1=否）\n     */\n    private String status;\n\n    /**\n     * 域\n     */\n    private String region;\n\n    /**\n     * 扩展字段\n     */\n    private String ext1;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 桶权限类型(0private 1public 2custom)\n     */\n    @NotBlank(message = \"桶权限类型不能为空\", groups = {AddGroup.class, EditGroup.class})\n    private String accessPolicy;\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/vo/MetaVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport top.flya.common.utils.StringUtils;\nimport lombok.Data;\n\n/**\n * 路由显示信息\n *\n * @author ruoyi\n */\n\n@Data\npublic class MetaVo {\n\n    /**\n     * 设置该路由在侧边栏和面包屑中展示的名字\n     */\n    private String title;\n\n    /**\n     * 设置该路由的图标，对应路径src/assets/icons/svg\n     */\n    private String icon;\n\n    /**\n     * 设置为true，则不会被 <keep-alive>缓存\n     */\n    private boolean noCache;\n\n    /**\n     * 内链地址（http(s)://开头）\n     */\n    private String link;\n\n    public MetaVo(String title, String icon) {\n        this.title = title;\n        this.icon = icon;\n    }\n\n    public MetaVo(String title, String icon, boolean noCache) {\n        this.title = title;\n        this.icon = icon;\n        this.noCache = noCache;\n    }\n\n    public MetaVo(String title, String icon, String link) {\n        this.title = title;\n        this.icon = icon;\n        this.link = link;\n    }\n\n    public MetaVo(String title, String icon, boolean noCache, String link) {\n        this.title = title;\n        this.icon = icon;\n        this.noCache = noCache;\n        if (StringUtils.ishttp(link)) {\n            this.link = link;\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/vo/RouterVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * 路由配置信息\n *\n * @author Lion Li\n */\n@Data\n@JsonInclude(JsonInclude.Include.NON_EMPTY)\npublic class RouterVo {\n\n    /**\n     * 路由名字\n     */\n    private String name;\n\n    /**\n     * 路由地址\n     */\n    private String path;\n\n    /**\n     * 是否隐藏路由，当设置 true 的时候该路由不会再侧边栏出现\n     */\n    private boolean hidden;\n\n    /**\n     * 重定向地址，当设置 noRedirect 的时候该路由在面包屑导航中不可被点击\n     */\n    private String redirect;\n\n    /**\n     * 组件地址\n     */\n    private String component;\n\n    /**\n     * 路由参数：如 {\"id\": 1, \"name\": \"ry\"}\n     */\n    private String query;\n\n    /**\n     * 当你一个路由下面的 children 声明的路由大于1个时，自动会变成嵌套的模式--如组件页面\n     */\n    private Boolean alwaysShow;\n\n    /**\n     * 其他元素\n     */\n    private MetaVo meta;\n\n    /**\n     * 子路由\n     */\n    private List<RouterVo> children;\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/vo/SysOssConfigVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelIgnoreUnannotated;\nimport lombok.Data;\n\n\n/**\n * 对象存储配置视图对象 sys_oss_config\n *\n * @author Lion Li\n * @author 孤舟烟雨\n * @date 2021-08-13\n */\n@Data\n@ExcelIgnoreUnannotated\npublic class SysOssConfigVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 主建\n     */\n    private Long ossConfigId;\n\n    /**\n     * 配置key\n     */\n    private String configKey;\n\n    /**\n     * accessKey\n     */\n    private String accessKey;\n\n    /**\n     * 秘钥\n     */\n    private String secretKey;\n\n    /**\n     * 桶名称\n     */\n    private String bucketName;\n\n    /**\n     * 前缀\n     */\n    private String prefix;\n\n    /**\n     * 访问站点\n     */\n    private String endpoint;\n\n    /**\n     * 自定义域名\n     */\n    private String domain;\n\n    /**\n     * 是否https（Y=是,N=否）\n     */\n    private String isHttps;\n\n    /**\n     * 域\n     */\n    private String region;\n\n    /**\n     * 是否默认（0=是,1=否）\n     */\n    private String status;\n\n    /**\n     * 扩展字段\n     */\n    private String ext1;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 桶权限类型(0private 1public 2custom)\n     */\n    private String accessPolicy;\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/vo/SysOssVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport lombok.Data;\n\nimport java.util.Date;\n\n/**\n * OSS对象存储视图对象 sys_oss\n *\n * @author Lion Li\n */\n@Data\npublic class SysOssVo {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 对象存储主键\n     */\n    private Long ossId;\n\n    /**\n     * 文件名\n     */\n    private String fileName;\n\n    /**\n     * 原名\n     */\n    private String originalName;\n\n    /**\n     * 文件后缀名\n     */\n    private String fileSuffix;\n\n    /**\n     * URL地址\n     */\n    private String url;\n\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    /**\n     * 上传人\n     */\n    private String createBy;\n\n    /**\n     * 服务商\n     */\n    private String service;\n\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/vo/SysUserExportVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 用户对象导出VO\n *\n * @author Lion Li\n */\n\n@Data\n@NoArgsConstructor\npublic class SysUserExportVo implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 用户ID\n     */\n    @ExcelProperty(value = \"用户序号\")\n    private Long userId;\n\n    /**\n     * 用户账号\n     */\n    @ExcelProperty(value = \"登录名称\")\n    private String userName;\n\n    /**\n     * 用户昵称\n     */\n    @ExcelProperty(value = \"用户名称\")\n    private String nickName;\n\n    /**\n     * 用户邮箱\n     */\n    @ExcelProperty(value = \"用户邮箱\")\n    private String email;\n\n    /**\n     * 手机号码\n     */\n    @ExcelProperty(value = \"手机号码\")\n    private String phonenumber;\n\n    /**\n     * 用户性别\n     */\n    @ExcelProperty(value = \"用户性别\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"sys_user_sex\")\n    private String sex;\n\n    /**\n     * 帐号状态（0正常 1停用）\n     */\n    @ExcelProperty(value = \"帐号状态\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"sys_normal_disable\")\n    private String status;\n\n    /**\n     * 最后登录IP\n     */\n    @ExcelProperty(value = \"最后登录IP\")\n    private String loginIp;\n\n    /**\n     * 最后登录时间\n     */\n    @ExcelProperty(value = \"最后登录时间\")\n    private Date loginDate;\n\n    /**\n     * 部门名称\n     */\n    @ExcelProperty(value = \"部门名称\")\n    private String deptName;\n\n    /**\n     * 负责人\n     */\n    @ExcelProperty(value = \"部门负责人\")\n    private String leader;\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/domain/vo/SysUserImportVo.java",
    "content": "package top.flya.system.domain.vo;\n\nimport com.alibaba.excel.annotation.ExcelProperty;\nimport top.flya.common.annotation.ExcelDictFormat;\nimport top.flya.common.convert.ExcelDictConvert;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\n\n/**\n * 用户对象导入VO\n *\n * @author Lion Li\n */\n\n@Data\n@NoArgsConstructor\n// @Accessors(chain = true) // 导入不允许使用 会找不到set方法\npublic class SysUserImportVo implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 用户ID\n     */\n    @ExcelProperty(value = \"用户序号\")\n    private Long userId;\n\n    /**\n     * 部门ID\n     */\n    @ExcelProperty(value = \"部门编号\")\n    private Long deptId;\n\n    /**\n     * 用户账号\n     */\n    @ExcelProperty(value = \"登录名称\")\n    private String userName;\n\n    /**\n     * 用户昵称\n     */\n    @ExcelProperty(value = \"用户名称\")\n    private String nickName;\n\n    /**\n     * 用户邮箱\n     */\n    @ExcelProperty(value = \"用户邮箱\")\n    private String email;\n\n    /**\n     * 手机号码\n     */\n    @ExcelProperty(value = \"手机号码\")\n    private String phonenumber;\n\n    /**\n     * 用户性别\n     */\n    @ExcelProperty(value = \"用户性别\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"sys_user_sex\")\n    private String sex;\n\n    /**\n     * 帐号状态（0正常 1停用）\n     */\n    @ExcelProperty(value = \"帐号状态\", converter = ExcelDictConvert.class)\n    @ExcelDictFormat(dictType = \"sys_normal_disable\")\n    private String status;\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/listener/SysUserImportListener.java",
    "content": "package top.flya.system.listener;\n\nimport cn.dev33.satoken.secure.BCrypt;\nimport cn.hutool.core.bean.BeanUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.alibaba.excel.context.AnalysisContext;\nimport com.alibaba.excel.event.AnalysisEventListener;\nimport top.flya.common.core.domain.entity.SysUser;\nimport top.flya.common.excel.ExcelListener;\nimport top.flya.common.excel.ExcelResult;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.ValidatorUtils;\nimport top.flya.common.utils.spring.SpringUtils;\nimport top.flya.system.domain.vo.SysUserImportVo;\nimport top.flya.system.service.ISysConfigService;\nimport top.flya.system.service.ISysUserService;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.List;\n\n/**\n * 系统用户自定义导入\n *\n * @author Lion Li\n */\n@Slf4j\npublic class SysUserImportListener extends AnalysisEventListener<SysUserImportVo> implements ExcelListener<SysUserImportVo> {\n\n    private final ISysUserService userService;\n\n    private final String password;\n\n    private final Boolean isUpdateSupport;\n\n    private final String operName;\n\n    private int successNum = 0;\n    private int failureNum = 0;\n    private final StringBuilder successMsg = new StringBuilder();\n    private final StringBuilder failureMsg = new StringBuilder();\n\n    public SysUserImportListener(Boolean isUpdateSupport) {\n        String initPassword = SpringUtils.getBean(ISysConfigService.class).selectConfigByKey(\"sys.user.initPassword\");\n        this.userService = SpringUtils.getBean(ISysUserService.class);\n        this.password = BCrypt.hashpw(initPassword);\n        this.isUpdateSupport = isUpdateSupport;\n        this.operName = LoginHelper.getUsername();\n    }\n\n    @Override\n    public void invoke(SysUserImportVo userVo, AnalysisContext context) {\n        SysUser user = this.userService.selectUserByUserName(userVo.getUserName());\n        try {\n            // 验证是否存在这个用户\n            if (ObjectUtil.isNull(user)) {\n                user = BeanUtil.toBean(userVo, SysUser.class);\n                ValidatorUtils.validate(user);\n                user.setPassword(password);\n//                user.setCreateBy(operName);\n                userService.insertUser(user);\n                successNum++;\n                successMsg.append(\"<br/>\").append(successNum).append(\"、账号 \").append(user.getUserName()).append(\" 导入成功\");\n            } else if (isUpdateSupport) {\n                Long userId = user.getUserId();\n                user = BeanUtil.toBean(userVo, SysUser.class);\n                user.setUserId(userId);\n                ValidatorUtils.validate(user);\n                userService.checkUserAllowed(user);\n                userService.checkUserDataScope(user.getUserId());\n//                user.setUpdateBy(operName);\n                userService.updateUser(user);\n                successNum++;\n                successMsg.append(\"<br/>\").append(successNum).append(\"、账号 \").append(user.getUserName()).append(\" 更新成功\");\n            } else {\n                failureNum++;\n                failureMsg.append(\"<br/>\").append(failureNum).append(\"、账号 \").append(user.getUserName()).append(\" 已存在\");\n            }\n        } catch (Exception e) {\n            failureNum++;\n            String msg = \"<br/>\" + failureNum + \"、账号 \" + user.getUserName() + \" 导入失败：\";\n            failureMsg.append(msg).append(e.getMessage());\n            log.error(msg, e);\n        }\n    }\n\n    @Override\n    public void doAfterAllAnalysed(AnalysisContext context) {\n\n    }\n\n    @Override\n    public ExcelResult<SysUserImportVo> getExcelResult() {\n        return new ExcelResult<SysUserImportVo>() {\n\n            @Override\n            public String getAnalysis() {\n                if (failureNum > 0) {\n                    failureMsg.insert(0, \"很抱歉，导入失败！共 \" + failureNum + \" 条数据格式不正确，错误如下：\");\n                    throw new ServiceException(failureMsg.toString());\n                } else {\n                    successMsg.insert(0, \"恭喜您，数据已全部导入成功！共 \" + successNum + \" 条，数据如下：\");\n                }\n                return successMsg.toString();\n            }\n\n            @Override\n            public List<SysUserImportVo> getList() {\n                return null;\n            }\n\n            @Override\n            public List<String> getErrorList() {\n                return null;\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysConfigMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.system.domain.SysConfig;\n\n/**\n * 参数配置 数据层\n *\n * @author Lion Li\n */\npublic interface SysConfigMapper extends BaseMapperPlus<SysConfigMapper, SysConfig, SysConfig> {\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysDeptMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport top.flya.common.annotation.DataColumn;\nimport top.flya.common.annotation.DataPermission;\nimport top.flya.common.core.domain.entity.SysDept;\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * 部门管理 数据层\n *\n * @author Lion Li\n */\npublic interface SysDeptMapper extends BaseMapperPlus<SysDeptMapper, SysDept, SysDept> {\n\n    /**\n     * 查询部门管理数据\n     *\n     * @param queryWrapper 查询条件\n     * @return 部门信息集合\n     */\n    @DataPermission({\n        @DataColumn(key = \"deptName\", value = \"dept_id\")\n    })\n    List<SysDept> selectDeptList(@Param(Constants.WRAPPER) Wrapper<SysDept> queryWrapper);\n\n    /**\n     * 根据角色ID查询部门树信息\n     *\n     * @param roleId            角色ID\n     * @param deptCheckStrictly 部门树选择项是否关联显示\n     * @return 选中部门列表\n     */\n    List<Long> selectDeptListByRoleId(@Param(\"roleId\") Long roleId, @Param(\"deptCheckStrictly\") boolean deptCheckStrictly);\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysDictDataMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.domain.entity.SysDictData;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\nimport java.util.List;\n\n/**\n * 字典表 数据层\n *\n * @author Lion Li\n */\npublic interface SysDictDataMapper extends BaseMapperPlus<SysDictDataMapper, SysDictData, SysDictData> {\n\n    default List<SysDictData> selectDictDataByType(String dictType) {\n        return selectList(\n            new LambdaQueryWrapper<SysDictData>()\n                .eq(SysDictData::getStatus, UserConstants.DICT_NORMAL)\n                .eq(SysDictData::getDictType, dictType)\n                .orderByAsc(SysDictData::getDictSort));\n    }\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysDictTypeMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.common.core.domain.entity.SysDictType;\nimport top.flya.common.core.mapper.BaseMapperPlus;\n\n/**\n * 字典表 数据层\n *\n * @author Lion Li\n */\npublic interface SysDictTypeMapper extends BaseMapperPlus<SysDictTypeMapper, SysDictType, SysDictType> {\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysLogininforMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.system.domain.SysLogininfor;\n\n/**\n * 系统访问日志情况信息 数据层\n *\n * @author Lion Li\n */\npublic interface SysLogininforMapper extends BaseMapperPlus<SysLogininforMapper, SysLogininfor, SysLogininfor> {\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysMenuMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.domain.entity.SysMenu;\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * 菜单表 数据层\n *\n * @author Lion Li\n */\npublic interface SysMenuMapper extends BaseMapperPlus<SysMenuMapper, SysMenu, SysMenu> {\n\n    /**\n     * 根据用户所有权限\n     *\n     * @return 权限列表\n     */\n    List<String> selectMenuPerms();\n\n    /**\n     * 根据用户查询系统菜单列表\n     *\n     * @param queryWrapper 查询条件\n     * @return 菜单列表\n     */\n    List<SysMenu> selectMenuListByUserId(@Param(Constants.WRAPPER) Wrapper<SysMenu> queryWrapper);\n\n    /**\n     * 根据用户ID查询权限\n     *\n     * @param userId 用户ID\n     * @return 权限列表\n     */\n    List<String> selectMenuPermsByUserId(Long userId);\n\n    /**\n     * 根据角色ID查询权限\n     *\n     * @param roleId 角色ID\n     * @return 权限列表\n     */\n    List<String> selectMenuPermsByRoleId(Long roleId);\n\n    /**\n     * 根据用户ID查询菜单\n     *\n     * @return 菜单列表\n     */\n    default List<SysMenu> selectMenuTreeAll() {\n        LambdaQueryWrapper<SysMenu> lqw = new LambdaQueryWrapper<SysMenu>()\n            .in(SysMenu::getMenuType, UserConstants.TYPE_DIR, UserConstants.TYPE_MENU)\n            .eq(SysMenu::getStatus, UserConstants.MENU_NORMAL)\n            .orderByAsc(SysMenu::getParentId)\n            .orderByAsc(SysMenu::getOrderNum);\n        return this.selectList(lqw);\n    }\n\n    /**\n     * 根据用户ID查询菜单\n     *\n     * @param userId 用户ID\n     * @return 菜单列表\n     */\n    List<SysMenu> selectMenuTreeByUserId(Long userId);\n\n    /**\n     * 根据角色ID查询菜单树信息\n     *\n     * @param roleId            角色ID\n     * @param menuCheckStrictly 菜单树选择项是否关联显示\n     * @return 选中菜单列表\n     */\n    List<Long> selectMenuListByRoleId(@Param(\"roleId\") Long roleId, @Param(\"menuCheckStrictly\") boolean menuCheckStrictly);\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysNoticeMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.system.domain.SysNotice;\n\n/**\n * 通知公告表 数据层\n *\n * @author Lion Li\n */\npublic interface SysNoticeMapper extends BaseMapperPlus<SysNoticeMapper, SysNotice, SysNotice> {\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysOperLogMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.system.domain.SysOperLog;\n\n/**\n * 操作日志 数据层\n *\n * @author Lion Li\n */\npublic interface SysOperLogMapper extends BaseMapperPlus<SysOperLogMapper, SysOperLog, SysOperLog> {\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysOssConfigMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.system.domain.SysOssConfig;\nimport top.flya.system.domain.vo.SysOssConfigVo;\n\n/**\n * 对象存储配置Mapper接口\n *\n * @author Lion Li\n * @author 孤舟烟雨\n * @date 2021-08-13\n */\npublic interface SysOssConfigMapper extends BaseMapperPlus<SysOssConfigMapper, SysOssConfig, SysOssConfigVo> {\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysOssMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.system.domain.SysOss;\nimport top.flya.system.domain.vo.SysOssVo;\n\n/**\n * 文件上传 数据层\n *\n * @author Lion Li\n */\npublic interface SysOssMapper extends BaseMapperPlus<SysOssMapper, SysOss, SysOssVo> {\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysPostMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.system.domain.SysPost;\n\nimport java.util.List;\n\n/**\n * 岗位信息 数据层\n *\n * @author Lion Li\n */\npublic interface SysPostMapper extends BaseMapperPlus<SysPostMapper, SysPost, SysPost> {\n\n    /**\n     * 根据用户ID获取岗位选择框列表\n     *\n     * @param userId 用户ID\n     * @return 选中岗位ID列表\n     */\n    List<Long> selectPostListByUserId(Long userId);\n\n    /**\n     * 查询用户所属岗位组\n     *\n     * @param userName 用户名\n     * @return 结果\n     */\n    List<SysPost> selectPostsByUserName(String userName);\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysRoleDeptMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.system.domain.SysRoleDept;\n\n/**\n * 角色与部门关联表 数据层\n *\n * @author Lion Li\n */\npublic interface SysRoleDeptMapper extends BaseMapperPlus<SysRoleDeptMapper, SysRoleDept, SysRoleDept> {\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysRoleMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport top.flya.common.annotation.DataColumn;\nimport top.flya.common.annotation.DataPermission;\nimport top.flya.common.core.domain.entity.SysRole;\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * 角色表 数据层\n *\n * @author Lion Li\n */\npublic interface SysRoleMapper extends BaseMapperPlus<SysRoleMapper, SysRole, SysRole> {\n\n    @DataPermission({\n        @DataColumn(key = \"deptName\", value = \"d.dept_id\")\n    })\n    Page<SysRole> selectPageRoleList(@Param(\"page\") Page<SysRole> page, @Param(Constants.WRAPPER) Wrapper<SysRole> queryWrapper);\n\n    /**\n     * 根据条件分页查询角色数据\n     *\n     * @param queryWrapper 查询条件\n     * @return 角色数据集合信息\n     */\n    @DataPermission({\n        @DataColumn(key = \"deptName\", value = \"d.dept_id\")\n    })\n    List<SysRole> selectRoleList(@Param(Constants.WRAPPER) Wrapper<SysRole> queryWrapper);\n\n    /**\n     * 根据用户ID查询角色\n     *\n     * @param userId 用户ID\n     * @return 角色列表\n     */\n    List<SysRole> selectRolePermissionByUserId(Long userId);\n\n\n    /**\n     * 根据用户ID获取角色选择框列表\n     *\n     * @param userId 用户ID\n     * @return 选中角色ID列表\n     */\n    List<Long> selectRoleListByUserId(Long userId);\n\n    /**\n     * 根据用户ID查询角色\n     *\n     * @param userName 用户名\n     * @return 角色列表\n     */\n    List<SysRole> selectRolesByUserName(String userName);\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysRoleMenuMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.system.domain.SysRoleMenu;\n\n/**\n * 角色与菜单关联表 数据层\n *\n * @author Lion Li\n */\npublic interface SysRoleMenuMapper extends BaseMapperPlus<SysRoleMenuMapper, SysRoleMenu, SysRoleMenu> {\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysUserMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport top.flya.common.annotation.DataColumn;\nimport top.flya.common.annotation.DataPermission;\nimport top.flya.common.core.domain.entity.SysUser;\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * 用户表 数据层\n *\n * @author Lion Li\n */\npublic interface SysUserMapper extends BaseMapperPlus<SysUserMapper, SysUser, SysUser> {\n\n    @DataPermission({\n        @DataColumn(key = \"deptName\", value = \"d.dept_id\"),\n        @DataColumn(key = \"userName\", value = \"u.user_id\")\n    })\n    Page<SysUser> selectPageUserList(@Param(\"page\") Page<SysUser> page, @Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);\n\n    /**\n     * 根据条件分页查询用户列表\n     *\n     * @param queryWrapper 查询条件\n     * @return 用户信息集合信息\n     */\n    @DataPermission({\n        @DataColumn(key = \"deptName\", value = \"d.dept_id\"),\n        @DataColumn(key = \"userName\", value = \"u.user_id\")\n    })\n    List<SysUser> selectUserList(@Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);\n\n    /**\n     * 根据条件分页查询已配用户角色列表\n     *\n     * @param queryWrapper 查询条件\n     * @return 用户信息集合信息\n     */\n    @DataPermission({\n        @DataColumn(key = \"deptName\", value = \"d.dept_id\"),\n        @DataColumn(key = \"userName\", value = \"u.user_id\")\n    })\n    Page<SysUser> selectAllocatedList(@Param(\"page\") Page<SysUser> page, @Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);\n\n    /**\n     * 根据条件分页查询未分配用户角色列表\n     *\n     * @param queryWrapper 查询条件\n     * @return 用户信息集合信息\n     */\n    @DataPermission({\n        @DataColumn(key = \"deptName\", value = \"d.dept_id\"),\n        @DataColumn(key = \"userName\", value = \"u.user_id\")\n    })\n    Page<SysUser> selectUnallocatedList(@Param(\"page\") Page<SysUser> page, @Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);\n\n    /**\n     * 通过用户名查询用户\n     *\n     * @param userName 用户名\n     * @return 用户对象信息\n     */\n    SysUser selectUserByUserName(String userName);\n\n    /**\n     * 通过手机号查询用户\n     *\n     * @param phonenumber 手机号\n     * @return 用户对象信息\n     */\n    SysUser selectUserByPhonenumber(String phonenumber);\n\n    /**\n     * 通过邮箱查询用户\n     *\n     * @param email 邮箱\n     * @return 用户对象信息\n     */\n    SysUser selectUserByEmail(String email);\n\n    /**\n     * 通过用户ID查询用户\n     *\n     * @param userId 用户ID\n     * @return 用户对象信息\n     */\n    SysUser selectUserById(Long userId);\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysUserPostMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.system.domain.SysUserPost;\n\n/**\n * 用户与岗位关联表 数据层\n *\n * @author Lion Li\n */\npublic interface SysUserPostMapper extends BaseMapperPlus<SysUserPostMapper, SysUserPost, SysUserPost> {\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/mapper/SysUserRoleMapper.java",
    "content": "package top.flya.system.mapper;\n\nimport top.flya.common.core.mapper.BaseMapperPlus;\nimport top.flya.system.domain.SysUserRole;\n\nimport java.util.List;\n\n/**\n * 用户与角色关联表 数据层\n *\n * @author Lion Li\n */\npublic interface SysUserRoleMapper extends BaseMapperPlus<SysUserRoleMapper, SysUserRole, SysUserRole> {\n\n    List<Long> selectUserIdsByRoleId(Long roleId);\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/runner/SystemApplicationRunner.java",
    "content": "package top.flya.system.runner;\n\nimport top.flya.common.config.RuoYiConfig;\nimport top.flya.system.service.ISysConfigService;\nimport top.flya.system.service.ISysDictTypeService;\nimport top.flya.system.service.ISysOssConfigService;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.ApplicationArguments;\nimport org.springframework.boot.ApplicationRunner;\nimport org.springframework.stereotype.Component;\n\n/**\n * 初始化 system 模块对应业务数据\n *\n * @author Lion Li\n */\n@Slf4j\n@RequiredArgsConstructor\n@Component\npublic class SystemApplicationRunner implements ApplicationRunner {\n\n    private final RuoYiConfig ruoyiConfig;\n    private final ISysConfigService configService;\n    private final ISysDictTypeService dictTypeService;\n    private final ISysOssConfigService ossConfigService;\n\n    @Override\n    public void run(ApplicationArguments args) throws Exception {\n        ossConfigService.init();\n        log.info(\"初始化OSS配置成功\");\n        if (ruoyiConfig.isCacheLazy()) {\n            return;\n        }\n        configService.loadingConfigCache();\n        log.info(\"加载参数缓存数据成功\");\n        dictTypeService.loadingDictCache();\n        log.info(\"加载字典缓存数据成功\");\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/ISysConfigService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.domain.SysConfig;\n\nimport java.util.List;\n\n/**\n * 参数配置 服务层\n *\n * @author Lion Li\n */\npublic interface ISysConfigService {\n\n\n    TableDataInfo<SysConfig> selectPageConfigList(SysConfig config, PageQuery pageQuery);\n\n    /**\n     * 查询参数配置信息\n     *\n     * @param configId 参数配置ID\n     * @return 参数配置信息\n     */\n    SysConfig selectConfigById(Long configId);\n\n    /**\n     * 根据键名查询参数配置信息\n     *\n     * @param configKey 参数键名\n     * @return 参数键值\n     */\n    String selectConfigByKey(String configKey);\n\n    /**\n     * 获取验证码开关\n     *\n     * @return true开启，false关闭\n     */\n    boolean selectCaptchaEnabled();\n\n    /**\n     * 查询参数配置列表\n     *\n     * @param config 参数配置信息\n     * @return 参数配置集合\n     */\n    List<SysConfig> selectConfigList(SysConfig config);\n\n    /**\n     * 新增参数配置\n     *\n     * @param config 参数配置信息\n     * @return 结果\n     */\n    String insertConfig(SysConfig config);\n\n    /**\n     * 修改参数配置\n     *\n     * @param config 参数配置信息\n     * @return 结果\n     */\n    String updateConfig(SysConfig config);\n\n    /**\n     * 批量删除参数信息\n     *\n     * @param configIds 需要删除的参数ID\n     */\n    void deleteConfigByIds(Long[] configIds);\n\n    /**\n     * 加载参数缓存数据\n     */\n    void loadingConfigCache();\n\n    /**\n     * 清空参数缓存数据\n     */\n    void clearConfigCache();\n\n    /**\n     * 重置参数缓存数据\n     */\n    void resetConfigCache();\n\n    /**\n     * 校验参数键名是否唯一\n     *\n     * @param config 参数信息\n     * @return 结果\n     */\n    boolean checkConfigKeyUnique(SysConfig config);\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/ISysDataScopeService.java",
    "content": "package top.flya.system.service;\n\n/**\n * 通用 数据权限 服务\n *\n * @author Lion Li\n */\npublic interface ISysDataScopeService {\n\n    /**\n     * 获取角色自定义权限\n     *\n     * @param roleId 角色id\n     * @return 部门id组\n     */\n    String getRoleCustom(Long roleId);\n\n    /**\n     * 获取部门及以下权限\n     *\n     * @param deptId 部门id\n     * @return 部门id组\n     */\n    String getDeptAndChild(Long deptId);\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/ISysDeptService.java",
    "content": "package top.flya.system.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport top.flya.common.core.domain.entity.SysDept;\n\nimport java.util.List;\n\n/**\n * 部门管理 服务层\n *\n * @author Lion Li\n */\npublic interface ISysDeptService {\n    /**\n     * 查询部门管理数据\n     *\n     * @param dept 部门信息\n     * @return 部门信息集合\n     */\n    List<SysDept> selectDeptList(SysDept dept);\n\n    /**\n     * 查询部门树结构信息\n     *\n     * @param dept 部门信息\n     * @return 部门树信息集合\n     */\n    List<Tree<Long>> selectDeptTreeList(SysDept dept);\n\n    /**\n     * 构建前端所需要下拉树结构\n     *\n     * @param depts 部门列表\n     * @return 下拉树结构列表\n     */\n    List<Tree<Long>> buildDeptTreeSelect(List<SysDept> depts);\n\n    /**\n     * 根据角色ID查询部门树信息\n     *\n     * @param roleId 角色ID\n     * @return 选中部门列表\n     */\n    List<Long> selectDeptListByRoleId(Long roleId);\n\n    /**\n     * 根据部门ID查询信息\n     *\n     * @param deptId 部门ID\n     * @return 部门信息\n     */\n    SysDept selectDeptById(Long deptId);\n\n    /**\n     * 根据ID查询所有子部门数（正常状态）\n     *\n     * @param deptId 部门ID\n     * @return 子部门数\n     */\n    long selectNormalChildrenDeptById(Long deptId);\n\n    /**\n     * 是否存在部门子节点\n     *\n     * @param deptId 部门ID\n     * @return 结果\n     */\n    boolean hasChildByDeptId(Long deptId);\n\n    /**\n     * 查询部门是否存在用户\n     *\n     * @param deptId 部门ID\n     * @return 结果 true 存在 false 不存在\n     */\n    boolean checkDeptExistUser(Long deptId);\n\n    /**\n     * 校验部门名称是否唯一\n     *\n     * @param dept 部门信息\n     * @return 结果\n     */\n    boolean checkDeptNameUnique(SysDept dept);\n\n    /**\n     * 校验部门是否有数据权限\n     *\n     * @param deptId 部门id\n     */\n    void checkDeptDataScope(Long deptId);\n\n    /**\n     * 新增保存部门信息\n     *\n     * @param dept 部门信息\n     * @return 结果\n     */\n    int insertDept(SysDept dept);\n\n    /**\n     * 修改保存部门信息\n     *\n     * @param dept 部门信息\n     * @return 结果\n     */\n    int updateDept(SysDept dept);\n\n    /**\n     * 删除部门管理信息\n     *\n     * @param deptId 部门ID\n     * @return 结果\n     */\n    int deleteDeptById(Long deptId);\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/ISysDictDataService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.entity.SysDictData;\nimport top.flya.common.core.page.TableDataInfo;\n\nimport java.util.List;\n\n/**\n * 字典 业务层\n *\n * @author Lion Li\n */\npublic interface ISysDictDataService {\n\n\n    TableDataInfo<SysDictData> selectPageDictDataList(SysDictData dictData, PageQuery pageQuery);\n\n    /**\n     * 根据条件分页查询字典数据\n     *\n     * @param dictData 字典数据信息\n     * @return 字典数据集合信息\n     */\n    List<SysDictData> selectDictDataList(SysDictData dictData);\n\n    /**\n     * 根据字典类型和字典键值查询字典数据信息\n     *\n     * @param dictType  字典类型\n     * @param dictValue 字典键值\n     * @return 字典标签\n     */\n    String selectDictLabel(String dictType, String dictValue);\n\n    /**\n     * 根据字典数据ID查询信息\n     *\n     * @param dictCode 字典数据ID\n     * @return 字典数据\n     */\n    SysDictData selectDictDataById(Long dictCode);\n\n    /**\n     * 批量删除字典数据信息\n     *\n     * @param dictCodes 需要删除的字典数据ID\n     */\n    void deleteDictDataByIds(Long[] dictCodes);\n\n    /**\n     * 新增保存字典数据信息\n     *\n     * @param dictData 字典数据信息\n     * @return 结果\n     */\n    List<SysDictData> insertDictData(SysDictData dictData);\n\n    /**\n     * 修改保存字典数据信息\n     *\n     * @param dictData 字典数据信息\n     * @return 结果\n     */\n    List<SysDictData> updateDictData(SysDictData dictData);\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/ISysDictTypeService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.entity.SysDictData;\nimport top.flya.common.core.domain.entity.SysDictType;\nimport top.flya.common.core.page.TableDataInfo;\n\nimport java.util.List;\n\n/**\n * 字典 业务层\n *\n * @author Lion Li\n */\npublic interface ISysDictTypeService {\n\n\n    TableDataInfo<SysDictType> selectPageDictTypeList(SysDictType dictType, PageQuery pageQuery);\n\n    /**\n     * 根据条件分页查询字典类型\n     *\n     * @param dictType 字典类型信息\n     * @return 字典类型集合信息\n     */\n    List<SysDictType> selectDictTypeList(SysDictType dictType);\n\n    /**\n     * 根据所有字典类型\n     *\n     * @return 字典类型集合信息\n     */\n    List<SysDictType> selectDictTypeAll();\n\n    /**\n     * 根据字典类型查询字典数据\n     *\n     * @param dictType 字典类型\n     * @return 字典数据集合信息\n     */\n    List<SysDictData> selectDictDataByType(String dictType);\n\n    /**\n     * 根据字典类型ID查询信息\n     *\n     * @param dictId 字典类型ID\n     * @return 字典类型\n     */\n    SysDictType selectDictTypeById(Long dictId);\n\n    /**\n     * 根据字典类型查询信息\n     *\n     * @param dictType 字典类型\n     * @return 字典类型\n     */\n    SysDictType selectDictTypeByType(String dictType);\n\n    /**\n     * 批量删除字典信息\n     *\n     * @param dictIds 需要删除的字典ID\n     */\n    void deleteDictTypeByIds(Long[] dictIds);\n\n    /**\n     * 加载字典缓存数据\n     */\n    void loadingDictCache();\n\n    /**\n     * 清空字典缓存数据\n     */\n    void clearDictCache();\n\n    /**\n     * 重置字典缓存数据\n     */\n    void resetDictCache();\n\n    /**\n     * 新增保存字典类型信息\n     *\n     * @param dictType 字典类型信息\n     * @return 结果\n     */\n    List<SysDictData> insertDictType(SysDictType dictType);\n\n    /**\n     * 修改保存字典类型信息\n     *\n     * @param dictType 字典类型信息\n     * @return 结果\n     */\n    List<SysDictData> updateDictType(SysDictType dictType);\n\n    /**\n     * 校验字典类型称是否唯一\n     *\n     * @param dictType 字典类型\n     * @return 结果\n     */\n    boolean checkDictTypeUnique(SysDictType dictType);\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/ISysLogininforService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.domain.SysLogininfor;\n\nimport java.util.List;\n\n/**\n * 系统访问日志情况信息 服务层\n *\n * @author Lion Li\n */\npublic interface ISysLogininforService {\n\n\n    TableDataInfo<SysLogininfor> selectPageLogininforList(SysLogininfor logininfor, PageQuery pageQuery);\n\n    /**\n     * 新增系统登录日志\n     *\n     * @param logininfor 访问日志对象\n     */\n    void insertLogininfor(SysLogininfor logininfor);\n\n    /**\n     * 查询系统登录日志集合\n     *\n     * @param logininfor 访问日志对象\n     * @return 登录记录集合\n     */\n    List<SysLogininfor> selectLogininforList(SysLogininfor logininfor);\n\n    /**\n     * 批量删除系统登录日志\n     *\n     * @param infoIds 需要删除的登录日志ID\n     * @return 结果\n     */\n    int deleteLogininforByIds(Long[] infoIds);\n\n    /**\n     * 清空系统登录日志\n     */\n    void cleanLogininfor();\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/ISysMenuService.java",
    "content": "package top.flya.system.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport top.flya.common.core.domain.entity.SysMenu;\nimport top.flya.system.domain.vo.RouterVo;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * 菜单 业务层\n *\n * @author Lion Li\n */\npublic interface ISysMenuService {\n\n    /**\n     * 根据用户查询系统菜单列表\n     *\n     * @param userId 用户ID\n     * @return 菜单列表\n     */\n    List<SysMenu> selectMenuList(Long userId);\n\n    /**\n     * 根据用户查询系统菜单列表\n     *\n     * @param menu   菜单信息\n     * @param userId 用户ID\n     * @return 菜单列表\n     */\n    List<SysMenu> selectMenuList(SysMenu menu, Long userId);\n\n    /**\n     * 根据用户ID查询权限\n     *\n     * @param userId 用户ID\n     * @return 权限列表\n     */\n    Set<String> selectMenuPermsByUserId(Long userId);\n\n    /**\n     * 根据角色ID查询权限\n     *\n     * @param roleId 角色ID\n     * @return 权限列表\n     */\n    Set<String> selectMenuPermsByRoleId(Long roleId);\n\n    /**\n     * 根据用户ID查询菜单树信息\n     *\n     * @param userId 用户ID\n     * @return 菜单列表\n     */\n    List<SysMenu> selectMenuTreeByUserId(Long userId);\n\n    /**\n     * 根据角色ID查询菜单树信息\n     *\n     * @param roleId 角色ID\n     * @return 选中菜单列表\n     */\n    List<Long> selectMenuListByRoleId(Long roleId);\n\n    /**\n     * 构建前端路由所需要的菜单\n     *\n     * @param menus 菜单列表\n     * @return 路由列表\n     */\n    List<RouterVo> buildMenus(List<SysMenu> menus);\n\n    /**\n     * 构建前端所需要下拉树结构\n     *\n     * @param menus 菜单列表\n     * @return 下拉树结构列表\n     */\n    List<Tree<Long>> buildMenuTreeSelect(List<SysMenu> menus);\n\n    /**\n     * 根据菜单ID查询信息\n     *\n     * @param menuId 菜单ID\n     * @return 菜单信息\n     */\n    SysMenu selectMenuById(Long menuId);\n\n    /**\n     * 是否存在菜单子节点\n     *\n     * @param menuId 菜单ID\n     * @return 结果 true 存在 false 不存在\n     */\n    boolean hasChildByMenuId(Long menuId);\n\n    /**\n     * 查询菜单是否存在角色\n     *\n     * @param menuId 菜单ID\n     * @return 结果 true 存在 false 不存在\n     */\n    boolean checkMenuExistRole(Long menuId);\n\n    /**\n     * 新增保存菜单信息\n     *\n     * @param menu 菜单信息\n     * @return 结果\n     */\n    int insertMenu(SysMenu menu);\n\n    /**\n     * 修改保存菜单信息\n     *\n     * @param menu 菜单信息\n     * @return 结果\n     */\n    int updateMenu(SysMenu menu);\n\n    /**\n     * 删除菜单管理信息\n     *\n     * @param menuId 菜单ID\n     * @return 结果\n     */\n    int deleteMenuById(Long menuId);\n\n    /**\n     * 校验菜单名称是否唯一\n     *\n     * @param menu 菜单信息\n     * @return 结果\n     */\n    boolean checkMenuNameUnique(SysMenu menu);\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/ISysNoticeService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.domain.SysNotice;\n\nimport java.util.List;\n\n/**\n * 公告 服务层\n *\n * @author Lion Li\n */\npublic interface ISysNoticeService {\n\n\n    TableDataInfo<SysNotice> selectPageNoticeList(SysNotice notice, PageQuery pageQuery);\n\n    /**\n     * 查询公告信息\n     *\n     * @param noticeId 公告ID\n     * @return 公告信息\n     */\n    SysNotice selectNoticeById(Long noticeId);\n\n    /**\n     * 查询公告列表\n     *\n     * @param notice 公告信息\n     * @return 公告集合\n     */\n    List<SysNotice> selectNoticeList(SysNotice notice);\n\n    /**\n     * 新增公告\n     *\n     * @param notice 公告信息\n     * @return 结果\n     */\n    int insertNotice(SysNotice notice);\n\n    /**\n     * 修改公告\n     *\n     * @param notice 公告信息\n     * @return 结果\n     */\n    int updateNotice(SysNotice notice);\n\n    /**\n     * 删除公告信息\n     *\n     * @param noticeId 公告ID\n     * @return 结果\n     */\n    int deleteNoticeById(Long noticeId);\n\n    /**\n     * 批量删除公告信息\n     *\n     * @param noticeIds 需要删除的公告ID\n     * @return 结果\n     */\n    int deleteNoticeByIds(Long[] noticeIds);\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/ISysOperLogService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.domain.SysOperLog;\n\nimport java.util.List;\n\n/**\n * 操作日志 服务层\n *\n * @author Lion Li\n */\npublic interface ISysOperLogService {\n\n    TableDataInfo<SysOperLog> selectPageOperLogList(SysOperLog operLog, PageQuery pageQuery);\n\n    /**\n     * 新增操作日志\n     *\n     * @param operLog 操作日志对象\n     */\n    void insertOperlog(SysOperLog operLog);\n\n    /**\n     * 查询系统操作日志集合\n     *\n     * @param operLog 操作日志对象\n     * @return 操作日志集合\n     */\n    List<SysOperLog> selectOperLogList(SysOperLog operLog);\n\n    /**\n     * 批量删除系统操作日志\n     *\n     * @param operIds 需要删除的操作日志ID\n     * @return 结果\n     */\n    int deleteOperLogByIds(Long[] operIds);\n\n    /**\n     * 查询操作日志详细\n     *\n     * @param operId 操作ID\n     * @return 操作日志对象\n     */\n    SysOperLog selectOperLogById(Long operId);\n\n    /**\n     * 清空操作日志\n     */\n    void cleanOperLog();\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/ISysOssConfigService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.domain.bo.SysOssConfigBo;\nimport top.flya.system.domain.vo.SysOssConfigVo;\n\nimport java.util.Collection;\n\n/**\n * 对象存储配置Service接口\n *\n * @author Lion Li\n * @author 孤舟烟雨\n * @date 2021-08-13\n */\npublic interface ISysOssConfigService {\n\n    /**\n     * 初始化OSS配置\n     */\n    void init();\n\n    /**\n     * 查询单个\n     */\n    SysOssConfigVo queryById(Long ossConfigId);\n\n    /**\n     * 查询列表\n     */\n    TableDataInfo<SysOssConfigVo> queryPageList(SysOssConfigBo bo, PageQuery pageQuery);\n\n\n    /**\n     * 根据新增业务对象插入对象存储配置\n     *\n     * @param bo 对象存储配置新增业务对象\n     * @return\n     */\n    Boolean insertByBo(SysOssConfigBo bo);\n\n    /**\n     * 根据编辑业务对象修改对象存储配置\n     *\n     * @param bo 对象存储配置编辑业务对象\n     * @return\n     */\n    Boolean updateByBo(SysOssConfigBo bo);\n\n    /**\n     * 校验并删除数据\n     *\n     * @param ids     主键集合\n     * @param isValid 是否校验,true-删除前校验,false-不校验\n     * @return\n     */\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n\n    /**\n     * 启用停用状态\n     */\n    int updateOssConfigStatus(SysOssConfigBo bo);\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/ISysOssService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.domain.bo.SysOssBo;\nimport top.flya.system.domain.vo.SysOssVo;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 文件上传 服务层\n *\n * @author Lion Li\n */\npublic interface ISysOssService {\n\n    TableDataInfo<SysOssVo> queryPageList(SysOssBo sysOss, PageQuery pageQuery);\n\n    List<SysOssVo> listByIds(Collection<Long> ossIds);\n\n    SysOssVo getById(Long ossId);\n\n    SysOssVo upload(MultipartFile file);\n\n    void download(Long ossId, HttpServletResponse response) throws IOException;\n\n    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/ISysPostService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.domain.SysPost;\n\nimport java.util.List;\n\n/**\n * 岗位信息 服务层\n *\n * @author Lion Li\n */\npublic interface ISysPostService {\n\n\n    TableDataInfo<SysPost> selectPagePostList(SysPost post, PageQuery pageQuery);\n\n    /**\n     * 查询岗位信息集合\n     *\n     * @param post 岗位信息\n     * @return 岗位列表\n     */\n    List<SysPost> selectPostList(SysPost post);\n\n    /**\n     * 查询所有岗位\n     *\n     * @return 岗位列表\n     */\n    List<SysPost> selectPostAll();\n\n    /**\n     * 通过岗位ID查询岗位信息\n     *\n     * @param postId 岗位ID\n     * @return 角色对象信息\n     */\n    SysPost selectPostById(Long postId);\n\n    /**\n     * 根据用户ID获取岗位选择框列表\n     *\n     * @param userId 用户ID\n     * @return 选中岗位ID列表\n     */\n    List<Long> selectPostListByUserId(Long userId);\n\n    /**\n     * 校验岗位名称\n     *\n     * @param post 岗位信息\n     * @return 结果\n     */\n    boolean checkPostNameUnique(SysPost post);\n\n    /**\n     * 校验岗位编码\n     *\n     * @param post 岗位信息\n     * @return 结果\n     */\n    boolean checkPostCodeUnique(SysPost post);\n\n    /**\n     * 通过岗位ID查询岗位使用数量\n     *\n     * @param postId 岗位ID\n     * @return 结果\n     */\n    long countUserPostById(Long postId);\n\n    /**\n     * 删除岗位信息\n     *\n     * @param postId 岗位ID\n     * @return 结果\n     */\n    int deletePostById(Long postId);\n\n    /**\n     * 批量删除岗位信息\n     *\n     * @param postIds 需要删除的岗位ID\n     * @return 结果\n     */\n    int deletePostByIds(Long[] postIds);\n\n    /**\n     * 新增保存岗位信息\n     *\n     * @param post 岗位信息\n     * @return 结果\n     */\n    int insertPost(SysPost post);\n\n    /**\n     * 修改保存岗位信息\n     *\n     * @param post 岗位信息\n     * @return 结果\n     */\n    int updatePost(SysPost post);\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/ISysRoleService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.entity.SysRole;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.system.domain.SysUserRole;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * 角色业务层\n *\n * @author Lion Li\n */\npublic interface ISysRoleService {\n\n\n    TableDataInfo<SysRole> selectPageRoleList(SysRole role, PageQuery pageQuery);\n\n    /**\n     * 根据条件分页查询角色数据\n     *\n     * @param role 角色信息\n     * @return 角色数据集合信息\n     */\n    List<SysRole> selectRoleList(SysRole role);\n\n    /**\n     * 根据用户ID查询角色列表\n     *\n     * @param userId 用户ID\n     * @return 角色列表\n     */\n    List<SysRole> selectRolesByUserId(Long userId);\n\n    /**\n     * 根据用户ID查询角色权限\n     *\n     * @param userId 用户ID\n     * @return 权限列表\n     */\n    Set<String> selectRolePermissionByUserId(Long userId);\n\n    /**\n     * 查询所有角色\n     *\n     * @return 角色列表\n     */\n    List<SysRole> selectRoleAll();\n\n    /**\n     * 根据用户ID获取角色选择框列表\n     *\n     * @param userId 用户ID\n     * @return 选中角色ID列表\n     */\n    List<Long> selectRoleListByUserId(Long userId);\n\n    /**\n     * 通过角色ID查询角色\n     *\n     * @param roleId 角色ID\n     * @return 角色对象信息\n     */\n    SysRole selectRoleById(Long roleId);\n\n    /**\n     * 校验角色名称是否唯一\n     *\n     * @param role 角色信息\n     * @return 结果\n     */\n    boolean checkRoleNameUnique(SysRole role);\n\n    /**\n     * 校验角色权限是否唯一\n     *\n     * @param role 角色信息\n     * @return 结果\n     */\n    boolean checkRoleKeyUnique(SysRole role);\n\n    /**\n     * 校验角色是否允许操作\n     *\n     * @param role 角色信息\n     */\n    void checkRoleAllowed(SysRole role);\n\n    /**\n     * 校验角色是否有数据权限\n     *\n     * @param roleId 角色id\n     */\n    void checkRoleDataScope(Long roleId);\n\n    /**\n     * 通过角色ID查询角色使用数量\n     *\n     * @param roleId 角色ID\n     * @return 结果\n     */\n    long countUserRoleByRoleId(Long roleId);\n\n    /**\n     * 新增保存角色信息\n     *\n     * @param role 角色信息\n     * @return 结果\n     */\n    int insertRole(SysRole role);\n\n    /**\n     * 修改保存角色信息\n     *\n     * @param role 角色信息\n     * @return 结果\n     */\n    int updateRole(SysRole role);\n\n    /**\n     * 修改角色状态\n     *\n     * @param role 角色信息\n     * @return 结果\n     */\n    int updateRoleStatus(SysRole role);\n\n    /**\n     * 修改数据权限信息\n     *\n     * @param role 角色信息\n     * @return 结果\n     */\n    int authDataScope(SysRole role);\n\n    /**\n     * 通过角色ID删除角色\n     *\n     * @param roleId 角色ID\n     * @return 结果\n     */\n    int deleteRoleById(Long roleId);\n\n    /**\n     * 批量删除角色信息\n     *\n     * @param roleIds 需要删除的角色ID\n     * @return 结果\n     */\n    int deleteRoleByIds(Long[] roleIds);\n\n    /**\n     * 取消授权用户角色\n     *\n     * @param userRole 用户和角色关联信息\n     * @return 结果\n     */\n    int deleteAuthUser(SysUserRole userRole);\n\n    /**\n     * 批量取消授权用户角色\n     *\n     * @param roleId  角色ID\n     * @param userIds 需要取消授权的用户数据ID\n     * @return 结果\n     */\n    int deleteAuthUsers(Long roleId, Long[] userIds);\n\n    /**\n     * 批量选择授权用户角色\n     *\n     * @param roleId  角色ID\n     * @param userIds 需要删除的用户数据ID\n     * @return 结果\n     */\n    int insertAuthUsers(Long roleId, Long[] userIds);\n\n    void cleanOnlineUserByRole(Long roleId);\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/ISysUserService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.entity.SysUser;\nimport top.flya.common.core.page.TableDataInfo;\n\nimport java.util.List;\n\n/**\n * 用户 业务层\n *\n * @author Lion Li\n */\npublic interface ISysUserService {\n\n\n    TableDataInfo<SysUser> selectPageUserList(SysUser user, PageQuery pageQuery);\n\n    /**\n     * 根据条件分页查询用户列表\n     *\n     * @param user 用户信息\n     * @return 用户信息集合信息\n     */\n    List<SysUser> selectUserList(SysUser user);\n\n    /**\n     * 根据条件分页查询已分配用户角色列表\n     *\n     * @param user 用户信息\n     * @return 用户信息集合信息\n     */\n    TableDataInfo<SysUser> selectAllocatedList(SysUser user, PageQuery pageQuery);\n\n    /**\n     * 根据条件分页查询未分配用户角色列表\n     *\n     * @param user 用户信息\n     * @return 用户信息集合信息\n     */\n    TableDataInfo<SysUser> selectUnallocatedList(SysUser user, PageQuery pageQuery);\n\n    /**\n     * 通过用户名查询用户\n     *\n     * @param userName 用户名\n     * @return 用户对象信息\n     */\n    SysUser selectUserByUserName(String userName);\n\n    /**\n     * 通过手机号查询用户\n     *\n     * @param phonenumber 手机号\n     * @return 用户对象信息\n     */\n    SysUser selectUserByPhonenumber(String phonenumber);\n\n    /**\n     * 通过用户ID查询用户\n     *\n     * @param userId 用户ID\n     * @return 用户对象信息\n     */\n    SysUser selectUserById(Long userId);\n\n    /**\n     * 根据用户ID查询用户所属角色组\n     *\n     * @param userName 用户名\n     * @return 结果\n     */\n    String selectUserRoleGroup(String userName);\n\n    /**\n     * 根据用户ID查询用户所属岗位组\n     *\n     * @param userName 用户名\n     * @return 结果\n     */\n    String selectUserPostGroup(String userName);\n\n    /**\n     * 校验用户名称是否唯一\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    boolean checkUserNameUnique(SysUser user);\n\n    /**\n     * 校验手机号码是否唯一\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    boolean checkPhoneUnique(SysUser user);\n\n    /**\n     * 校验email是否唯一\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    boolean checkEmailUnique(SysUser user);\n\n    /**\n     * 校验用户是否允许操作\n     *\n     * @param user 用户信息\n     */\n    void checkUserAllowed(SysUser user);\n\n    /**\n     * 校验用户是否有数据权限\n     *\n     * @param userId 用户id\n     */\n    void checkUserDataScope(Long userId);\n\n    /**\n     * 新增用户信息\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    int insertUser(SysUser user);\n\n    /**\n     * 注册用户信息\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    boolean registerUser(SysUser user);\n\n    /**\n     * 修改用户信息\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    int updateUser(SysUser user);\n\n    /**\n     * 用户授权角色\n     *\n     * @param userId  用户ID\n     * @param roleIds 角色组\n     */\n    void insertUserAuth(Long userId, Long[] roleIds);\n\n    /**\n     * 修改用户状态\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    int updateUserStatus(SysUser user);\n\n    /**\n     * 修改用户基本信息\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    int updateUserProfile(SysUser user);\n\n    /**\n     * 修改用户头像\n     *\n     * @param userName 用户名\n     * @param avatar   头像地址\n     * @return 结果\n     */\n    boolean updateUserAvatar(String userName, String avatar);\n\n    /**\n     * 重置用户密码\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    int resetPwd(SysUser user);\n\n    /**\n     * 重置用户密码\n     *\n     * @param userName 用户名\n     * @param password 密码\n     * @return 结果\n     */\n    int resetUserPwd(String userName, String password);\n\n    /**\n     * 通过用户ID删除用户\n     *\n     * @param userId 用户ID\n     * @return 结果\n     */\n    int deleteUserById(Long userId);\n\n    /**\n     * 批量删除用户信息\n     *\n     * @param userIds 需要删除的用户ID\n     * @return 结果\n     */\n    int deleteUserByIds(Long[] userIds);\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/SysLoginService.java",
    "content": "package top.flya.system.service;\n\nimport cn.dev33.satoken.exception.NotLoginException;\nimport cn.dev33.satoken.secure.BCrypt;\nimport cn.dev33.satoken.stp.StpUtil;\nimport cn.hutool.core.bean.BeanUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.http.HttpUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport top.flya.common.constant.CacheConstants;\nimport top.flya.common.constant.Constants;\nimport top.flya.common.core.domain.R;\nimport top.flya.common.core.domain.event.LogininforEvent;\nimport top.flya.common.core.domain.dto.RoleDTO;\nimport top.flya.common.core.domain.entity.SysUser;\nimport top.flya.common.core.domain.model.LoginUser;\nimport top.flya.common.core.domain.model.XcxLoginUser;\nimport top.flya.common.enums.DeviceType;\nimport top.flya.common.enums.LoginType;\nimport top.flya.common.enums.UserStatus;\nimport top.flya.common.exception.user.CaptchaException;\nimport top.flya.common.exception.user.CaptchaExpireException;\nimport top.flya.common.exception.user.UserException;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.DateUtils;\nimport top.flya.common.utils.MessageUtils;\nimport top.flya.common.utils.ServletUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.redis.RedisUtils;\nimport top.flya.common.utils.spring.SpringUtils;\nimport top.flya.system.mapper.SysUserMapper;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Service;\n\nimport java.time.Duration;\nimport java.util.List;\nimport java.util.function.Supplier;\n\n/**\n * 登录校验方法\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Slf4j\n@Service\npublic class SysLoginService {\n\n    private final SysUserMapper userMapper;\n    private final ISysConfigService configService;\n    private final SysPermissionService permissionService;\n\n    @Value(\"${user.password.maxRetryCount}\")\n    private Integer maxRetryCount;\n\n    @Value(\"${user.password.lockTime}\")\n    private Integer lockTime;\n\n    /**\n     * 登录验证\n     *\n     * @param username 用户名\n     * @param password 密码\n     * @param code     验证码\n     * @param uuid     唯一标识\n     * @return 结果\n     */\n    public String login(String username, String password, String code, String uuid) {\n        boolean captchaEnabled = configService.selectCaptchaEnabled();\n        // 验证码开关\n        if (captchaEnabled) {\n            validateCaptcha(username, code, uuid);\n        }\n        SysUser user = loadUserByUsername(username);\n        checkLogin(LoginType.PASSWORD, username, () -> !BCrypt.checkpw(password, user.getPassword()));\n        // 此处可根据登录用户的数据不同 自行创建 loginUser\n        LoginUser loginUser = buildLoginUser(user);\n        // 生成token\n        LoginHelper.loginByDevice(loginUser, DeviceType.PC);\n\n        recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message(\"user.login.success\"));\n        recordLoginInfo(user.getUserId(), username);\n        return StpUtil.getTokenValue();\n    }\n\n    public String smsLogin(String phonenumber, String smsCode) {\n        // 通过手机号查找用户\n        SysUser user = loadUserByPhonenumber(phonenumber);\n\n        checkLogin(LoginType.SMS, user.getUserName(), () -> !validateSmsCode(phonenumber, smsCode));\n        // 此处可根据登录用户的数据不同 自行创建 loginUser\n        LoginUser loginUser = buildLoginUser(user);\n        // 生成token\n        LoginHelper.loginByDevice(loginUser, DeviceType.APP);\n\n        recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message(\"user.login.success\"));\n        recordLoginInfo(user.getUserId(), user.getUserName());\n        return StpUtil.getTokenValue();\n    }\n\n    public String emailLogin(String email, String emailCode) {\n        // 通过手机号查找用户\n        SysUser user = loadUserByEmail(email);\n\n        checkLogin(LoginType.EMAIL, user.getUserName(), () -> !validateEmailCode(email, emailCode));\n        // 此处可根据登录用户的数据不同 自行创建 loginUser\n        LoginUser loginUser = buildLoginUser(user);\n        // 生成token\n        LoginHelper.loginByDevice(loginUser, DeviceType.APP);\n\n        recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message(\"user.login.success\"));\n        recordLoginInfo(user.getUserId(), user.getUserName());\n        return StpUtil.getTokenValue();\n    }\n\n    public String xcxLogin(String xcxCode) {\n//        String url = \"https://api.weixin.qq.com/sns/jscode2session?appid=\" + appId +\n//            \"&secret=\" + secret + \"&js_code=\" + xcxCode + \"&grant_type=authorization_code\";\n//        log.info(\"微信小程序登录 url : {}\", url);\n//        String response = HttpUtil.get(url);\n//\n//        JSONObject wxUser = JSONObject.parseObject(response);\n//        if (com.baomidou.mybatisplus.core.toolkit.StringUtils.checkValNull(wxUser) || wxUser.get(\"errcode\") != null) {\n//            return null;\n//        }\n//        String openId = wxUser.get(\"openid\").toString();\n//        //如果存在 就直接返回 不存在就新建用户\n//        PzcUser user = userMapper.selectOne(new QueryWrapper<PzcUser>().eq(\"openid\", openId));\n//        SysUser user = loadUserByOpenid(openid);\n//\n//        // 此处可根据登录用户的数据不同 自行创建 loginUser\n//        XcxLoginUser loginUser = new XcxLoginUser();\n//        loginUser.setUserId(user.getUserId());\n//        loginUser.setUsername(user.getUserName());\n//        loginUser.setUserType(user.getUserType());\n//        loginUser.setOpenid(openId);\n//        // 生成token\n//        LoginHelper.loginByDevice(loginUser, DeviceType.XCX);\n//\n//        recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message(\"user.login.success\"));\n//        recordLoginInfo(user.getUserId(), user.getUserName());\n//        return StpUtil.getTokenValue();\n        return null;\n    }\n\n    /**\n     * 退出登录\n     */\n    public void logout() {\n        try {\n            LoginUser loginUser = LoginHelper.getLoginUser();\n            StpUtil.logout();\n            recordLogininfor(loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message(\"user.logout.success\"));\n        } catch (NotLoginException ignored) {\n        }\n    }\n\n    /**\n     * 记录登录信息\n     *\n     * @param username 用户名\n     * @param status   状态\n     * @param message  消息内容\n     */\n    private void recordLogininfor(String username, String status, String message) {\n        LogininforEvent logininforEvent = new LogininforEvent();\n        logininforEvent.setUsername(username);\n        logininforEvent.setStatus(status);\n        logininforEvent.setMessage(message);\n        logininforEvent.setRequest(ServletUtils.getRequest());\n        SpringUtils.context().publishEvent(logininforEvent);\n    }\n\n    /**\n     * 校验短信验证码\n     */\n    private boolean validateSmsCode(String phonenumber, String smsCode) {\n        String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_CODE_KEY + phonenumber);\n        if (StringUtils.isBlank(code)) {\n            recordLogininfor(phonenumber, Constants.LOGIN_FAIL, MessageUtils.message(\"user.jcaptcha.expire\"));\n            throw new CaptchaExpireException();\n        }\n        return code.equals(smsCode);\n    }\n\n    /**\n     * 校验邮箱验证码\n     */\n    private boolean validateEmailCode(String email, String emailCode) {\n        String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_CODE_KEY + email);\n        if (StringUtils.isBlank(code)) {\n            recordLogininfor(email, Constants.LOGIN_FAIL, MessageUtils.message(\"user.jcaptcha.expire\"));\n            throw new CaptchaExpireException();\n        }\n        return code.equals(emailCode);\n    }\n\n    /**\n     * 校验验证码\n     *\n     * @param username 用户名\n     * @param code     验证码\n     * @param uuid     唯一标识\n     */\n    public void validateCaptcha(String username, String code, String uuid) {\n        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, \"\");\n        String captcha = RedisUtils.getCacheObject(verifyKey);\n        RedisUtils.deleteObject(verifyKey);\n        if (captcha == null) {\n            recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message(\"user.jcaptcha.expire\"));\n            throw new CaptchaExpireException();\n        }\n        if (!code.equalsIgnoreCase(captcha)) {\n            recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message(\"user.jcaptcha.error\"));\n            throw new CaptchaException();\n        }\n    }\n\n    private SysUser loadUserByUsername(String username) {\n        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()\n            .select(SysUser::getUserName, SysUser::getStatus)\n            .eq(SysUser::getUserName, username));\n        if (ObjectUtil.isNull(user)) {\n            log.info(\"登录用户：{} 不存在.\", username);\n            throw new UserException(\"user.not.exists\", username);\n        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {\n            log.info(\"登录用户：{} 已被停用.\", username);\n            throw new UserException(\"user.blocked\", username);\n        }\n        return userMapper.selectUserByUserName(username);\n    }\n\n    private SysUser loadUserByPhonenumber(String phonenumber) {\n        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()\n            .select(SysUser::getPhonenumber, SysUser::getStatus)\n            .eq(SysUser::getPhonenumber, phonenumber));\n        if (ObjectUtil.isNull(user)) {\n            log.info(\"登录用户：{} 不存在.\", phonenumber);\n            throw new UserException(\"user.not.exists\", phonenumber);\n        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {\n            log.info(\"登录用户：{} 已被停用.\", phonenumber);\n            throw new UserException(\"user.blocked\", phonenumber);\n        }\n        return userMapper.selectUserByPhonenumber(phonenumber);\n    }\n\n    private SysUser loadUserByEmail(String email) {\n        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()\n            .select(SysUser::getPhonenumber, SysUser::getStatus)\n            .eq(SysUser::getEmail, email));\n        if (ObjectUtil.isNull(user)) {\n            log.info(\"登录用户：{} 不存在.\", email);\n            throw new UserException(\"user.not.exists\", email);\n        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {\n            log.info(\"登录用户：{} 已被停用.\", email);\n            throw new UserException(\"user.blocked\", email);\n        }\n        return userMapper.selectUserByEmail(email);\n    }\n\n    private SysUser loadUserByOpenid(String openid) {\n        // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户\n        // todo 自行实现 userService.selectUserByOpenid(openid);\n        SysUser user = new SysUser();\n        if (ObjectUtil.isNull(user)) {\n            log.info(\"登录用户：{} 不存在.\", openid);\n            // todo 用户不存在 业务逻辑自行实现\n        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {\n            log.info(\"登录用户：{} 已被停用.\", openid);\n            // todo 用户已被停用 业务逻辑自行实现\n        }\n        return user;\n    }\n\n    /**\n     * 构建登录用户\n     */\n    private LoginUser buildLoginUser(SysUser user) {\n        LoginUser loginUser = new LoginUser();\n        loginUser.setUserId(user.getUserId());\n        loginUser.setDeptId(user.getDeptId());\n        loginUser.setUsername(user.getUserName());\n        loginUser.setUserType(user.getUserType());\n        loginUser.setMenuPermission(permissionService.getMenuPermission(user));\n        loginUser.setRolePermission(permissionService.getRolePermission(user));\n        loginUser.setDeptName(ObjectUtil.isNull(user.getDept()) ? \"\" : user.getDept().getDeptName());\n        List<RoleDTO> roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class);\n        loginUser.setRoles(roles);\n        return loginUser;\n    }\n\n    /**\n     * 记录登录信息\n     *\n     * @param userId 用户ID\n     */\n    public void recordLoginInfo(Long userId, String username) {\n        SysUser sysUser = new SysUser();\n        sysUser.setUserId(userId);\n        sysUser.setLoginIp(ServletUtils.getClientIP());\n        sysUser.setLoginDate(DateUtils.getNowDate());\n//        sysUser.setUpdateBy(username);\n        userMapper.updateById(sysUser);\n    }\n\n    /**\n     * 登录校验\n     */\n    private void checkLogin(LoginType loginType, String username, Supplier<Boolean> supplier) {\n        String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username;\n        String loginFail = Constants.LOGIN_FAIL;\n\n        // 获取用户登录错误次数(可自定义限制策略 例如: key + username + ip)\n        Integer errorNumber = RedisUtils.getCacheObject(errorKey);\n        // 锁定时间内登录 则踢出\n        if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(maxRetryCount)) {\n            recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));\n            throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);\n        }\n\n        if (supplier.get()) {\n            // 是否第一次\n            errorNumber = ObjectUtil.isNull(errorNumber) ? 1 : errorNumber + 1;\n            // 达到规定错误次数 则锁定登录\n            if (errorNumber.equals(maxRetryCount)) {\n                RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime));\n                recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));\n                throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);\n            } else {\n                // 未达到规定错误次数 则递增\n                RedisUtils.setCacheObject(errorKey, errorNumber);\n                recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber));\n                throw new UserException(loginType.getRetryLimitCount(), errorNumber);\n            }\n        }\n\n        // 登录成功 清空错误次数\n        RedisUtils.deleteObject(errorKey);\n    }\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/SysPermissionService.java",
    "content": "package top.flya.system.service;\n\nimport top.flya.common.core.domain.entity.SysUser;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * 用户权限处理\n *\n * @author ruoyi\n */\n@RequiredArgsConstructor\n@Service\npublic class SysPermissionService {\n\n    private final ISysRoleService roleService;\n    private final ISysMenuService menuService;\n\n    /**\n     * 获取角色数据权限\n     *\n     * @param user 用户信息\n     * @return 角色权限信息\n     */\n    public Set<String> getRolePermission(SysUser user) {\n        Set<String> roles = new HashSet<>();\n        // 管理员拥有所有权限\n        if (user.isAdmin()) {\n            roles.add(\"admin\");\n        } else {\n            roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId()));\n        }\n        return roles;\n    }\n\n    /**\n     * 获取菜单数据权限\n     *\n     * @param user 用户信息\n     * @return 菜单权限信息\n     */\n    public Set<String> getMenuPermission(SysUser user) {\n        Set<String> perms = new HashSet<>();\n        // 管理员拥有所有权限\n        if (user.isAdmin()) {\n            perms.add(\"*:*:*\");\n        } else {\n            perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId()));\n        }\n        return perms;\n    }\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/SysRegisterService.java",
    "content": "package top.flya.system.service;\n\nimport cn.dev33.satoken.secure.BCrypt;\nimport top.flya.common.constant.CacheConstants;\nimport top.flya.common.constant.Constants;\nimport top.flya.common.core.domain.event.LogininforEvent;\nimport top.flya.common.core.domain.entity.SysUser;\nimport top.flya.common.core.domain.model.RegisterBody;\nimport top.flya.common.enums.UserType;\nimport top.flya.common.exception.user.CaptchaException;\nimport top.flya.common.exception.user.CaptchaExpireException;\nimport top.flya.common.exception.user.UserException;\nimport top.flya.common.utils.MessageUtils;\nimport top.flya.common.utils.ServletUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.redis.RedisUtils;\nimport top.flya.common.utils.spring.SpringUtils;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\n\n/**\n * 注册校验方法\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Service\npublic class SysRegisterService {\n\n    private final ISysUserService userService;\n    private final ISysConfigService configService;\n\n    /**\n     * 注册\n     */\n    public void register(RegisterBody registerBody) {\n        String username = registerBody.getUsername();\n        String password = registerBody.getPassword();\n        // 校验用户类型是否存在\n        String userType = UserType.getUserType(registerBody.getUserType()).getUserType();\n\n        boolean captchaEnabled = configService.selectCaptchaEnabled();\n        // 验证码开关\n        if (captchaEnabled) {\n            validateCaptcha(username, registerBody.getCode(), registerBody.getUuid());\n        }\n        SysUser sysUser = new SysUser();\n        sysUser.setUserName(username);\n        sysUser.setNickName(username);\n        sysUser.setPassword(BCrypt.hashpw(password));\n        sysUser.setUserType(userType);\n\n        if (!userService.checkUserNameUnique(sysUser)) {\n            throw new UserException(\"user.register.save.error\", username);\n        }\n        boolean regFlag = userService.registerUser(sysUser);\n        if (!regFlag) {\n            throw new UserException(\"user.register.error\");\n        }\n        recordLogininfor(username, Constants.REGISTER, MessageUtils.message(\"user.register.success\"));\n    }\n\n    /**\n     * 校验验证码\n     *\n     * @param username 用户名\n     * @param code     验证码\n     * @param uuid     唯一标识\n     */\n    public void validateCaptcha(String username, String code, String uuid) {\n        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, \"\");\n        String captcha = RedisUtils.getCacheObject(verifyKey);\n        RedisUtils.deleteObject(verifyKey);\n        if (captcha == null) {\n            recordLogininfor(username, Constants.REGISTER, MessageUtils.message(\"user.jcaptcha.expire\"));\n            throw new CaptchaExpireException();\n        }\n        if (!code.equalsIgnoreCase(captcha)) {\n            recordLogininfor(username, Constants.REGISTER, MessageUtils.message(\"user.jcaptcha.error\"));\n            throw new CaptchaException();\n        }\n    }\n\n    /**\n     * 记录登录信息\n     *\n     * @param username 用户名\n     * @param status   状态\n     * @param message  消息内容\n     * @return\n     */\n    private void recordLogininfor(String username, String status, String message) {\n        LogininforEvent logininforEvent = new LogininforEvent();\n        logininforEvent.setUsername(username);\n        logininforEvent.setStatus(status);\n        logininforEvent.setMessage(message);\n        logininforEvent.setRequest(ServletUtils.getRequest());\n        SpringUtils.context().publishEvent(logininforEvent);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/impl/SysConfigServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport top.flya.common.constant.CacheNames;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.service.ConfigService;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.redis.CacheUtils;\nimport top.flya.common.utils.spring.SpringUtils;\nimport top.flya.system.domain.SysConfig;\nimport top.flya.system.mapper.SysConfigMapper;\nimport top.flya.system.service.ISysConfigService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.cache.annotation.CachePut;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 参数配置 服务层实现\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Service\npublic class SysConfigServiceImpl implements ISysConfigService, ConfigService {\n\n    private final SysConfigMapper baseMapper;\n\n    @Override\n    public TableDataInfo<SysConfig> selectPageConfigList(SysConfig config, PageQuery pageQuery) {\n        Map<String, Object> params = config.getParams();\n        LambdaQueryWrapper<SysConfig> lqw = new LambdaQueryWrapper<SysConfig>()\n            .like(StringUtils.isNotBlank(config.getConfigName()), SysConfig::getConfigName, config.getConfigName())\n            .eq(StringUtils.isNotBlank(config.getConfigType()), SysConfig::getConfigType, config.getConfigType())\n            .like(StringUtils.isNotBlank(config.getConfigKey()), SysConfig::getConfigKey, config.getConfigKey())\n            .between(params.get(\"beginTime\") != null && params.get(\"endTime\") != null,\n                SysConfig::getCreateTime, params.get(\"beginTime\"), params.get(\"endTime\"));\n        Page<SysConfig> page = baseMapper.selectPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(page);\n    }\n\n    /**\n     * 查询参数配置信息\n     *\n     * @param configId 参数配置ID\n     * @return 参数配置信息\n     */\n    @Override\n    @DS(\"master\")\n    public SysConfig selectConfigById(Long configId) {\n        return baseMapper.selectById(configId);\n    }\n\n    /**\n     * 根据键名查询参数配置信息\n     *\n     * @param configKey 参数key\n     * @return 参数键值\n     */\n    @Cacheable(cacheNames = CacheNames.SYS_CONFIG, key = \"#configKey\")\n    @Override\n    public String selectConfigByKey(String configKey) {\n        SysConfig retConfig = baseMapper.selectOne(new LambdaQueryWrapper<SysConfig>()\n            .eq(SysConfig::getConfigKey, configKey));\n        if (ObjectUtil.isNotNull(retConfig)) {\n            return retConfig.getConfigValue();\n        }\n        return StringUtils.EMPTY;\n    }\n\n    /**\n     * 获取验证码开关\n     *\n     * @return true开启，false关闭\n     */\n    @Override\n    public boolean selectCaptchaEnabled() {\n        String captchaEnabled = SpringUtils.getAopProxy(this).selectConfigByKey(\"sys.account.captchaEnabled\");\n        if (StringUtils.isEmpty(captchaEnabled)) {\n            return true;\n        }\n        return Convert.toBool(captchaEnabled);\n    }\n\n    /**\n     * 查询参数配置列表\n     *\n     * @param config 参数配置信息\n     * @return 参数配置集合\n     */\n    @Override\n    public List<SysConfig> selectConfigList(SysConfig config) {\n        Map<String, Object> params = config.getParams();\n        LambdaQueryWrapper<SysConfig> lqw = new LambdaQueryWrapper<SysConfig>()\n            .like(StringUtils.isNotBlank(config.getConfigName()), SysConfig::getConfigName, config.getConfigName())\n            .eq(StringUtils.isNotBlank(config.getConfigType()), SysConfig::getConfigType, config.getConfigType())\n            .like(StringUtils.isNotBlank(config.getConfigKey()), SysConfig::getConfigKey, config.getConfigKey())\n            .between(params.get(\"beginTime\") != null && params.get(\"endTime\") != null,\n                SysConfig::getCreateTime, params.get(\"beginTime\"), params.get(\"endTime\"));\n        return baseMapper.selectList(lqw);\n    }\n\n    /**\n     * 新增参数配置\n     *\n     * @param config 参数配置信息\n     * @return 结果\n     */\n    @CachePut(cacheNames = CacheNames.SYS_CONFIG, key = \"#config.configKey\")\n    @Override\n    public String insertConfig(SysConfig config) {\n        int row = baseMapper.insert(config);\n        if (row > 0) {\n            return config.getConfigValue();\n        }\n        throw new ServiceException(\"操作失败\");\n    }\n\n    /**\n     * 修改参数配置\n     *\n     * @param config 参数配置信息\n     * @return 结果\n     */\n    @CachePut(cacheNames = CacheNames.SYS_CONFIG, key = \"#config.configKey\")\n    @Override\n    public String updateConfig(SysConfig config) {\n        int row = 0;\n        if (config.getConfigId() != null) {\n            SysConfig temp = baseMapper.selectById(config.getConfigId());\n            if (!StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) {\n                CacheUtils.evict(CacheNames.SYS_CONFIG, temp.getConfigKey());\n            }\n            row = baseMapper.updateById(config);\n        } else {\n            row = baseMapper.update(config, new LambdaQueryWrapper<SysConfig>()\n                .eq(SysConfig::getConfigKey, config.getConfigKey()));\n        }\n        if (row > 0) {\n            return config.getConfigValue();\n        }\n        throw new ServiceException(\"操作失败\");\n    }\n\n    /**\n     * 批量删除参数信息\n     *\n     * @param configIds 需要删除的参数ID\n     */\n    @Override\n    public void deleteConfigByIds(Long[] configIds) {\n        for (Long configId : configIds) {\n            SysConfig config = selectConfigById(configId);\n            if (StringUtils.equals(UserConstants.YES, config.getConfigType())) {\n                throw new ServiceException(String.format(\"内置参数【%1$s】不能删除 \", config.getConfigKey()));\n            }\n            CacheUtils.evict(CacheNames.SYS_CONFIG, config.getConfigKey());\n        }\n        baseMapper.deleteBatchIds(Arrays.asList(configIds));\n    }\n\n    /**\n     * 加载参数缓存数据\n     */\n    @Override\n    public void loadingConfigCache() {\n        List<SysConfig> configsList = selectConfigList(new SysConfig());\n        configsList.forEach(config ->\n            CacheUtils.put(CacheNames.SYS_CONFIG, config.getConfigKey(), config.getConfigValue()));\n    }\n\n    /**\n     * 清空参数缓存数据\n     */\n    @Override\n    public void clearConfigCache() {\n        CacheUtils.clear(CacheNames.SYS_CONFIG);\n    }\n\n    /**\n     * 重置参数缓存数据\n     */\n    @Override\n    public void resetConfigCache() {\n        clearConfigCache();\n        loadingConfigCache();\n    }\n\n    /**\n     * 校验参数键名是否唯一\n     *\n     * @param config 参数配置信息\n     * @return 结果\n     */\n    @Override\n    public boolean checkConfigKeyUnique(SysConfig config) {\n        long configId = ObjectUtil.isNull(config.getConfigId()) ? -1L : config.getConfigId();\n        SysConfig info = baseMapper.selectOne(new LambdaQueryWrapper<SysConfig>().eq(SysConfig::getConfigKey, config.getConfigKey()));\n        if (ObjectUtil.isNotNull(info) && info.getConfigId() != configId) {\n            return false;\n        }\n        return true;\n    }\n\n    /**\n     * 根据参数 key 获取参数值\n     *\n     * @param configKey 参数 key\n     * @return 参数值\n     */\n    @Override\n    public String getConfigValue(String configKey) {\n        return SpringUtils.getAopProxy(this).selectConfigByKey(configKey);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/impl/SysDataScopeServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.convert.Convert;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport top.flya.common.core.domain.entity.SysDept;\nimport top.flya.common.helper.DataBaseHelper;\nimport top.flya.common.utils.StreamUtils;\nimport top.flya.system.domain.SysRoleDept;\nimport top.flya.system.mapper.SysDeptMapper;\nimport top.flya.system.mapper.SysRoleDeptMapper;\nimport top.flya.system.service.ISysDataScopeService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n/**\n * 数据权限 实现\n * <p>\n * 注意: 此Service内不允许调用标注`数据权限`注解的方法\n * 例如: deptMapper.selectList 此 selectList 方法标注了`数据权限`注解 会出现循环解析的问题\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Service(\"sdss\")\npublic class SysDataScopeServiceImpl implements ISysDataScopeService {\n\n    private final SysRoleDeptMapper roleDeptMapper;\n    private final SysDeptMapper deptMapper;\n\n    @Override\n    public String getRoleCustom(Long roleId) {\n        List<SysRoleDept> list = roleDeptMapper.selectList(\n            new LambdaQueryWrapper<SysRoleDept>()\n                .select(SysRoleDept::getDeptId)\n                .eq(SysRoleDept::getRoleId, roleId));\n        if (CollUtil.isNotEmpty(list)) {\n            return StreamUtils.join(list, rd -> Convert.toStr(rd.getDeptId()));\n        }\n        return null;\n    }\n\n    @Override\n    public String getDeptAndChild(Long deptId) {\n        List<SysDept> deptList = deptMapper.selectList(new LambdaQueryWrapper<SysDept>()\n            .select(SysDept::getDeptId)\n            .apply(DataBaseHelper.findInSet(deptId, \"ancestors\")));\n        List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId);\n        ids.add(deptId);\n        List<SysDept> list = deptMapper.selectList(new LambdaQueryWrapper<SysDept>()\n            .select(SysDept::getDeptId)\n            .in(SysDept::getDeptId, ids));\n        if (CollUtil.isNotEmpty(list)) {\n            return StreamUtils.join(list, d -> Convert.toStr(d.getDeptId()));\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/impl/SysDeptServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport top.flya.common.constant.CacheNames;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.domain.entity.SysDept;\nimport top.flya.common.core.domain.entity.SysRole;\nimport top.flya.common.core.domain.entity.SysUser;\nimport top.flya.common.core.service.DeptService;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.helper.DataBaseHelper;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.TreeBuildUtils;\nimport top.flya.common.utils.redis.CacheUtils;\nimport top.flya.common.utils.spring.SpringUtils;\nimport top.flya.system.mapper.SysDeptMapper;\nimport top.flya.system.mapper.SysRoleMapper;\nimport top.flya.system.mapper.SysUserMapper;\nimport top.flya.system.service.ISysDeptService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.cache.annotation.CacheEvict;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 部门管理 服务实现\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Service\npublic class SysDeptServiceImpl implements ISysDeptService, DeptService {\n\n    private final SysDeptMapper baseMapper;\n    private final SysRoleMapper roleMapper;\n    private final SysUserMapper userMapper;\n\n    /**\n     * 查询部门管理数据\n     *\n     * @param dept 部门信息\n     * @return 部门信息集合\n     */\n    @Override\n    public List<SysDept> selectDeptList(SysDept dept) {\n        LambdaQueryWrapper<SysDept> lqw = new LambdaQueryWrapper<>();\n        lqw.eq(SysDept::getDelFlag, \"0\")\n            .eq(ObjectUtil.isNotNull(dept.getDeptId()), SysDept::getDeptId, dept.getDeptId())\n            .eq(ObjectUtil.isNotNull(dept.getParentId()), SysDept::getParentId, dept.getParentId())\n            .like(StringUtils.isNotBlank(dept.getDeptName()), SysDept::getDeptName, dept.getDeptName())\n            .eq(StringUtils.isNotBlank(dept.getStatus()), SysDept::getStatus, dept.getStatus())\n            .orderByAsc(SysDept::getParentId)\n            .orderByAsc(SysDept::getOrderNum);\n        return baseMapper.selectDeptList(lqw);\n    }\n\n    /**\n     * 查询部门树结构信息\n     *\n     * @param dept 部门信息\n     * @return 部门树信息集合\n     */\n    @Override\n    public List<Tree<Long>> selectDeptTreeList(SysDept dept) {\n        List<SysDept> depts = this.selectDeptList(dept);\n        return buildDeptTreeSelect(depts);\n    }\n\n    /**\n     * 构建前端所需要下拉树结构\n     *\n     * @param depts 部门列表\n     * @return 下拉树结构列表\n     */\n    @Override\n    public List<Tree<Long>> buildDeptTreeSelect(List<SysDept> depts) {\n        if (CollUtil.isEmpty(depts)) {\n            return CollUtil.newArrayList();\n        }\n        return TreeBuildUtils.build(depts, (dept, tree) ->\n            tree.setId(dept.getDeptId())\n                .setParentId(dept.getParentId())\n                .setName(dept.getDeptName())\n                .setWeight(dept.getOrderNum()));\n    }\n\n    /**\n     * 根据角色ID查询部门树信息\n     *\n     * @param roleId 角色ID\n     * @return 选中部门列表\n     */\n    @Override\n    public List<Long> selectDeptListByRoleId(Long roleId) {\n        SysRole role = roleMapper.selectById(roleId);\n        return baseMapper.selectDeptListByRoleId(roleId, role.getDeptCheckStrictly());\n    }\n\n    /**\n     * 根据部门ID查询信息\n     *\n     * @param deptId 部门ID\n     * @return 部门信息\n     */\n    @Cacheable(cacheNames = CacheNames.SYS_DEPT, key = \"#deptId\")\n    @Override\n    public SysDept selectDeptById(Long deptId) {\n        SysDept dept = baseMapper.selectById(deptId);\n        if (ObjectUtil.isNull(dept)) {\n            return null;\n        }\n        SysDept parentDept = baseMapper.selectOne(new LambdaQueryWrapper<SysDept>()\n            .select(SysDept::getDeptName).eq(SysDept::getDeptId, dept.getParentId()));\n        dept.setParentName(ObjectUtil.isNotNull(parentDept) ? parentDept.getDeptName() : null);\n        return dept;\n    }\n\n    /**\n     * 通过部门ID查询部门名称\n     *\n     * @param deptIds 部门ID串逗号分隔\n     * @return 部门名称串逗号分隔\n     */\n    @Override\n    public String selectDeptNameByIds(String deptIds) {\n        List<String> list = new ArrayList<>();\n        for (Long id : StringUtils.splitTo(deptIds, Convert::toLong)) {\n            SysDept dept = SpringUtils.getAopProxy(this).selectDeptById(id);\n            if (ObjectUtil.isNotNull(dept)) {\n                list.add(dept.getDeptName());\n            }\n        }\n        return String.join(StringUtils.SEPARATOR, list);\n    }\n\n    /**\n     * 根据ID查询所有子部门数（正常状态）\n     *\n     * @param deptId 部门ID\n     * @return 子部门数\n     */\n    @Override\n    public long selectNormalChildrenDeptById(Long deptId) {\n        return baseMapper.selectCount(new LambdaQueryWrapper<SysDept>()\n            .eq(SysDept::getStatus, UserConstants.DEPT_NORMAL)\n            .apply(DataBaseHelper.findInSet(deptId, \"ancestors\")));\n    }\n\n    /**\n     * 是否存在子节点\n     *\n     * @param deptId 部门ID\n     * @return 结果\n     */\n    @Override\n    public boolean hasChildByDeptId(Long deptId) {\n        return baseMapper.exists(new LambdaQueryWrapper<SysDept>()\n            .eq(SysDept::getParentId, deptId));\n    }\n\n    /**\n     * 查询部门是否存在用户\n     *\n     * @param deptId 部门ID\n     * @return 结果 true 存在 false 不存在\n     */\n    @Override\n    public boolean checkDeptExistUser(Long deptId) {\n        return userMapper.exists(new LambdaQueryWrapper<SysUser>()\n            .eq(SysUser::getDeptId, deptId));\n    }\n\n    /**\n     * 校验部门名称是否唯一\n     *\n     * @param dept 部门信息\n     * @return 结果\n     */\n    @Override\n    public boolean checkDeptNameUnique(SysDept dept) {\n        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysDept>()\n            .eq(SysDept::getDeptName, dept.getDeptName())\n            .eq(SysDept::getParentId, dept.getParentId())\n            .ne(ObjectUtil.isNotNull(dept.getDeptId()), SysDept::getDeptId, dept.getDeptId()));\n        return !exist;\n    }\n\n    /**\n     * 校验部门是否有数据权限\n     *\n     * @param deptId 部门id\n     */\n    @Override\n    public void checkDeptDataScope(Long deptId) {\n        if (!LoginHelper.isAdmin()) {\n            SysDept dept = new SysDept();\n            dept.setDeptId(deptId);\n            List<SysDept> depts = this.selectDeptList(dept);\n            if (CollUtil.isEmpty(depts)) {\n                throw new ServiceException(\"没有权限访问部门数据！\");\n            }\n        }\n    }\n\n    /**\n     * 新增保存部门信息\n     *\n     * @param dept 部门信息\n     * @return 结果\n     */\n    @Override\n    public int insertDept(SysDept dept) {\n        SysDept info = baseMapper.selectById(dept.getParentId());\n        // 如果父节点不为正常状态,则不允许新增子节点\n        if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) {\n            throw new ServiceException(\"部门停用，不允许新增\");\n        }\n        dept.setAncestors(info.getAncestors() + StringUtils.SEPARATOR + dept.getParentId());\n        return baseMapper.insert(dept);\n    }\n\n    /**\n     * 修改保存部门信息\n     *\n     * @param dept 部门信息\n     * @return 结果\n     */\n    @CacheEvict(cacheNames = CacheNames.SYS_DEPT, key = \"#dept.deptId\")\n    @Override\n    public int updateDept(SysDept dept) {\n        SysDept newParentDept = baseMapper.selectById(dept.getParentId());\n        SysDept oldDept = baseMapper.selectById(dept.getDeptId());\n        if (ObjectUtil.isNotNull(newParentDept) && ObjectUtil.isNotNull(oldDept)) {\n            String newAncestors = newParentDept.getAncestors() + StringUtils.SEPARATOR + newParentDept.getDeptId();\n            String oldAncestors = oldDept.getAncestors();\n            dept.setAncestors(newAncestors);\n            updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors);\n        }\n        int result = baseMapper.updateById(dept);\n        if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors())\n            && !StringUtils.equals(UserConstants.DEPT_NORMAL, dept.getAncestors())) {\n            // 如果该部门是启用状态，则启用该部门的所有上级部门\n            updateParentDeptStatusNormal(dept);\n        }\n        return result;\n    }\n\n    /**\n     * 修改该部门的父级部门状态\n     *\n     * @param dept 当前部门\n     */\n    private void updateParentDeptStatusNormal(SysDept dept) {\n        String ancestors = dept.getAncestors();\n        Long[] deptIds = Convert.toLongArray(ancestors);\n        baseMapper.update(null, new LambdaUpdateWrapper<SysDept>()\n            .set(SysDept::getStatus, UserConstants.DEPT_NORMAL)\n            .in(SysDept::getDeptId, Arrays.asList(deptIds)));\n    }\n\n    /**\n     * 修改子元素关系\n     *\n     * @param deptId       被修改的部门ID\n     * @param newAncestors 新的父ID集合\n     * @param oldAncestors 旧的父ID集合\n     */\n    public void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) {\n        List<SysDept> children = baseMapper.selectList(new LambdaQueryWrapper<SysDept>()\n            .apply(DataBaseHelper.findInSet(deptId, \"ancestors\")));\n        List<SysDept> list = new ArrayList<>();\n        for (SysDept child : children) {\n            SysDept dept = new SysDept();\n            dept.setDeptId(child.getDeptId());\n            dept.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors));\n            list.add(dept);\n        }\n        if (CollUtil.isNotEmpty(list)) {\n            if (baseMapper.updateBatchById(list)) {\n                list.forEach(dept -> CacheUtils.evict(CacheNames.SYS_DEPT, dept.getDeptId()));\n            }\n        }\n    }\n\n    /**\n     * 删除部门管理信息\n     *\n     * @param deptId 部门ID\n     * @return 结果\n     */\n    @CacheEvict(cacheNames = CacheNames.SYS_DEPT, key = \"#deptId\")\n    @Override\n    public int deleteDeptById(Long deptId) {\n        return baseMapper.deleteById(deptId);\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/impl/SysDictDataServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport top.flya.common.constant.CacheNames;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.entity.SysDictData;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.redis.CacheUtils;\nimport top.flya.system.mapper.SysDictDataMapper;\nimport top.flya.system.service.ISysDictDataService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.cache.annotation.CachePut;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n/**\n * 字典 业务层处理\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Service\npublic class SysDictDataServiceImpl implements ISysDictDataService {\n\n    private final SysDictDataMapper baseMapper;\n\n    @Override\n    public TableDataInfo<SysDictData> selectPageDictDataList(SysDictData dictData, PageQuery pageQuery) {\n        LambdaQueryWrapper<SysDictData> lqw = new LambdaQueryWrapper<SysDictData>()\n            .eq(StringUtils.isNotBlank(dictData.getDictType()), SysDictData::getDictType, dictData.getDictType())\n            .like(StringUtils.isNotBlank(dictData.getDictLabel()), SysDictData::getDictLabel, dictData.getDictLabel())\n            .eq(StringUtils.isNotBlank(dictData.getStatus()), SysDictData::getStatus, dictData.getStatus())\n            .orderByAsc(SysDictData::getDictSort);\n        Page<SysDictData> page = baseMapper.selectPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(page);\n    }\n\n    /**\n     * 根据条件分页查询字典数据\n     *\n     * @param dictData 字典数据信息\n     * @return 字典数据集合信息\n     */\n    @Override\n    public List<SysDictData> selectDictDataList(SysDictData dictData) {\n        return baseMapper.selectList(new LambdaQueryWrapper<SysDictData>()\n            .eq(StringUtils.isNotBlank(dictData.getDictType()), SysDictData::getDictType, dictData.getDictType())\n            .like(StringUtils.isNotBlank(dictData.getDictLabel()), SysDictData::getDictLabel, dictData.getDictLabel())\n            .eq(StringUtils.isNotBlank(dictData.getStatus()), SysDictData::getStatus, dictData.getStatus())\n            .orderByAsc(SysDictData::getDictSort));\n    }\n\n    /**\n     * 根据字典类型和字典键值查询字典数据信息\n     *\n     * @param dictType  字典类型\n     * @param dictValue 字典键值\n     * @return 字典标签\n     */\n    @Override\n    public String selectDictLabel(String dictType, String dictValue) {\n        return baseMapper.selectOne(new LambdaQueryWrapper<SysDictData>()\n                .select(SysDictData::getDictLabel)\n                .eq(SysDictData::getDictType, dictType)\n                .eq(SysDictData::getDictValue, dictValue))\n            .getDictLabel();\n    }\n\n    /**\n     * 根据字典数据ID查询信息\n     *\n     * @param dictCode 字典数据ID\n     * @return 字典数据\n     */\n    @Override\n    public SysDictData selectDictDataById(Long dictCode) {\n        return baseMapper.selectById(dictCode);\n    }\n\n    /**\n     * 批量删除字典数据信息\n     *\n     * @param dictCodes 需要删除的字典数据ID\n     */\n    @Override\n    public void deleteDictDataByIds(Long[] dictCodes) {\n        for (Long dictCode : dictCodes) {\n            SysDictData data = selectDictDataById(dictCode);\n            baseMapper.deleteById(dictCode);\n            CacheUtils.evict(CacheNames.SYS_DICT, data.getDictType());\n        }\n    }\n\n    /**\n     * 新增保存字典数据信息\n     *\n     * @param data 字典数据信息\n     * @return 结果\n     */\n    @CachePut(cacheNames = CacheNames.SYS_DICT, key = \"#data.dictType\")\n    @Override\n    public List<SysDictData> insertDictData(SysDictData data) {\n        int row = baseMapper.insert(data);\n        if (row > 0) {\n            return baseMapper.selectDictDataByType(data.getDictType());\n        }\n        throw new ServiceException(\"操作失败\");\n    }\n\n    /**\n     * 修改保存字典数据信息\n     *\n     * @param data 字典数据信息\n     * @return 结果\n     */\n    @CachePut(cacheNames = CacheNames.SYS_DICT, key = \"#data.dictType\")\n    @Override\n    public List<SysDictData> updateDictData(SysDictData data) {\n        int row = baseMapper.updateById(data);\n        if (row > 0) {\n            return baseMapper.selectDictDataByType(data.getDictType());\n        }\n        throw new ServiceException(\"操作失败\");\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/impl/SysDictTypeServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.dev33.satoken.context.SaHolder;\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport top.flya.common.constant.CacheConstants;\nimport top.flya.common.constant.CacheNames;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.entity.SysDictData;\nimport top.flya.common.core.domain.entity.SysDictType;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.service.DictService;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.utils.StreamUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.redis.CacheUtils;\nimport top.flya.common.utils.spring.SpringUtils;\nimport top.flya.system.mapper.SysDictDataMapper;\nimport top.flya.system.mapper.SysDictTypeMapper;\nimport top.flya.system.service.ISysDictTypeService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.cache.annotation.CachePut;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 字典 业务层处理\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Service\npublic class SysDictTypeServiceImpl implements ISysDictTypeService, DictService {\n\n    private final SysDictTypeMapper baseMapper;\n    private final SysDictDataMapper dictDataMapper;\n\n    @Override\n    public TableDataInfo<SysDictType> selectPageDictTypeList(SysDictType dictType, PageQuery pageQuery) {\n        Map<String, Object> params = dictType.getParams();\n        LambdaQueryWrapper<SysDictType> lqw = new LambdaQueryWrapper<SysDictType>()\n            .like(StringUtils.isNotBlank(dictType.getDictName()), SysDictType::getDictName, dictType.getDictName())\n            .eq(StringUtils.isNotBlank(dictType.getStatus()), SysDictType::getStatus, dictType.getStatus())\n            .like(StringUtils.isNotBlank(dictType.getDictType()), SysDictType::getDictType, dictType.getDictType())\n            .between(params.get(\"beginTime\") != null && params.get(\"endTime\") != null,\n                SysDictType::getCreateTime, params.get(\"beginTime\"), params.get(\"endTime\"));\n        Page<SysDictType> page = baseMapper.selectPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(page);\n    }\n\n    /**\n     * 根据条件分页查询字典类型\n     *\n     * @param dictType 字典类型信息\n     * @return 字典类型集合信息\n     */\n    @Override\n    public List<SysDictType> selectDictTypeList(SysDictType dictType) {\n        Map<String, Object> params = dictType.getParams();\n        return baseMapper.selectList(new LambdaQueryWrapper<SysDictType>()\n            .like(StringUtils.isNotBlank(dictType.getDictName()), SysDictType::getDictName, dictType.getDictName())\n            .eq(StringUtils.isNotBlank(dictType.getStatus()), SysDictType::getStatus, dictType.getStatus())\n            .like(StringUtils.isNotBlank(dictType.getDictType()), SysDictType::getDictType, dictType.getDictType())\n            .between(params.get(\"beginTime\") != null && params.get(\"endTime\") != null,\n                SysDictType::getCreateTime, params.get(\"beginTime\"), params.get(\"endTime\")));\n    }\n\n    /**\n     * 根据所有字典类型\n     *\n     * @return 字典类型集合信息\n     */\n    @Override\n    public List<SysDictType> selectDictTypeAll() {\n        return baseMapper.selectList();\n    }\n\n    /**\n     * 根据字典类型查询字典数据\n     *\n     * @param dictType 字典类型\n     * @return 字典数据集合信息\n     */\n    @Cacheable(cacheNames = CacheNames.SYS_DICT, key = \"#dictType\")\n    @Override\n    public List<SysDictData> selectDictDataByType(String dictType) {\n        List<SysDictData> dictDatas = dictDataMapper.selectDictDataByType(dictType);\n        if (CollUtil.isNotEmpty(dictDatas)) {\n            return dictDatas;\n        }\n        return null;\n    }\n\n    /**\n     * 根据字典类型ID查询信息\n     *\n     * @param dictId 字典类型ID\n     * @return 字典类型\n     */\n    @Override\n    public SysDictType selectDictTypeById(Long dictId) {\n        return baseMapper.selectById(dictId);\n    }\n\n    /**\n     * 根据字典类型查询信息\n     *\n     * @param dictType 字典类型\n     * @return 字典类型\n     */\n    @Cacheable(cacheNames = CacheNames.SYS_DICT, key = \"#dictType\")\n    @Override\n    public SysDictType selectDictTypeByType(String dictType) {\n        return baseMapper.selectById(new LambdaQueryWrapper<SysDictType>().eq(SysDictType::getDictType, dictType));\n    }\n\n    /**\n     * 批量删除字典类型信息\n     *\n     * @param dictIds 需要删除的字典ID\n     */\n    @Override\n    public void deleteDictTypeByIds(Long[] dictIds) {\n        for (Long dictId : dictIds) {\n            SysDictType dictType = selectDictTypeById(dictId);\n            if (dictDataMapper.exists(new LambdaQueryWrapper<SysDictData>()\n                .eq(SysDictData::getDictType, dictType.getDictType()))) {\n                throw new ServiceException(String.format(\"%1$s已分配,不能删除\", dictType.getDictName()));\n            }\n            CacheUtils.evict(CacheNames.SYS_DICT, dictType.getDictType());\n        }\n        baseMapper.deleteBatchIds(Arrays.asList(dictIds));\n    }\n\n    /**\n     * 加载字典缓存数据\n     */\n    @Override\n    public void loadingDictCache() {\n        List<SysDictData> dictDataList = dictDataMapper.selectList(\n            new LambdaQueryWrapper<SysDictData>().eq(SysDictData::getStatus, UserConstants.DICT_NORMAL));\n        Map<String, List<SysDictData>> dictDataMap = StreamUtils.groupByKey(dictDataList, SysDictData::getDictType);\n        dictDataMap.forEach((k,v) -> {\n            List<SysDictData> dictList = StreamUtils.sorted(v, Comparator.comparing(SysDictData::getDictSort));\n            CacheUtils.put(CacheNames.SYS_DICT, k, dictList);\n        });\n    }\n\n    /**\n     * 清空字典缓存数据\n     */\n    @Override\n    public void clearDictCache() {\n        CacheUtils.clear(CacheNames.SYS_DICT);\n    }\n\n    /**\n     * 重置字典缓存数据\n     */\n    @Override\n    public void resetDictCache() {\n        clearDictCache();\n        loadingDictCache();\n    }\n\n    /**\n     * 新增保存字典类型信息\n     *\n     * @param dict 字典类型信息\n     * @return 结果\n     */\n    @CachePut(cacheNames = CacheNames.SYS_DICT, key = \"#dict.dictType\")\n    @Override\n    public List<SysDictData> insertDictType(SysDictType dict) {\n        int row = baseMapper.insert(dict);\n        if (row > 0) {\n            return new ArrayList<>();\n        }\n        throw new ServiceException(\"操作失败\");\n    }\n\n    /**\n     * 修改保存字典类型信息\n     *\n     * @param dict 字典类型信息\n     * @return 结果\n     */\n    @CachePut(cacheNames = CacheNames.SYS_DICT, key = \"#dict.dictType\")\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public List<SysDictData> updateDictType(SysDictType dict) {\n        SysDictType oldDict = baseMapper.selectById(dict.getDictId());\n        dictDataMapper.update(null, new LambdaUpdateWrapper<SysDictData>()\n            .set(SysDictData::getDictType, dict.getDictType())\n            .eq(SysDictData::getDictType, oldDict.getDictType()));\n        int row = baseMapper.updateById(dict);\n        if (row > 0) {\n            CacheUtils.evict(CacheNames.SYS_DICT, oldDict.getDictType());\n            return dictDataMapper.selectDictDataByType(dict.getDictType());\n        }\n        throw new ServiceException(\"操作失败\");\n    }\n\n    /**\n     * 校验字典类型称是否唯一\n     *\n     * @param dict 字典类型\n     * @return 结果\n     */\n    @Override\n    public boolean checkDictTypeUnique(SysDictType dict) {\n        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysDictType>()\n            .eq(SysDictType::getDictType, dict.getDictType())\n            .ne(ObjectUtil.isNotNull(dict.getDictId()), SysDictType::getDictId, dict.getDictId()));\n        return !exist;\n    }\n\n    /**\n     * 根据字典类型和字典值获取字典标签\n     *\n     * @param dictType  字典类型\n     * @param dictValue 字典值\n     * @param separator 分隔符\n     * @return 字典标签\n     */\n    @SuppressWarnings(\"unchecked cast\")\n    @Override\n    public String getDictLabel(String dictType, String dictValue, String separator) {\n        // 优先从本地缓存获取\n        List<SysDictData> datas = (List<SysDictData>) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType);\n        if (ObjectUtil.isNull(datas)) {\n            datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);\n            SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas);\n        }\n\n        Map<String, String> map = StreamUtils.toMap(datas, SysDictData::getDictValue, SysDictData::getDictLabel);\n        if (StringUtils.containsAny(dictValue, separator)) {\n            return Arrays.stream(dictValue.split(separator))\n                .map(v -> map.getOrDefault(v, StringUtils.EMPTY))\n                .collect(Collectors.joining(separator));\n        } else {\n            return map.getOrDefault(dictValue, StringUtils.EMPTY);\n        }\n    }\n\n    /**\n     * 根据字典类型和字典标签获取字典值\n     *\n     * @param dictType  字典类型\n     * @param dictLabel 字典标签\n     * @param separator 分隔符\n     * @return 字典值\n     */\n    @SuppressWarnings(\"unchecked cast\")\n    @Override\n    public String getDictValue(String dictType, String dictLabel, String separator) {\n        // 优先从本地缓存获取\n        List<SysDictData> datas = (List<SysDictData>) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType);\n        if (ObjectUtil.isNull(datas)) {\n            datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);\n            SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas);\n        }\n\n        Map<String, String> map = StreamUtils.toMap(datas, SysDictData::getDictLabel, SysDictData::getDictValue);\n        if (StringUtils.containsAny(dictLabel, separator)) {\n            return Arrays.stream(dictLabel.split(separator))\n                .map(l -> map.getOrDefault(l, StringUtils.EMPTY))\n                .collect(Collectors.joining(separator));\n        } else {\n            return map.getOrDefault(dictLabel, StringUtils.EMPTY);\n        }\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/impl/SysLogininforServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.http.useragent.UserAgent;\nimport cn.hutool.http.useragent.UserAgentUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport top.flya.common.constant.Constants;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.event.LogininforEvent;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.utils.ServletUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.ip.AddressUtils;\nimport top.flya.system.domain.SysLogininfor;\nimport top.flya.system.mapper.SysLogininforMapper;\nimport top.flya.system.service.ISysLogininforService;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.stereotype.Service;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 系统访问日志情况信息 服务层处理\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Slf4j\n@Service\npublic class SysLogininforServiceImpl implements ISysLogininforService {\n\n    private final SysLogininforMapper baseMapper;\n\n    /**\n     * 记录登录信息\n     *\n     * @param logininforEvent 登录事件\n     */\n    @Async\n    @EventListener\n    public void recordLogininfor(LogininforEvent logininforEvent) {\n        HttpServletRequest request = logininforEvent.getRequest();\n        final UserAgent userAgent = UserAgentUtil.parse(request.getHeader(\"User-Agent\"));\n        final String ip = ServletUtils.getClientIP(request);\n\n        String address = AddressUtils.getRealAddressByIP(ip);\n        StringBuilder s = new StringBuilder();\n        s.append(getBlock(ip));\n        s.append(address);\n        s.append(getBlock(logininforEvent.getUsername()));\n        s.append(getBlock(logininforEvent.getStatus()));\n        s.append(getBlock(logininforEvent.getMessage()));\n        // 打印信息到日志\n        log.info(s.toString(), logininforEvent.getArgs());\n        // 获取客户端操作系统\n        String os = userAgent.getOs().getName();\n        // 获取客户端浏览器\n        String browser = userAgent.getBrowser().getName();\n        // 封装对象\n        SysLogininfor logininfor = new SysLogininfor();\n        logininfor.setUserName(logininforEvent.getUsername());\n        logininfor.setIpaddr(ip);\n        logininfor.setLoginLocation(address);\n        logininfor.setBrowser(browser);\n        logininfor.setOs(os);\n        logininfor.setMsg(logininforEvent.getMessage());\n        // 日志状态\n        if (StringUtils.equalsAny(logininforEvent.getStatus(), Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) {\n            logininfor.setStatus(Constants.SUCCESS);\n        } else if (Constants.LOGIN_FAIL.equals(logininforEvent.getStatus())) {\n            logininfor.setStatus(Constants.FAIL);\n        }\n        // 插入数据\n        insertLogininfor(logininfor);\n    }\n\n    private String getBlock(Object msg) {\n        if (msg == null) {\n            msg = \"\";\n        }\n        return \"[\" + msg.toString() + \"]\";\n    }\n\n    @Override\n    public TableDataInfo<SysLogininfor> selectPageLogininforList(SysLogininfor logininfor, PageQuery pageQuery) {\n        Map<String, Object> params = logininfor.getParams();\n        LambdaQueryWrapper<SysLogininfor> lqw = new LambdaQueryWrapper<SysLogininfor>()\n            .like(StringUtils.isNotBlank(logininfor.getIpaddr()), SysLogininfor::getIpaddr, logininfor.getIpaddr())\n            .eq(StringUtils.isNotBlank(logininfor.getStatus()), SysLogininfor::getStatus, logininfor.getStatus())\n            .like(StringUtils.isNotBlank(logininfor.getUserName()), SysLogininfor::getUserName, logininfor.getUserName())\n            .between(params.get(\"beginTime\") != null && params.get(\"endTime\") != null,\n                SysLogininfor::getLoginTime, params.get(\"beginTime\"), params.get(\"endTime\"));\n        if (StringUtils.isBlank(pageQuery.getOrderByColumn())) {\n            pageQuery.setOrderByColumn(\"info_id\");\n            pageQuery.setIsAsc(\"desc\");\n        }\n        Page<SysLogininfor> page = baseMapper.selectPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(page);\n    }\n\n    /**\n     * 新增系统登录日志\n     *\n     * @param logininfor 访问日志对象\n     */\n    @Override\n    public void insertLogininfor(SysLogininfor logininfor) {\n        logininfor.setLoginTime(new Date());\n        baseMapper.insert(logininfor);\n    }\n\n    /**\n     * 查询系统登录日志集合\n     *\n     * @param logininfor 访问日志对象\n     * @return 登录记录集合\n     */\n    @Override\n    public List<SysLogininfor> selectLogininforList(SysLogininfor logininfor) {\n        Map<String, Object> params = logininfor.getParams();\n        return baseMapper.selectList(new LambdaQueryWrapper<SysLogininfor>()\n            .like(StringUtils.isNotBlank(logininfor.getIpaddr()), SysLogininfor::getIpaddr, logininfor.getIpaddr())\n            .eq(StringUtils.isNotBlank(logininfor.getStatus()), SysLogininfor::getStatus, logininfor.getStatus())\n            .like(StringUtils.isNotBlank(logininfor.getUserName()), SysLogininfor::getUserName, logininfor.getUserName())\n            .between(params.get(\"beginTime\") != null && params.get(\"endTime\") != null,\n                SysLogininfor::getLoginTime, params.get(\"beginTime\"), params.get(\"endTime\"))\n            .orderByDesc(SysLogininfor::getInfoId));\n    }\n\n    /**\n     * 批量删除系统登录日志\n     *\n     * @param infoIds 需要删除的登录日志ID\n     * @return 结果\n     */\n    @Override\n    public int deleteLogininforByIds(Long[] infoIds) {\n        return baseMapper.deleteBatchIds(Arrays.asList(infoIds));\n    }\n\n    /**\n     * 清空系统登录日志\n     */\n    @Override\n    public void cleanLogininfor() {\n        baseMapper.delete(new LambdaQueryWrapper<>());\n    }\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/impl/SysMenuServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport top.flya.common.constant.Constants;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.domain.entity.SysMenu;\nimport top.flya.common.core.domain.entity.SysRole;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.StreamUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.TreeBuildUtils;\nimport top.flya.system.domain.SysRoleMenu;\nimport top.flya.system.domain.vo.MetaVo;\nimport top.flya.system.domain.vo.RouterVo;\nimport top.flya.system.mapper.SysMenuMapper;\nimport top.flya.system.mapper.SysRoleMapper;\nimport top.flya.system.mapper.SysRoleMenuMapper;\nimport top.flya.system.service.ISysMenuService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\n\nimport java.util.*;\n\n/**\n * 菜单 业务层处理\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Service\npublic class SysMenuServiceImpl implements ISysMenuService {\n\n    private final SysMenuMapper baseMapper;\n    private final SysRoleMapper roleMapper;\n    private final SysRoleMenuMapper roleMenuMapper;\n\n    /**\n     * 根据用户查询系统菜单列表\n     *\n     * @param userId 用户ID\n     * @return 菜单列表\n     */\n    @Override\n    public List<SysMenu> selectMenuList(Long userId) {\n        return selectMenuList(new SysMenu(), userId);\n    }\n\n    /**\n     * 查询系统菜单列表\n     *\n     * @param menu 菜单信息\n     * @return 菜单列表\n     */\n    @Override\n    public List<SysMenu> selectMenuList(SysMenu menu, Long userId) {\n        List<SysMenu> menuList = null;\n        // 管理员显示所有菜单信息\n        if (LoginHelper.isAdmin(userId)) {\n            menuList = baseMapper.selectList(new LambdaQueryWrapper<SysMenu>()\n                .like(StringUtils.isNotBlank(menu.getMenuName()), SysMenu::getMenuName, menu.getMenuName())\n                .eq(StringUtils.isNotBlank(menu.getVisible()), SysMenu::getVisible, menu.getVisible())\n                .eq(StringUtils.isNotBlank(menu.getStatus()), SysMenu::getStatus, menu.getStatus())\n                .orderByAsc(SysMenu::getParentId)\n                .orderByAsc(SysMenu::getOrderNum));\n        } else {\n            QueryWrapper<SysMenu> wrapper = Wrappers.query();\n            wrapper.eq(\"sur.user_id\", userId)\n                .like(StringUtils.isNotBlank(menu.getMenuName()), \"m.menu_name\", menu.getMenuName())\n                .eq(StringUtils.isNotBlank(menu.getVisible()), \"m.visible\", menu.getVisible())\n                .eq(StringUtils.isNotBlank(menu.getStatus()), \"m.status\", menu.getStatus())\n                .orderByAsc(\"m.parent_id\")\n                .orderByAsc(\"m.order_num\");\n            menuList = baseMapper.selectMenuListByUserId(wrapper);\n        }\n        return menuList;\n    }\n\n    /**\n     * 根据用户ID查询权限\n     *\n     * @param userId 用户ID\n     * @return 权限列表\n     */\n    @Override\n    public Set<String> selectMenuPermsByUserId(Long userId) {\n        List<String> perms = baseMapper.selectMenuPermsByUserId(userId);\n        Set<String> permsSet = new HashSet<>();\n        for (String perm : perms) {\n            if (StringUtils.isNotEmpty(perm)) {\n                permsSet.addAll(StringUtils.splitList(perm.trim()));\n            }\n        }\n        return permsSet;\n    }\n\n    /**\n     * 根据角色ID查询权限\n     *\n     * @param roleId 角色ID\n     * @return 权限列表\n     */\n    @Override\n    public Set<String> selectMenuPermsByRoleId(Long roleId) {\n        List<String> perms = baseMapper.selectMenuPermsByRoleId(roleId);\n        Set<String> permsSet = new HashSet<>();\n        for (String perm : perms) {\n            if (StringUtils.isNotEmpty(perm)) {\n                permsSet.addAll(StringUtils.splitList(perm.trim()));\n            }\n        }\n        return permsSet;\n    }\n\n    /**\n     * 根据用户ID查询菜单\n     *\n     * @param userId 用户名称\n     * @return 菜单列表\n     */\n    @Override\n    public List<SysMenu> selectMenuTreeByUserId(Long userId) {\n        List<SysMenu> menus = null;\n        if (LoginHelper.isAdmin(userId)) {\n            menus = baseMapper.selectMenuTreeAll();\n        } else {\n            menus = baseMapper.selectMenuTreeByUserId(userId);\n        }\n        return getChildPerms(menus, 0);\n    }\n\n    /**\n     * 根据角色ID查询菜单树信息\n     *\n     * @param roleId 角色ID\n     * @return 选中菜单列表\n     */\n    @Override\n    public List<Long> selectMenuListByRoleId(Long roleId) {\n        SysRole role = roleMapper.selectById(roleId);\n        return baseMapper.selectMenuListByRoleId(roleId, role.getMenuCheckStrictly());\n    }\n\n    /**\n     * 构建前端路由所需要的菜单\n     *\n     * @param menus 菜单列表\n     * @return 路由列表\n     */\n    @Override\n    public List<RouterVo> buildMenus(List<SysMenu> menus) {\n        List<RouterVo> routers = new LinkedList<>();\n        for (SysMenu menu : menus) {\n            RouterVo router = new RouterVo();\n            router.setHidden(\"1\".equals(menu.getVisible()));\n            router.setName(getRouteName(menu));\n            router.setPath(getRouterPath(menu));\n            router.setComponent(getComponent(menu));\n            router.setQuery(menu.getQueryParam());\n            router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals(\"1\", menu.getIsCache()), menu.getPath()));\n            List<SysMenu> cMenus = menu.getChildren();\n            if (CollUtil.isNotEmpty(cMenus) && UserConstants.TYPE_DIR.equals(menu.getMenuType())) {\n                router.setAlwaysShow(true);\n                router.setRedirect(\"noRedirect\");\n                router.setChildren(buildMenus(cMenus));\n            } else if (isMenuFrame(menu)) {\n                router.setMeta(null);\n                List<RouterVo> childrenList = new ArrayList<>();\n                RouterVo children = new RouterVo();\n                children.setPath(menu.getPath());\n                children.setComponent(menu.getComponent());\n                children.setName(StringUtils.capitalize(menu.getPath()));\n                children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals(\"1\", menu.getIsCache()), menu.getPath()));\n                children.setQuery(menu.getQueryParam());\n                childrenList.add(children);\n                router.setChildren(childrenList);\n            } else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) {\n                router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon()));\n                router.setPath(\"/\");\n                List<RouterVo> childrenList = new ArrayList<>();\n                RouterVo children = new RouterVo();\n                String routerPath = innerLinkReplaceEach(menu.getPath());\n                children.setPath(routerPath);\n                children.setComponent(UserConstants.INNER_LINK);\n                children.setName(StringUtils.capitalize(routerPath));\n                children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath()));\n                childrenList.add(children);\n                router.setChildren(childrenList);\n            }\n            routers.add(router);\n        }\n        return routers;\n    }\n\n    /**\n     * 构建前端所需要下拉树结构\n     *\n     * @param menus 菜单列表\n     * @return 下拉树结构列表\n     */\n    @Override\n    public List<Tree<Long>> buildMenuTreeSelect(List<SysMenu> menus) {\n        if (CollUtil.isEmpty(menus)) {\n            return CollUtil.newArrayList();\n        }\n        return TreeBuildUtils.build(menus, (menu, tree) ->\n            tree.setId(menu.getMenuId())\n                .setParentId(menu.getParentId())\n                .setName(menu.getMenuName())\n                .setWeight(menu.getOrderNum()));\n    }\n\n    /**\n     * 根据菜单ID查询信息\n     *\n     * @param menuId 菜单ID\n     * @return 菜单信息\n     */\n    @Override\n    public SysMenu selectMenuById(Long menuId) {\n        return baseMapper.selectById(menuId);\n    }\n\n    /**\n     * 是否存在菜单子节点\n     *\n     * @param menuId 菜单ID\n     * @return 结果\n     */\n    @Override\n    public boolean hasChildByMenuId(Long menuId) {\n        return baseMapper.exists(new LambdaQueryWrapper<SysMenu>().eq(SysMenu::getParentId, menuId));\n    }\n\n    /**\n     * 查询菜单使用数量\n     *\n     * @param menuId 菜单ID\n     * @return 结果\n     */\n    @Override\n    public boolean checkMenuExistRole(Long menuId) {\n        return roleMenuMapper.exists(new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getMenuId, menuId));\n    }\n\n    /**\n     * 新增保存菜单信息\n     *\n     * @param menu 菜单信息\n     * @return 结果\n     */\n    @Override\n    public int insertMenu(SysMenu menu) {\n        return baseMapper.insert(menu);\n    }\n\n    /**\n     * 修改保存菜单信息\n     *\n     * @param menu 菜单信息\n     * @return 结果\n     */\n    @Override\n    public int updateMenu(SysMenu menu) {\n        return baseMapper.updateById(menu);\n    }\n\n    /**\n     * 删除菜单管理信息\n     *\n     * @param menuId 菜单ID\n     * @return 结果\n     */\n    @Override\n    public int deleteMenuById(Long menuId) {\n        return baseMapper.deleteById(menuId);\n    }\n\n    /**\n     * 校验菜单名称是否唯一\n     *\n     * @param menu 菜单信息\n     * @return 结果\n     */\n    @Override\n    public boolean checkMenuNameUnique(SysMenu menu) {\n        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysMenu>()\n            .eq(SysMenu::getMenuName, menu.getMenuName())\n            .eq(SysMenu::getParentId, menu.getParentId())\n            .ne(ObjectUtil.isNotNull(menu.getMenuId()), SysMenu::getMenuId, menu.getMenuId()));\n        return !exist;\n    }\n\n    /**\n     * 获取路由名称\n     *\n     * @param menu 菜单信息\n     * @return 路由名称\n     */\n    public String getRouteName(SysMenu menu) {\n        String routerName = StringUtils.capitalize(menu.getPath());\n        // 非外链并且是一级目录（类型为目录）\n        if (isMenuFrame(menu)) {\n            routerName = StringUtils.EMPTY;\n        }\n        return routerName;\n    }\n\n    /**\n     * 获取路由地址\n     *\n     * @param menu 菜单信息\n     * @return 路由地址\n     */\n    public String getRouterPath(SysMenu menu) {\n        String routerPath = menu.getPath();\n        // 内链打开外网方式\n        if (menu.getParentId().intValue() != 0 && isInnerLink(menu)) {\n            routerPath = innerLinkReplaceEach(routerPath);\n        }\n        // 非外链并且是一级目录（类型为目录）\n        if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType())\n            && UserConstants.NO_FRAME.equals(menu.getIsFrame())) {\n            routerPath = \"/\" + menu.getPath();\n        }\n        // 非外链并且是一级目录（类型为菜单）\n        else if (isMenuFrame(menu)) {\n            routerPath = \"/\";\n        }\n        return routerPath;\n    }\n\n    /**\n     * 获取组件信息\n     *\n     * @param menu 菜单信息\n     * @return 组件信息\n     */\n    public String getComponent(SysMenu menu) {\n        String component = UserConstants.LAYOUT;\n        if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) {\n            component = menu.getComponent();\n        } else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu)) {\n            component = UserConstants.INNER_LINK;\n        } else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu)) {\n            component = UserConstants.PARENT_VIEW;\n        }\n        return component;\n    }\n\n    /**\n     * 是否为菜单内部跳转\n     *\n     * @param menu 菜单信息\n     * @return 结果\n     */\n    public boolean isMenuFrame(SysMenu menu) {\n        return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType())\n            && menu.getIsFrame().equals(UserConstants.NO_FRAME);\n    }\n\n    /**\n     * 是否为内链组件\n     *\n     * @param menu 菜单信息\n     * @return 结果\n     */\n    public boolean isInnerLink(SysMenu menu) {\n        return menu.getIsFrame().equals(UserConstants.NO_FRAME) && StringUtils.ishttp(menu.getPath());\n    }\n\n    /**\n     * 是否为parent_view组件\n     *\n     * @param menu 菜单信息\n     * @return 结果\n     */\n    public boolean isParentView(SysMenu menu) {\n        return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType());\n    }\n\n    /**\n     * 根据父节点的ID获取所有子节点\n     *\n     * @param list     分类表\n     * @param parentId 传入的父节点ID\n     * @return String\n     */\n    public List<SysMenu> getChildPerms(List<SysMenu> list, int parentId) {\n        List<SysMenu> returnList = new ArrayList<>();\n        for (SysMenu t : list) {\n            // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点\n            if (t.getParentId() == parentId) {\n                recursionFn(list, t);\n                returnList.add(t);\n            }\n        }\n        return returnList;\n    }\n\n    /**\n     * 递归列表\n     *\n     * @param list\n     * @param t\n     */\n    private void recursionFn(List<SysMenu> list, SysMenu t) {\n        // 得到子节点列表\n        List<SysMenu> childList = getChildList(list, t);\n        t.setChildren(childList);\n        for (SysMenu tChild : childList) {\n            if (hasChild(list, tChild)) {\n                recursionFn(list, tChild);\n            }\n        }\n    }\n\n    /**\n     * 得到子节点列表\n     */\n    private List<SysMenu> getChildList(List<SysMenu> list, SysMenu t) {\n        return StreamUtils.filter(list, n -> n.getParentId().equals(t.getMenuId()));\n    }\n\n    /**\n     * 判断是否有子节点\n     */\n    private boolean hasChild(List<SysMenu> list, SysMenu t) {\n        return CollUtil.isNotEmpty(getChildList(list, t));\n    }\n\n    /**\n     * 内链域名特殊字符替换\n     */\n    public String innerLinkReplaceEach(String path) {\n        return StringUtils.replaceEach(path, new String[]{Constants.HTTP, Constants.HTTPS, Constants.WWW, \".\"},\n            new String[]{\"\", \"\", \"\", \"/\"});\n    }\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/impl/SysNoticeServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.system.domain.SysNotice;\nimport top.flya.system.mapper.SysNoticeMapper;\nimport top.flya.system.service.ISysNoticeService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 公告 服务层实现\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Service\npublic class SysNoticeServiceImpl implements ISysNoticeService {\n\n    private final SysNoticeMapper baseMapper;\n\n    @Override\n    public TableDataInfo<SysNotice> selectPageNoticeList(SysNotice notice, PageQuery pageQuery) {\n        LambdaQueryWrapper<SysNotice> lqw = new LambdaQueryWrapper<SysNotice>()\n            .like(StringUtils.isNotBlank(notice.getNoticeTitle()), SysNotice::getNoticeTitle, notice.getNoticeTitle())\n            .eq(StringUtils.isNotBlank(notice.getNoticeType()), SysNotice::getNoticeType, notice.getNoticeType())\n//            .like(StringUtils.isNotBlank(notice.getCreateBy()), SysNotice::getCreateBy, notice.getCreateBy())\n            ;\n        Page<SysNotice> page = baseMapper.selectPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(page);\n    }\n\n    /**\n     * 查询公告信息\n     *\n     * @param noticeId 公告ID\n     * @return 公告信息\n     */\n    @Override\n    public SysNotice selectNoticeById(Long noticeId) {\n        return baseMapper.selectById(noticeId);\n    }\n\n    /**\n     * 查询公告列表\n     *\n     * @param notice 公告信息\n     * @return 公告集合\n     */\n    @Override\n    public List<SysNotice> selectNoticeList(SysNotice notice) {\n        return baseMapper.selectList(new LambdaQueryWrapper<SysNotice>()\n            .like(StringUtils.isNotBlank(notice.getNoticeTitle()), SysNotice::getNoticeTitle, notice.getNoticeTitle())\n            .eq(StringUtils.isNotBlank(notice.getNoticeType()), SysNotice::getNoticeType, notice.getNoticeType()))\n//            .like(StringUtils.isNotBlank(notice.getCreateBy()), SysNotice::getCreateBy, notice.getCreateBy()))\n            ;\n    }\n\n    /**\n     * 新增公告\n     *\n     * @param notice 公告信息\n     * @return 结果\n     */\n    @Override\n    public int insertNotice(SysNotice notice) {\n        return baseMapper.insert(notice);\n    }\n\n    /**\n     * 修改公告\n     *\n     * @param notice 公告信息\n     * @return 结果\n     */\n    @Override\n    public int updateNotice(SysNotice notice) {\n        return baseMapper.updateById(notice);\n    }\n\n    /**\n     * 删除公告对象\n     *\n     * @param noticeId 公告ID\n     * @return 结果\n     */\n    @Override\n    public int deleteNoticeById(Long noticeId) {\n        return baseMapper.deleteById(noticeId);\n    }\n\n    /**\n     * 批量删除公告信息\n     *\n     * @param noticeIds 需要删除的公告ID\n     * @return 结果\n     */\n    @Override\n    public int deleteNoticeByIds(Long[] noticeIds) {\n        return baseMapper.deleteBatchIds(Arrays.asList(noticeIds));\n    }\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/impl/SysOperLogServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.event.OperLogEvent;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.ip.AddressUtils;\nimport top.flya.system.domain.SysOperLog;\nimport top.flya.system.mapper.SysOperLogMapper;\nimport top.flya.system.service.ISysOperLogService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 操作日志 服务层处理\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Service\npublic class SysOperLogServiceImpl implements ISysOperLogService {\n\n    private final SysOperLogMapper baseMapper;\n\n    /**\n     * 操作日志记录\n     *\n     * @param operLogEvent 操作日志事件\n     */\n    @Async\n    @EventListener\n    public void recordOper(OperLogEvent operLogEvent) {\n        SysOperLog operLog = BeanUtil.toBean(operLogEvent, SysOperLog.class);\n        // 远程查询操作地点\n        operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));\n        insertOperlog(operLog);\n    }\n\n    @Override\n    public TableDataInfo<SysOperLog> selectPageOperLogList(SysOperLog operLog, PageQuery pageQuery) {\n        Map<String, Object> params = operLog.getParams();\n        LambdaQueryWrapper<SysOperLog> lqw = new LambdaQueryWrapper<SysOperLog>()\n            .like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle())\n            .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0,\n                SysOperLog::getBusinessType, operLog.getBusinessType())\n            .func(f -> {\n                if (ArrayUtil.isNotEmpty(operLog.getBusinessTypes())) {\n                    f.in(SysOperLog::getBusinessType, Arrays.asList(operLog.getBusinessTypes()));\n                }\n            })\n            .eq(operLog.getStatus() != null,\n                SysOperLog::getStatus, operLog.getStatus())\n            .like(StringUtils.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName())\n            .between(params.get(\"beginTime\") != null && params.get(\"endTime\") != null,\n                SysOperLog::getOperTime, params.get(\"beginTime\"), params.get(\"endTime\"));\n        if (StringUtils.isBlank(pageQuery.getOrderByColumn())) {\n            pageQuery.setOrderByColumn(\"oper_id\");\n            pageQuery.setIsAsc(\"desc\");\n        }\n        Page<SysOperLog> page = baseMapper.selectPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(page);\n    }\n\n    /**\n     * 新增操作日志\n     *\n     * @param operLog 操作日志对象\n     */\n    @Override\n    public void insertOperlog(SysOperLog operLog) {\n        operLog.setOperTime(new Date());\n        baseMapper.insert(operLog);\n    }\n\n    /**\n     * 查询系统操作日志集合\n     *\n     * @param operLog 操作日志对象\n     * @return 操作日志集合\n     */\n    @Override\n    public List<SysOperLog> selectOperLogList(SysOperLog operLog) {\n        Map<String, Object> params = operLog.getParams();\n        return baseMapper.selectList(new LambdaQueryWrapper<SysOperLog>()\n            .like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle())\n            .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0,\n                SysOperLog::getBusinessType, operLog.getBusinessType())\n            .func(f -> {\n                if (ArrayUtil.isNotEmpty(operLog.getBusinessTypes())) {\n                    f.in(SysOperLog::getBusinessType, Arrays.asList(operLog.getBusinessTypes()));\n                }\n            })\n            .eq(operLog.getStatus() != null && operLog.getStatus() > 0,\n                SysOperLog::getStatus, operLog.getStatus())\n            .like(StringUtils.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName())\n            .between(params.get(\"beginTime\") != null && params.get(\"endTime\") != null,\n                SysOperLog::getOperTime, params.get(\"beginTime\"), params.get(\"endTime\"))\n            .orderByDesc(SysOperLog::getOperId));\n    }\n\n    /**\n     * 批量删除系统操作日志\n     *\n     * @param operIds 需要删除的操作日志ID\n     * @return 结果\n     */\n    @Override\n    public int deleteOperLogByIds(Long[] operIds) {\n        return baseMapper.deleteBatchIds(Arrays.asList(operIds));\n    }\n\n    /**\n     * 查询操作日志详细\n     *\n     * @param operId 操作ID\n     * @return 操作日志对象\n     */\n    @Override\n    public SysOperLog selectOperLogById(Long operId) {\n        return baseMapper.selectById(operId);\n    }\n\n    /**\n     * 清空操作日志\n     */\n    @Override\n    public void cleanOperLog() {\n        baseMapper.delete(new LambdaQueryWrapper<>());\n    }\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/impl/SysOssConfigServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport top.flya.common.constant.CacheNames;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.utils.JsonUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.redis.CacheUtils;\nimport top.flya.common.utils.redis.RedisUtils;\nimport top.flya.oss.constant.OssConstant;\nimport top.flya.system.domain.SysOssConfig;\nimport top.flya.system.domain.bo.SysOssConfigBo;\nimport top.flya.system.domain.vo.SysOssConfigVo;\nimport top.flya.system.mapper.SysOssConfigMapper;\nimport top.flya.system.service.ISysOssConfigService;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 对象存储配置Service业务层处理\n *\n * @author Lion Li\n * @author 孤舟烟雨\n * @date 2021-08-13\n */\n@Slf4j\n@RequiredArgsConstructor\n@Service\npublic class SysOssConfigServiceImpl implements ISysOssConfigService {\n\n    private final SysOssConfigMapper baseMapper;\n\n    /**\n     * 项目启动时，初始化参数到缓存，加载配置类\n     */\n    @Override\n    public void init() {\n        List<SysOssConfig> list = baseMapper.selectList();\n        // 加载OSS初始化配置\n        for (SysOssConfig config : list) {\n            String configKey = config.getConfigKey();\n            if (\"0\".equals(config.getStatus())) {\n                RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, configKey);\n            }\n            CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config));\n        }\n    }\n\n    @Override\n    public SysOssConfigVo queryById(Long ossConfigId) {\n        return baseMapper.selectVoById(ossConfigId);\n    }\n\n    @Override\n    public TableDataInfo<SysOssConfigVo> queryPageList(SysOssConfigBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<SysOssConfig> lqw = buildQueryWrapper(bo);\n        Page<SysOssConfigVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(result);\n    }\n\n\n    private LambdaQueryWrapper<SysOssConfig> buildQueryWrapper(SysOssConfigBo bo) {\n        LambdaQueryWrapper<SysOssConfig> lqw = Wrappers.lambdaQuery();\n        lqw.eq(StringUtils.isNotBlank(bo.getConfigKey()), SysOssConfig::getConfigKey, bo.getConfigKey());\n        lqw.like(StringUtils.isNotBlank(bo.getBucketName()), SysOssConfig::getBucketName, bo.getBucketName());\n        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysOssConfig::getStatus, bo.getStatus());\n        return lqw;\n    }\n\n    @Override\n    public Boolean insertByBo(SysOssConfigBo bo) {\n        SysOssConfig config = BeanUtil.toBean(bo, SysOssConfig.class);\n        validEntityBeforeSave(config);\n        boolean flag = baseMapper.insert(config) > 0;\n        if (flag) {\n            CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config));\n        }\n        return flag;\n    }\n\n    @Override\n    public Boolean updateByBo(SysOssConfigBo bo) {\n        SysOssConfig config = BeanUtil.toBean(bo, SysOssConfig.class);\n        validEntityBeforeSave(config);\n        LambdaUpdateWrapper<SysOssConfig> luw = new LambdaUpdateWrapper<>();\n        luw.set(ObjectUtil.isNull(config.getPrefix()), SysOssConfig::getPrefix, \"\");\n        luw.set(ObjectUtil.isNull(config.getRegion()), SysOssConfig::getRegion, \"\");\n        luw.set(ObjectUtil.isNull(config.getExt1()), SysOssConfig::getExt1, \"\");\n        luw.set(ObjectUtil.isNull(config.getRemark()), SysOssConfig::getRemark, \"\");\n        luw.eq(SysOssConfig::getOssConfigId, config.getOssConfigId());\n        boolean flag = baseMapper.update(config, luw) > 0;\n        if (flag) {\n            CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config));\n        }\n        return flag;\n    }\n\n    /**\n     * 保存前的数据校验\n     */\n    private void validEntityBeforeSave(SysOssConfig entity) {\n        if (StringUtils.isNotEmpty(entity.getConfigKey()) && !checkConfigKeyUnique(entity)) {\n            throw new ServiceException(\"操作配置'\" + entity.getConfigKey() + \"'失败, 配置key已存在!\");\n        }\n    }\n\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if (isValid) {\n            if (CollUtil.containsAny(ids, OssConstant.SYSTEM_DATA_IDS)) {\n                throw new ServiceException(\"系统内置, 不可删除!\");\n            }\n        }\n        List<SysOssConfig> list = CollUtil.newArrayList();\n        for (Long configId : ids) {\n            SysOssConfig config = baseMapper.selectById(configId);\n            list.add(config);\n        }\n        boolean flag = baseMapper.deleteBatchIds(ids) > 0;\n        if (flag) {\n            list.forEach(sysOssConfig ->\n                CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, sysOssConfig.getConfigKey()));\n        }\n        return flag;\n    }\n\n    /**\n     * 判断configKey是否唯一\n     */\n    private boolean checkConfigKeyUnique(SysOssConfig sysOssConfig) {\n        long ossConfigId = ObjectUtil.isNull(sysOssConfig.getOssConfigId()) ? -1L : sysOssConfig.getOssConfigId();\n        SysOssConfig info = baseMapper.selectOne(new LambdaQueryWrapper<SysOssConfig>()\n            .select(SysOssConfig::getOssConfigId, SysOssConfig::getConfigKey)\n            .eq(SysOssConfig::getConfigKey, sysOssConfig.getConfigKey()));\n        if (ObjectUtil.isNotNull(info) && info.getOssConfigId() != ossConfigId) {\n            return false;\n        }\n        return true;\n    }\n\n    /**\n     * 启用禁用状态\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public int updateOssConfigStatus(SysOssConfigBo bo) {\n        SysOssConfig sysOssConfig = BeanUtil.toBean(bo, SysOssConfig.class);\n        int row = baseMapper.update(null, new LambdaUpdateWrapper<SysOssConfig>()\n            .set(SysOssConfig::getStatus, \"1\"));\n        row += baseMapper.updateById(sysOssConfig);\n        if (row > 0) {\n            RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, sysOssConfig.getConfigKey());\n        }\n        return row;\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/impl/SysOssServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.io.IoUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.apache.poi.ss.formula.functions.T;\nimport top.flya.common.constant.CacheNames;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.service.OssService;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.utils.BeanCopyUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.common.utils.file.FileUtils;\nimport top.flya.common.utils.spring.SpringUtils;\nimport top.flya.oss.core.OssClient;\nimport top.flya.oss.entity.UploadResult;\nimport top.flya.oss.enumd.AccessPolicyType;\nimport top.flya.oss.factory.OssFactory;\nimport top.flya.system.domain.SysOss;\nimport top.flya.system.domain.bo.SysOssBo;\nimport top.flya.system.domain.vo.SysOssVo;\nimport top.flya.system.mapper.SysOssMapper;\nimport top.flya.system.service.ISysOssService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.http.MediaType;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 文件上传 服务层实现\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Service\npublic class SysOssServiceImpl implements ISysOssService, OssService {\n\n    private final SysOssMapper baseMapper;\n\n    @Override\n    public TableDataInfo<SysOssVo> queryPageList(SysOssBo bo, PageQuery pageQuery) {\n        LambdaQueryWrapper<SysOss> lqw = buildQueryWrapper(bo);\n        Page<SysOssVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);\n        List<SysOssVo> filterResult = result.getRecords().stream().map(this::matchingUrl).collect(Collectors.toList());\n        result.setRecords(filterResult);\n        return TableDataInfo.build(result);\n    }\n\n    @Override\n    public List<SysOssVo> listByIds(Collection<Long> ossIds) {\n        List<SysOssVo> list = new ArrayList<>();\n        for (Long id : ossIds) {\n            SysOssVo vo = SpringUtils.getAopProxy(this).getById(id);\n            if (ObjectUtil.isNotNull(vo)) {\n                list.add(this.matchingUrl(vo));\n            }\n        }\n        return list;\n    }\n\n    @Override\n    public String selectUrlByIds(String ossIds) {\n        List<String> list = new ArrayList<>();\n        for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) {\n            SysOssVo vo = SpringUtils.getAopProxy(this).getById(id);\n            if (ObjectUtil.isNotNull(vo)) {\n                list.add(this.matchingUrl(vo).getUrl());\n            }\n        }\n        return String.join(StringUtils.SEPARATOR, list);\n    }\n\n    private LambdaQueryWrapper<SysOss> buildQueryWrapper(SysOssBo bo) {\n        Map<String, Object> params = bo.getParams();\n        LambdaQueryWrapper<SysOss> lqw = Wrappers.lambdaQuery();\n        lqw.like(StringUtils.isNotBlank(bo.getFileName()), SysOss::getFileName, bo.getFileName());\n        lqw.like(StringUtils.isNotBlank(bo.getOriginalName()), SysOss::getOriginalName, bo.getOriginalName());\n        lqw.eq(StringUtils.isNotBlank(bo.getFileSuffix()), SysOss::getFileSuffix, bo.getFileSuffix());\n        lqw.eq(StringUtils.isNotBlank(bo.getUrl()), SysOss::getUrl, bo.getUrl());\n        lqw.between(params.get(\"beginCreateTime\") != null && params.get(\"endCreateTime\") != null,\n            SysOss::getCreateTime, params.get(\"beginCreateTime\"), params.get(\"endCreateTime\"));\n//        lqw.eq(StringUtils.isNotBlank(bo.getCreateBy()), SysOss::getCreateBy, bo.getCreateBy());\n        lqw.eq(StringUtils.isNotBlank(bo.getService()), SysOss::getService, bo.getService());\n        return lqw;\n    }\n\n    @Cacheable(cacheNames = CacheNames.SYS_OSS, key = \"#ossId\")\n    @Override\n    public SysOssVo getById(Long ossId) {\n        return baseMapper.selectVoById(ossId);\n    }\n\n    @Override\n    public void download(Long ossId, HttpServletResponse response) throws IOException {\n        SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId);\n        if (ObjectUtil.isNull(sysOss)) {\n            throw new ServiceException(\"文件数据不存在!\");\n        }\n        FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName());\n        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + \"; charset=UTF-8\");\n        OssClient storage = OssFactory.instance();\n        try(InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) {\n            int available = inputStream.available();\n            IoUtil.copy(inputStream, response.getOutputStream(), available);\n            response.setContentLength(available);\n        } catch (Exception e) {\n            throw new ServiceException(e.getMessage());\n        }\n    }\n\n    @Override\n    public SysOssVo upload(MultipartFile file) {\n        String originalfileName = file.getOriginalFilename();\n        String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf(\".\"), originalfileName.length());\n        OssClient storage = OssFactory.instance();\n        UploadResult uploadResult;\n        try {\n            uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());\n        } catch (IOException e) {\n            throw new ServiceException(e.getMessage());\n        }\n        // 保存文件信息\n        SysOss oss = new SysOss();\n        oss.setUrl(uploadResult.getUrl());\n        oss.setFileSuffix(suffix);\n        oss.setFileName(uploadResult.getFilename());\n        oss.setOriginalName(originalfileName);\n        oss.setService(storage.getConfigKey());\n        baseMapper.insert(oss);\n        SysOssVo sysOssVo = new SysOssVo();\n        BeanCopyUtils.copy(oss, sysOssVo);\n        return this.matchingUrl(sysOssVo);\n    }\n\n    @Override\n    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {\n        if (isValid) {\n            // 做一些业务上的校验,判断是否需要校验\n        }\n        List<SysOss> list = baseMapper.selectBatchIds(ids);\n        for (SysOss sysOss : list) {\n            OssClient storage = OssFactory.instance(sysOss.getService());\n            storage.delete(sysOss.getUrl());\n        }\n        return baseMapper.deleteBatchIds(ids) > 0;\n    }\n\n    /**\n     * 匹配Url\n     *\n     * @param oss OSS对象\n     * @return oss 匹配Url的OSS对象\n     */\n    private SysOssVo matchingUrl(SysOssVo oss) {\n        OssClient storage = OssFactory.instance(oss.getService());\n        // 仅修改桶类型为 private 的URL，临时URL时长为120s\n        if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {\n            oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120));\n        }\n        return oss;\n    }\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/impl/SysPostServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.system.domain.SysPost;\nimport top.flya.system.domain.SysUserPost;\nimport top.flya.system.mapper.SysPostMapper;\nimport top.flya.system.mapper.SysUserPostMapper;\nimport top.flya.system.service.ISysPostService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 岗位信息 服务层处理\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Service\npublic class SysPostServiceImpl implements ISysPostService {\n\n    private final SysPostMapper baseMapper;\n    private final SysUserPostMapper userPostMapper;\n\n    @Override\n    public TableDataInfo<SysPost> selectPagePostList(SysPost post, PageQuery pageQuery) {\n        LambdaQueryWrapper<SysPost> lqw = new LambdaQueryWrapper<SysPost>()\n            .like(StringUtils.isNotBlank(post.getPostCode()), SysPost::getPostCode, post.getPostCode())\n            .eq(StringUtils.isNotBlank(post.getStatus()), SysPost::getStatus, post.getStatus())\n            .like(StringUtils.isNotBlank(post.getPostName()), SysPost::getPostName, post.getPostName());\n        Page<SysPost> page = baseMapper.selectPage(pageQuery.build(), lqw);\n        return TableDataInfo.build(page);\n    }\n\n    /**\n     * 查询岗位信息集合\n     *\n     * @param post 岗位信息\n     * @return 岗位信息集合\n     */\n    @Override\n    public List<SysPost> selectPostList(SysPost post) {\n        return baseMapper.selectList(new LambdaQueryWrapper<SysPost>()\n            .like(StringUtils.isNotBlank(post.getPostCode()), SysPost::getPostCode, post.getPostCode())\n            .eq(StringUtils.isNotBlank(post.getStatus()), SysPost::getStatus, post.getStatus())\n            .like(StringUtils.isNotBlank(post.getPostName()), SysPost::getPostName, post.getPostName()));\n    }\n\n    /**\n     * 查询所有岗位\n     *\n     * @return 岗位列表\n     */\n    @Override\n    public List<SysPost> selectPostAll() {\n        return baseMapper.selectList();\n    }\n\n    /**\n     * 通过岗位ID查询岗位信息\n     *\n     * @param postId 岗位ID\n     * @return 角色对象信息\n     */\n    @Override\n    public SysPost selectPostById(Long postId) {\n        return baseMapper.selectById(postId);\n    }\n\n    /**\n     * 根据用户ID获取岗位选择框列表\n     *\n     * @param userId 用户ID\n     * @return 选中岗位ID列表\n     */\n    @Override\n    public List<Long> selectPostListByUserId(Long userId) {\n        return baseMapper.selectPostListByUserId(userId);\n    }\n\n    /**\n     * 校验岗位名称是否唯一\n     *\n     * @param post 岗位信息\n     * @return 结果\n     */\n    @Override\n    public boolean checkPostNameUnique(SysPost post) {\n        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysPost>()\n            .eq(SysPost::getPostName, post.getPostName())\n            .ne(ObjectUtil.isNotNull(post.getPostId()), SysPost::getPostId, post.getPostId()));\n        return !exist;\n    }\n\n    /**\n     * 校验岗位编码是否唯一\n     *\n     * @param post 岗位信息\n     * @return 结果\n     */\n    @Override\n    public boolean checkPostCodeUnique(SysPost post) {\n        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysPost>()\n            .eq(SysPost::getPostCode, post.getPostCode())\n            .ne(ObjectUtil.isNotNull(post.getPostId()), SysPost::getPostId, post.getPostId()));\n        return !exist;\n    }\n\n    /**\n     * 通过岗位ID查询岗位使用数量\n     *\n     * @param postId 岗位ID\n     * @return 结果\n     */\n    @Override\n    public long countUserPostById(Long postId) {\n        return userPostMapper.selectCount(new LambdaQueryWrapper<SysUserPost>().eq(SysUserPost::getPostId, postId));\n    }\n\n    /**\n     * 删除岗位信息\n     *\n     * @param postId 岗位ID\n     * @return 结果\n     */\n    @Override\n    public int deletePostById(Long postId) {\n        return baseMapper.deleteById(postId);\n    }\n\n    /**\n     * 批量删除岗位信息\n     *\n     * @param postIds 需要删除的岗位ID\n     * @return 结果\n     */\n    @Override\n    public int deletePostByIds(Long[] postIds) {\n        for (Long postId : postIds) {\n            SysPost post = selectPostById(postId);\n            if (countUserPostById(postId) > 0) {\n                throw new ServiceException(String.format(\"%1$s已分配,不能删除\", post.getPostName()));\n            }\n        }\n        return baseMapper.deleteBatchIds(Arrays.asList(postIds));\n    }\n\n    /**\n     * 新增保存岗位信息\n     *\n     * @param post 岗位信息\n     * @return 结果\n     */\n    @Override\n    public int insertPost(SysPost post) {\n        return baseMapper.insert(post);\n    }\n\n    /**\n     * 修改保存岗位信息\n     *\n     * @param post 岗位信息\n     * @return 结果\n     */\n    @Override\n    public int updatePost(SysPost post) {\n        return baseMapper.updateById(post);\n    }\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/impl/SysRoleServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.dev33.satoken.exception.NotLoginException;\nimport cn.dev33.satoken.stp.StpUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport lombok.extern.slf4j.Slf4j;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.entity.SysRole;\nimport top.flya.common.core.domain.model.LoginUser;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.StreamUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.system.domain.SysRoleDept;\nimport top.flya.system.domain.SysRoleMenu;\nimport top.flya.system.domain.SysUserRole;\nimport top.flya.system.mapper.SysRoleDeptMapper;\nimport top.flya.system.mapper.SysRoleMapper;\nimport top.flya.system.mapper.SysRoleMenuMapper;\nimport top.flya.system.mapper.SysUserRoleMapper;\nimport top.flya.system.service.ISysRoleService;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.*;\n\n/**\n * 角色 业务层处理\n *\n * @author Lion Li\n */\n@RequiredArgsConstructor\n@Service\n@Slf4j\npublic class SysRoleServiceImpl implements ISysRoleService {\n\n    private final SysRoleMapper baseMapper;\n    private final SysRoleMenuMapper roleMenuMapper;\n    private final SysUserRoleMapper userRoleMapper;\n    private final SysRoleDeptMapper roleDeptMapper;\n\n    @Override\n    public TableDataInfo<SysRole> selectPageRoleList(SysRole role, PageQuery pageQuery) {\n        Page<SysRole> page = baseMapper.selectPageRoleList(pageQuery.build(), this.buildQueryWrapper(role));\n        return TableDataInfo.build(page);\n    }\n\n    /**\n     * 根据条件分页查询角色数据\n     *\n     * @param role 角色信息\n     * @return 角色数据集合信息\n     */\n    @Override\n    public List<SysRole> selectRoleList(SysRole role) {\n        return baseMapper.selectRoleList(this.buildQueryWrapper(role));\n    }\n\n    private Wrapper<SysRole> buildQueryWrapper(SysRole role) {\n        Map<String, Object> params = role.getParams();\n        QueryWrapper<SysRole> wrapper = Wrappers.query();\n        wrapper.eq(\"r.del_flag\", UserConstants.ROLE_NORMAL)\n            .eq(ObjectUtil.isNotNull(role.getRoleId()), \"r.role_id\", role.getRoleId())\n            .like(StringUtils.isNotBlank(role.getRoleName()), \"r.role_name\", role.getRoleName())\n            .eq(StringUtils.isNotBlank(role.getStatus()), \"r.status\", role.getStatus())\n            .like(StringUtils.isNotBlank(role.getRoleKey()), \"r.role_key\", role.getRoleKey())\n            .between(params.get(\"beginTime\") != null && params.get(\"endTime\") != null,\n                \"r.create_time\", params.get(\"beginTime\"), params.get(\"endTime\"))\n            .orderByAsc(\"r.role_sort\").orderByAsc(\"r.create_time\");\n        return wrapper;\n    }\n\n    /**\n     * 根据用户ID查询角色\n     *\n     * @param userId 用户ID\n     * @return 角色列表\n     */\n    @Override\n    public List<SysRole> selectRolesByUserId(Long userId) {\n        List<SysRole> userRoles = baseMapper.selectRolePermissionByUserId(userId);\n        List<SysRole> roles = selectRoleAll();\n        for (SysRole role : roles) {\n            for (SysRole userRole : userRoles) {\n                if (role.getRoleId().longValue() == userRole.getRoleId().longValue()) {\n                    role.setFlag(true);\n                    break;\n                }\n            }\n        }\n        return roles;\n    }\n\n    /**\n     * 根据用户ID查询权限\n     *\n     * @param userId 用户ID\n     * @return 权限列表\n     */\n    @Override\n    public Set<String> selectRolePermissionByUserId(Long userId) {\n        List<SysRole> perms = baseMapper.selectRolePermissionByUserId(userId);\n        Set<String> permsSet = new HashSet<>();\n        for (SysRole perm : perms) {\n            if (ObjectUtil.isNotNull(perm)) {\n                permsSet.addAll(StringUtils.splitList(perm.getRoleKey().trim()));\n            }\n        }\n        return permsSet;\n    }\n\n    /**\n     * 查询所有角色\n     *\n     * @return 角色列表\n     */\n    @Override\n    public List<SysRole> selectRoleAll() {\n        return this.selectRoleList(new SysRole());\n    }\n\n    /**\n     * 根据用户ID获取角色选择框列表\n     *\n     * @param userId 用户ID\n     * @return 选中角色ID列表\n     */\n    @Override\n    public List<Long> selectRoleListByUserId(Long userId) {\n        return baseMapper.selectRoleListByUserId(userId);\n    }\n\n    /**\n     * 通过角色ID查询角色\n     *\n     * @param roleId 角色ID\n     * @return 角色对象信息\n     */\n    @Override\n    public SysRole selectRoleById(Long roleId) {\n        return baseMapper.selectById(roleId);\n    }\n\n    /**\n     * 校验角色名称是否唯一\n     *\n     * @param role 角色信息\n     * @return 结果\n     */\n    @Override\n    public boolean checkRoleNameUnique(SysRole role) {\n        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysRole>()\n            .eq(SysRole::getRoleName, role.getRoleName())\n            .ne(ObjectUtil.isNotNull(role.getRoleId()), SysRole::getRoleId, role.getRoleId()));\n        return !exist;\n    }\n\n    /**\n     * 校验角色权限是否唯一\n     *\n     * @param role 角色信息\n     * @return 结果\n     */\n    @Override\n    public boolean checkRoleKeyUnique(SysRole role) {\n        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysRole>()\n            .eq(SysRole::getRoleKey, role.getRoleKey())\n            .ne(ObjectUtil.isNotNull(role.getRoleId()), SysRole::getRoleId, role.getRoleId()));\n        return !exist;\n    }\n\n    /**\n     * 校验角色是否允许操作\n     *\n     * @param role 角色信息\n     */\n    @Override\n    public void checkRoleAllowed(SysRole role) {\n        if (ObjectUtil.isNotNull(role.getRoleId()) && role.isAdmin()) {\n            throw new ServiceException(\"不允许操作超级管理员角色\");\n        }\n    }\n\n    /**\n     * 校验角色是否有数据权限\n     *\n     * @param roleId 角色id\n     */\n    @Override\n    public void checkRoleDataScope(Long roleId) {\n        if (!LoginHelper.isAdmin()) {\n            SysRole role = new SysRole();\n            role.setRoleId(roleId);\n            List<SysRole> roles = this.selectRoleList(role);\n            if (CollUtil.isEmpty(roles)) {\n                throw new ServiceException(\"没有权限访问角色数据！\");\n            }\n        }\n    }\n\n    /**\n     * 通过角色ID查询角色使用数量\n     *\n     * @param roleId 角色ID\n     * @return 结果\n     */\n    @Override\n    public long countUserRoleByRoleId(Long roleId) {\n        return userRoleMapper.selectCount(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getRoleId, roleId));\n    }\n\n    /**\n     * 新增保存角色信息\n     *\n     * @param role 角色信息\n     * @return 结果\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public int insertRole(SysRole role) {\n        // 新增角色信息\n        baseMapper.insert(role);\n        return insertRoleMenu(role);\n    }\n\n    /**\n     * 修改保存角色信息\n     *\n     * @param role 角色信息\n     * @return 结果\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public int updateRole(SysRole role) {\n        // 修改角色信息\n        baseMapper.updateById(role);\n        // 删除角色与菜单关联\n        roleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getRoleId, role.getRoleId()));\n        return insertRoleMenu(role);\n    }\n\n    /**\n     * 修改角色状态\n     *\n     * @param role 角色信息\n     * @return 结果\n     */\n    @Override\n    public int updateRoleStatus(SysRole role) {\n        return baseMapper.updateById(role);\n    }\n\n    /**\n     * 修改数据权限信息\n     *\n     * @param role 角色信息\n     * @return 结果\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public int authDataScope(SysRole role) {\n        // 修改角色信息\n        baseMapper.updateById(role);\n        // 删除角色与部门关联\n        roleDeptMapper.delete(new LambdaQueryWrapper<SysRoleDept>().eq(SysRoleDept::getRoleId, role.getRoleId()));\n        // 新增角色和部门信息（数据权限）\n        return insertRoleDept(role);\n    }\n\n    /**\n     * 新增角色菜单信息\n     *\n     * @param role 角色对象\n     */\n    public int insertRoleMenu(SysRole role) {\n        int rows = 1;\n        // 新增用户与角色管理\n        List<SysRoleMenu> list = new ArrayList<SysRoleMenu>();\n        for (Long menuId : role.getMenuIds()) {\n            SysRoleMenu rm = new SysRoleMenu();\n            rm.setRoleId(role.getRoleId());\n            rm.setMenuId(menuId);\n            list.add(rm);\n        }\n        if (list.size() > 0) {\n            rows = roleMenuMapper.insertBatch(list) ? list.size() : 0;\n        }\n        return rows;\n    }\n\n    /**\n     * 新增角色部门信息(数据权限)\n     *\n     * @param role 角色对象\n     */\n    public int insertRoleDept(SysRole role) {\n        int rows = 1;\n        // 新增角色与部门（数据权限）管理\n        List<SysRoleDept> list = new ArrayList<SysRoleDept>();\n        for (Long deptId : role.getDeptIds()) {\n            SysRoleDept rd = new SysRoleDept();\n            rd.setRoleId(role.getRoleId());\n            rd.setDeptId(deptId);\n            list.add(rd);\n        }\n        if (list.size() > 0) {\n            rows = roleDeptMapper.insertBatch(list) ? list.size() : 0;\n        }\n        return rows;\n    }\n\n    /**\n     * 通过角色ID删除角色\n     *\n     * @param roleId 角色ID\n     * @return 结果\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public int deleteRoleById(Long roleId) {\n        // 删除角色与菜单关联\n        roleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getRoleId, roleId));\n        // 删除角色与部门关联\n        roleDeptMapper.delete(new LambdaQueryWrapper<SysRoleDept>().eq(SysRoleDept::getRoleId, roleId));\n        return baseMapper.deleteById(roleId);\n    }\n\n    /**\n     * 批量删除角色信息\n     *\n     * @param roleIds 需要删除的角色ID\n     * @return 结果\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public int deleteRoleByIds(Long[] roleIds) {\n        for (Long roleId : roleIds) {\n            checkRoleAllowed(new SysRole(roleId));\n            checkRoleDataScope(roleId);\n            SysRole role = selectRoleById(roleId);\n            if (countUserRoleByRoleId(roleId) > 0) {\n                throw new ServiceException(String.format(\"%1$s已分配,不能删除\", role.getRoleName()));\n            }\n        }\n        List<Long> ids = Arrays.asList(roleIds);\n        // 删除角色与菜单关联\n        roleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenu>().in(SysRoleMenu::getRoleId, ids));\n        // 删除角色与部门关联\n        roleDeptMapper.delete(new LambdaQueryWrapper<SysRoleDept>().in(SysRoleDept::getRoleId, ids));\n        return baseMapper.deleteBatchIds(ids);\n    }\n\n    /**\n     * 取消授权用户角色\n     *\n     * @param userRole 用户和角色关联信息\n     * @return 结果\n     */\n    @Override\n    public int deleteAuthUser(SysUserRole userRole) {\n        int rows = userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>()\n            .eq(SysUserRole::getRoleId, userRole.getRoleId())\n            .eq(SysUserRole::getUserId, userRole.getUserId()));\n        if (rows > 0) {\n            cleanOnlineUserByRole(userRole.getRoleId());\n        }\n        return rows;\n    }\n\n    /**\n     * 批量取消授权用户角色\n     *\n     * @param roleId  角色ID\n     * @param userIds 需要取消授权的用户数据ID\n     * @return 结果\n     */\n    @Override\n    public int deleteAuthUsers(Long roleId, Long[] userIds) {\n        int rows = userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>()\n            .eq(SysUserRole::getRoleId, roleId)\n            .in(SysUserRole::getUserId, Arrays.asList(userIds)));\n        if (rows > 0) {\n            cleanOnlineUserByRole(roleId);\n        }\n        return rows;\n    }\n\n    /**\n     * 批量选择授权用户角色\n     *\n     * @param roleId  角色ID\n     * @param userIds 需要授权的用户数据ID\n     * @return 结果\n     */\n    @Override\n    public int insertAuthUsers(Long roleId, Long[] userIds) {\n        // 新增用户与角色管理\n        int rows = 1;\n        List<SysUserRole> list = StreamUtils.toList(Arrays.asList(userIds), userId -> {\n            SysUserRole ur = new SysUserRole();\n            ur.setUserId(userId);\n            ur.setRoleId(roleId);\n            return ur;\n        });\n        if (CollUtil.isNotEmpty(list)) {\n            rows = userRoleMapper.insertBatch(list) ? list.size() : 0;\n        }\n        if (rows > 0) {\n            cleanOnlineUserByRole(roleId);\n        }\n        return rows;\n    }\n\n    @Override\n    public void cleanOnlineUserByRole(Long roleId) {\n        List<String> keys = StpUtil.searchTokenValue(\"\", 0, -1, false);\n        if (CollUtil.isEmpty(keys)) {\n            return;\n        }\n        log.info(\"清除角色为{}的在线用户,keys is {}\", roleId, keys);\n        // 角色关联的在线用户量过大会导致redis阻塞卡顿 谨慎操作\n        keys.parallelStream().forEach(key -> {\n            String token = StringUtils.substringAfterLast(key, \":\");\n            // 如果已经过期则跳过\n            if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {\n                return;\n            }\n            LoginUser loginUser = LoginHelper.getLoginUser(token);\n            if(loginUser == null|loginUser.getRoles()==null){\n                return;\n            }\n            if (loginUser.getRoles().stream().anyMatch(r -> r.getRoleId().equals(roleId))) {\n                try {\n                    StpUtil.logoutByTokenValue(token);\n                } catch (NotLoginException ignored) {\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/impl/SysSensitiveServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport top.flya.common.core.service.SensitiveService;\nimport top.flya.common.helper.LoginHelper;\nimport org.springframework.stereotype.Service;\n\n/**\n * 脱敏服务\n * 默认管理员不过滤\n * 需自行根据业务重写实现\n *\n * @author Lion Li\n * @version 3.6.0\n */\n@Service\npublic class SysSensitiveServiceImpl implements SensitiveService {\n\n    /**\n     * 是否脱敏\n     */\n    @Override\n    public boolean isSensitive() {\n        return !LoginHelper.isAdmin();\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/java/top/flya/system/service/impl/SysUserServiceImpl.java",
    "content": "package top.flya.system.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport top.flya.common.constant.CacheNames;\nimport top.flya.common.constant.UserConstants;\nimport top.flya.common.core.domain.PageQuery;\nimport top.flya.common.core.domain.entity.SysDept;\nimport top.flya.common.core.domain.entity.SysRole;\nimport top.flya.common.core.domain.entity.SysUser;\nimport top.flya.common.core.page.TableDataInfo;\nimport top.flya.common.core.service.UserService;\nimport top.flya.common.exception.ServiceException;\nimport top.flya.common.helper.DataBaseHelper;\nimport top.flya.common.helper.LoginHelper;\nimport top.flya.common.utils.StreamUtils;\nimport top.flya.common.utils.StringUtils;\nimport top.flya.system.domain.SysPost;\nimport top.flya.system.domain.SysUserPost;\nimport top.flya.system.domain.SysUserRole;\nimport top.flya.system.mapper.*;\nimport top.flya.system.mapper.*;\nimport top.flya.system.service.ISysUserService;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 用户 业务层处理\n *\n * @author Lion Li\n */\n@Slf4j\n@RequiredArgsConstructor\n@Service\npublic class SysUserServiceImpl implements ISysUserService, UserService {\n\n    private final SysUserMapper baseMapper;\n    private final SysDeptMapper deptMapper;\n    private final SysRoleMapper roleMapper;\n    private final SysPostMapper postMapper;\n    private final SysUserRoleMapper userRoleMapper;\n    private final SysUserPostMapper userPostMapper;\n\n    @Override\n    public TableDataInfo<SysUser> selectPageUserList(SysUser user, PageQuery pageQuery) {\n        Page<SysUser> page = baseMapper.selectPageUserList(pageQuery.build(), this.buildQueryWrapper(user));\n        return TableDataInfo.build(page);\n    }\n\n    /**\n     * 根据条件分页查询用户列表\n     *\n     * @param user 用户信息\n     * @return 用户信息集合信息\n     */\n    @Override\n    public List<SysUser> selectUserList(SysUser user) {\n        return baseMapper.selectUserList(this.buildQueryWrapper(user));\n    }\n\n    private Wrapper<SysUser> buildQueryWrapper(SysUser user) {\n        Map<String, Object> params = user.getParams();\n        QueryWrapper<SysUser> wrapper = Wrappers.query();\n        wrapper.eq(\"u.del_flag\", UserConstants.USER_NORMAL)\n            .eq(ObjectUtil.isNotNull(user.getUserId()), \"u.user_id\", user.getUserId())\n            .like(StringUtils.isNotBlank(user.getUserName()), \"u.user_name\", user.getUserName())\n            .eq(StringUtils.isNotBlank(user.getStatus()), \"u.status\", user.getStatus())\n            .like(StringUtils.isNotBlank(user.getPhonenumber()), \"u.phonenumber\", user.getPhonenumber())\n            .between(params.get(\"beginTime\") != null && params.get(\"endTime\") != null,\n                \"u.create_time\", params.get(\"beginTime\"), params.get(\"endTime\"))\n            .and(ObjectUtil.isNotNull(user.getDeptId()), w -> {\n                List<SysDept> deptList = deptMapper.selectList(new LambdaQueryWrapper<SysDept>()\n                    .select(SysDept::getDeptId)\n                    .apply(DataBaseHelper.findInSet(user.getDeptId(), \"ancestors\")));\n                List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId);\n                ids.add(user.getDeptId());\n                w.in(\"u.dept_id\", ids);\n            });\n        return wrapper;\n    }\n\n    /**\n     * 根据条件分页查询已分配用户角色列表\n     *\n     * @param user 用户信息\n     * @return 用户信息集合信息\n     */\n    @Override\n    public TableDataInfo<SysUser> selectAllocatedList(SysUser user, PageQuery pageQuery) {\n        QueryWrapper<SysUser> wrapper = Wrappers.query();\n        wrapper.eq(\"u.del_flag\", UserConstants.USER_NORMAL)\n            .eq(ObjectUtil.isNotNull(user.getRoleId()), \"r.role_id\", user.getRoleId())\n            .like(StringUtils.isNotBlank(user.getUserName()), \"u.user_name\", user.getUserName())\n            .eq(StringUtils.isNotBlank(user.getStatus()), \"u.status\", user.getStatus())\n            .like(StringUtils.isNotBlank(user.getPhonenumber()), \"u.phonenumber\", user.getPhonenumber());\n        Page<SysUser> page = baseMapper.selectAllocatedList(pageQuery.build(), wrapper);\n        return TableDataInfo.build(page);\n    }\n\n    /**\n     * 根据条件分页查询未分配用户角色列表\n     *\n     * @param user 用户信息\n     * @return 用户信息集合信息\n     */\n    @Override\n    public TableDataInfo<SysUser> selectUnallocatedList(SysUser user, PageQuery pageQuery) {\n        List<Long> userIds = userRoleMapper.selectUserIdsByRoleId(user.getRoleId());\n        QueryWrapper<SysUser> wrapper = Wrappers.query();\n        wrapper.eq(\"u.del_flag\", UserConstants.USER_NORMAL)\n            .and(w -> w.ne(\"r.role_id\", user.getRoleId()).or().isNull(\"r.role_id\"))\n            .notIn(CollUtil.isNotEmpty(userIds), \"u.user_id\", userIds)\n            .like(StringUtils.isNotBlank(user.getUserName()), \"u.user_name\", user.getUserName())\n            .like(StringUtils.isNotBlank(user.getPhonenumber()), \"u.phonenumber\", user.getPhonenumber());\n        Page<SysUser> page = baseMapper.selectUnallocatedList(pageQuery.build(), wrapper);\n        return TableDataInfo.build(page);\n    }\n\n    /**\n     * 通过用户名查询用户\n     *\n     * @param userName 用户名\n     * @return 用户对象信息\n     */\n    @Override\n    public SysUser selectUserByUserName(String userName) {\n        return baseMapper.selectUserByUserName(userName);\n    }\n\n    /**\n     * 通过手机号查询用户\n     *\n     * @param phonenumber 手机号\n     * @return 用户对象信息\n     */\n    @Override\n    public SysUser selectUserByPhonenumber(String phonenumber) {\n        return baseMapper.selectUserByPhonenumber(phonenumber);\n    }\n\n    /**\n     * 通过用户ID查询用户\n     *\n     * @param userId 用户ID\n     * @return 用户对象信息\n     */\n    @Override\n    public SysUser selectUserById(Long userId) {\n        return baseMapper.selectUserById(userId);\n    }\n\n    /**\n     * 查询用户所属角色组\n     *\n     * @param userName 用户名\n     * @return 结果\n     */\n    @Override\n    public String selectUserRoleGroup(String userName) {\n        List<SysRole> list = roleMapper.selectRolesByUserName(userName);\n        if (CollUtil.isEmpty(list)) {\n            return StringUtils.EMPTY;\n        }\n        return StreamUtils.join(list, SysRole::getRoleName);\n    }\n\n    /**\n     * 查询用户所属岗位组\n     *\n     * @param userName 用户名\n     * @return 结果\n     */\n    @Override\n    public String selectUserPostGroup(String userName) {\n        List<SysPost> list = postMapper.selectPostsByUserName(userName);\n        if (CollUtil.isEmpty(list)) {\n            return StringUtils.EMPTY;\n        }\n        return StreamUtils.join(list, SysPost::getPostName);\n    }\n\n    /**\n     * 校验用户名称是否唯一\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    @Override\n    public boolean checkUserNameUnique(SysUser user) {\n        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysUser>()\n            .eq(SysUser::getUserName, user.getUserName())\n            .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()));\n        return !exist;\n    }\n\n    /**\n     * 校验手机号码是否唯一\n     *\n     * @param user 用户信息\n     */\n    @Override\n    public boolean checkPhoneUnique(SysUser user) {\n        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysUser>()\n            .eq(SysUser::getPhonenumber, user.getPhonenumber())\n            .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()));\n        return !exist;\n    }\n\n    /**\n     * 校验email是否唯一\n     *\n     * @param user 用户信息\n     */\n    @Override\n    public boolean checkEmailUnique(SysUser user) {\n        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysUser>()\n            .eq(SysUser::getEmail, user.getEmail())\n            .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()));\n        return !exist;\n    }\n\n    /**\n     * 校验用户是否允许操作\n     *\n     * @param user 用户信息\n     */\n    @Override\n    public void checkUserAllowed(SysUser user) {\n        if (ObjectUtil.isNotNull(user.getUserId()) && user.isAdmin()) {\n            throw new ServiceException(\"不允许操作超级管理员用户\");\n        }\n    }\n\n    /**\n     * 校验用户是否有数据权限\n     *\n     * @param userId 用户id\n     */\n    @Override\n    public void checkUserDataScope(Long userId) {\n        if (!LoginHelper.isAdmin()) {\n            SysUser user = new SysUser();\n            user.setUserId(userId);\n            List<SysUser> users = this.selectUserList(user);\n            if (CollUtil.isEmpty(users)) {\n                throw new ServiceException(\"没有权限访问用户数据！\");\n            }\n        }\n    }\n\n    /**\n     * 新增保存用户信息\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public int insertUser(SysUser user) {\n        // 新增用户信息\n        int rows = baseMapper.insert(user);\n        // 新增用户岗位关联\n        insertUserPost(user);\n        // 新增用户与角色管理\n        insertUserRole(user);\n        return rows;\n    }\n\n    /**\n     * 注册用户信息\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    @Override\n    public boolean registerUser(SysUser user) {\n//        user.setCreateBy(user.getUserName());\n//        user.setUpdateBy(user.getUserName());\n        return baseMapper.insert(user) > 0;\n    }\n\n    /**\n     * 修改保存用户信息\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public int updateUser(SysUser user) {\n        Long userId = user.getUserId();\n        // 删除用户与角色关联\n        userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, userId));\n        // 新增用户与角色管理\n        insertUserRole(user);\n        // 删除用户与岗位关联\n        userPostMapper.delete(new LambdaQueryWrapper<SysUserPost>().eq(SysUserPost::getUserId, userId));\n        // 新增用户与岗位管理\n        insertUserPost(user);\n        return baseMapper.updateById(user);\n    }\n\n    /**\n     * 用户授权角色\n     *\n     * @param userId  用户ID\n     * @param roleIds 角色组\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void insertUserAuth(Long userId, Long[] roleIds) {\n        userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>()\n            .eq(SysUserRole::getUserId, userId));\n        insertUserRole(userId, roleIds);\n    }\n\n    /**\n     * 修改用户状态\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    @Override\n    public int updateUserStatus(SysUser user) {\n        return baseMapper.updateById(user);\n    }\n\n    /**\n     * 修改用户基本信息\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    @Override\n    public int updateUserProfile(SysUser user) {\n        return baseMapper.updateById(user);\n    }\n\n    /**\n     * 修改用户头像\n     *\n     * @param userName 用户名\n     * @param avatar   头像地址\n     * @return 结果\n     */\n    @Override\n    public boolean updateUserAvatar(String userName, String avatar) {\n        return baseMapper.update(null,\n            new LambdaUpdateWrapper<SysUser>()\n                .set(SysUser::getAvatar, avatar)\n                .eq(SysUser::getUserName, userName)) > 0;\n    }\n\n    /**\n     * 重置用户密码\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    @Override\n    public int resetPwd(SysUser user) {\n        return baseMapper.updateById(user);\n    }\n\n    /**\n     * 重置用户密码\n     *\n     * @param userName 用户名\n     * @param password 密码\n     * @return 结果\n     */\n    @Override\n    public int resetUserPwd(String userName, String password) {\n        return baseMapper.update(null,\n            new LambdaUpdateWrapper<SysUser>()\n                .set(SysUser::getPassword, password)\n                .eq(SysUser::getUserName, userName));\n    }\n\n    /**\n     * 新增用户角色信息\n     *\n     * @param user 用户对象\n     */\n    public void insertUserRole(SysUser user) {\n        this.insertUserRole(user.getUserId(), user.getRoleIds());\n    }\n\n    /**\n     * 新增用户岗位信息\n     *\n     * @param user 用户对象\n     */\n    public void insertUserPost(SysUser user) {\n        Long[] posts = user.getPostIds();\n        if (ArrayUtil.isNotEmpty(posts)) {\n            // 新增用户与岗位管理\n            List<SysUserPost> list = StreamUtils.toList(Arrays.asList(posts), postId -> {\n                SysUserPost up = new SysUserPost();\n                up.setUserId(user.getUserId());\n                up.setPostId(postId);\n                return up;\n            });\n            userPostMapper.insertBatch(list);\n        }\n    }\n\n    /**\n     * 新增用户角色信息\n     *\n     * @param userId  用户ID\n     * @param roleIds 角色组\n     */\n    public void insertUserRole(Long userId, Long[] roleIds) {\n        if (ArrayUtil.isNotEmpty(roleIds)) {\n            // 新增用户与角色管理\n            List<SysUserRole> list = StreamUtils.toList(Arrays.asList(roleIds), roleId -> {\n                SysUserRole ur = new SysUserRole();\n                ur.setUserId(userId);\n                ur.setRoleId(roleId);\n                return ur;\n            });\n            userRoleMapper.insertBatch(list);\n        }\n    }\n\n    /**\n     * 通过用户ID删除用户\n     *\n     * @param userId 用户ID\n     * @return 结果\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public int deleteUserById(Long userId) {\n        // 删除用户与角色关联\n        userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, userId));\n        // 删除用户与岗位表\n        userPostMapper.delete(new LambdaQueryWrapper<SysUserPost>().eq(SysUserPost::getUserId, userId));\n        return baseMapper.deleteById(userId);\n    }\n\n    /**\n     * 批量删除用户信息\n     *\n     * @param userIds 需要删除的用户ID\n     * @return 结果\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public int deleteUserByIds(Long[] userIds) {\n        for (Long userId : userIds) {\n            checkUserAllowed(new SysUser(userId));\n            checkUserDataScope(userId);\n        }\n        List<Long> ids = Arrays.asList(userIds);\n        // 删除用户与角色关联\n        userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().in(SysUserRole::getUserId, ids));\n        // 删除用户与岗位表\n        userPostMapper.delete(new LambdaQueryWrapper<SysUserPost>().in(SysUserPost::getUserId, ids));\n        return baseMapper.deleteBatchIds(ids);\n    }\n\n    @Cacheable(cacheNames = CacheNames.SYS_USER_NAME, key = \"#userId\")\n    @Override\n    public String selectUserNameById(Long userId) {\n        SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper<SysUser>()\n            .select(SysUser::getUserName).eq(SysUser::getUserId, userId));\n        return ObjectUtil.isNull(sysUser) ? null : sysUser.getUserName();\n    }\n\n}\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/package-info.md",
    "content": "java包使用 `.` 分割 resource 目录使用 `/` 分割\n<br>\n此文件目的 防止文件夹粘连找不到 `xml` 文件"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysConfigMapper\">\n\n    <resultMap type=\"SysConfig\" id=\"SysConfigResult\">\n        <id property=\"configId\" column=\"config_id\"/>\n        <result property=\"configName\" column=\"config_name\"/>\n        <result property=\"configKey\" column=\"config_key\"/>\n        <result property=\"configValue\" column=\"config_value\"/>\n        <result property=\"configType\" column=\"config_type\"/>\n     <!--        <result property=\"createBy\" column=\"create_by\"/>-->\n        <result property=\"createTime\" column=\"create_time\"/>\n  <!--        <result property=\"updateBy\" column=\"update_by\"/>-->\n        <result property=\"updateTime\" column=\"update_time\"/>\n    </resultMap>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysDeptMapper\">\n\n    <resultMap type=\"SysDept\" id=\"SysDeptResult\">\n        <id property=\"deptId\" column=\"dept_id\"/>\n        <result property=\"parentId\" column=\"parent_id\"/>\n        <result property=\"ancestors\" column=\"ancestors\"/>\n        <result property=\"deptName\" column=\"dept_name\"/>\n        <result property=\"orderNum\" column=\"order_num\"/>\n        <result property=\"leader\" column=\"leader\"/>\n        <result property=\"phone\" column=\"phone\"/>\n        <result property=\"email\" column=\"email\"/>\n        <result property=\"status\" column=\"status\"/>\n        <result property=\"delFlag\" column=\"del_flag\"/>\n        <result property=\"parentName\" column=\"parent_name\"/>\n <!--        <result property=\"createBy\" column=\"create_by\"/> -->\n        <result property=\"createTime\" column=\"create_time\"/>\n  <!--        <result property=\"updateBy\" column=\"update_by\"/>-->\n        <result property=\"updateTime\" column=\"update_time\"/>\n    </resultMap>\n\n    <select id=\"selectDeptList\" resultMap=\"SysDeptResult\">\n        select * from sys_dept ${ew.getCustomSqlSegment}\n    </select>\n\n    <select id=\"selectDeptListByRoleId\" resultType=\"Long\">\n        select d.dept_id\n        from sys_dept d\n            left join sys_role_dept rd on d.dept_id = rd.dept_id\n        where rd.role_id = #{roleId}\n            <if test=\"deptCheckStrictly\">\n                and d.dept_id not in (select d.parent_id from sys_dept d inner join sys_role_dept rd on d.dept_id = rd.dept_id and rd.role_id = #{roleId})\n            </if>\n        order by d.parent_id, d.order_num\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysDictDataMapper\">\n\n    <resultMap type=\"SysDictData\" id=\"SysDictDataResult\">\n        <id property=\"dictCode\" column=\"dict_code\"/>\n        <result property=\"dictSort\" column=\"dict_sort\"/>\n        <result property=\"dictLabel\" column=\"dict_label\"/>\n        <result property=\"dictValue\" column=\"dict_value\"/>\n        <result property=\"dictType\" column=\"dict_type\"/>\n        <result property=\"cssClass\" column=\"css_class\"/>\n        <result property=\"listClass\" column=\"list_class\"/>\n        <result property=\"isDefault\" column=\"is_default\"/>\n        <result property=\"status\" column=\"status\"/>\n     <!--        <result property=\"createBy\" column=\"create_by\"/>-->\n        <result property=\"createTime\" column=\"create_time\"/>\n  <!--        <result property=\"updateBy\" column=\"update_by\"/>-->\n        <result property=\"updateTime\" column=\"update_time\"/>\n    </resultMap>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysDictTypeMapper\">\n\n    <resultMap type=\"SysDictType\" id=\"SysDictTypeResult\">\n        <id property=\"dictId\" column=\"dict_id\"/>\n        <result property=\"dictName\" column=\"dict_name\"/>\n        <result property=\"dictType\" column=\"dict_type\"/>\n        <result property=\"status\" column=\"status\"/>\n     <!--        <result property=\"createBy\" column=\"create_by\"/>-->\n        <result property=\"createTime\" column=\"create_time\"/>\n  <!--        <result property=\"updateBy\" column=\"update_by\"/>-->\n        <result property=\"updateTime\" column=\"update_time\"/>\n    </resultMap>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysLogininforMapper\">\n\n    <resultMap type=\"SysLogininfor\" id=\"SysLogininforResult\">\n        <id property=\"infoId\" column=\"info_id\"/>\n        <result property=\"userName\" column=\"user_name\"/>\n        <result property=\"status\" column=\"status\"/>\n        <result property=\"ipaddr\" column=\"ipaddr\"/>\n        <result property=\"loginLocation\" column=\"login_location\"/>\n        <result property=\"browser\" column=\"browser\"/>\n        <result property=\"os\" column=\"os\"/>\n        <result property=\"msg\" column=\"msg\"/>\n        <result property=\"loginTime\" column=\"login_time\"/>\n    </resultMap>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysMenuMapper\">\n\n    <resultMap type=\"SysMenu\" id=\"SysMenuResult\">\n        <id property=\"menuId\" column=\"menu_id\"/>\n        <result property=\"menuName\" column=\"menu_name\"/>\n        <result property=\"parentName\" column=\"parent_name\"/>\n        <result property=\"parentId\" column=\"parent_id\"/>\n        <result property=\"orderNum\" column=\"order_num\"/>\n        <result property=\"path\" column=\"path\"/>\n        <result property=\"component\" column=\"component\"/>\n        <result property=\"queryParam\" column=\"query_param\"/>\n        <result property=\"isFrame\" column=\"is_frame\"/>\n        <result property=\"isCache\" column=\"is_cache\"/>\n        <result property=\"menuType\" column=\"menu_type\"/>\n        <result property=\"visible\" column=\"visible\"/>\n        <result property=\"status\" column=\"status\"/>\n        <result property=\"perms\" column=\"perms\"/>\n        <result property=\"icon\" column=\"icon\"/>\n     <!--        <result property=\"createBy\" column=\"create_by\"/>-->\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n  <!--        <result property=\"updateBy\" column=\"update_by\"/>-->\n        <result property=\"remark\" column=\"remark\"/>\n    </resultMap>\n\n    <select id=\"selectMenuListByUserId\" parameterType=\"SysMenu\" resultMap=\"SysMenuResult\">\n        select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query_param, m.visible, m.status,\n        m.perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time\n        from sys_menu m\n        left join sys_role_menu rm on m.menu_id = rm.menu_id\n        left join sys_user_role sur on rm.role_id = sur.role_id\n        left join sys_role ro on sur.role_id = ro.role_id\n        ${ew.getCustomSqlSegment}\n    </select>\n\n    <select id=\"selectMenuTreeByUserId\" parameterType=\"Long\" resultMap=\"SysMenuResult\">\n        select distinct m.menu_id,\n                        m.parent_id,\n                        m.menu_name,\n                        m.path,\n                        m.component,\n                        m.query_param,\n                        m.visible,\n                        m.status,\n                        m.perms,\n                        m.is_frame,\n                        m.is_cache,\n                        m.menu_type,\n                        m.icon,\n                        m.order_num,\n                        m.create_time\n        from sys_menu m\n                 left join sys_role_menu rm on m.menu_id = rm.menu_id\n                 left join sys_user_role sur on rm.role_id = sur.role_id\n                 left join sys_role ro on sur.role_id = ro.role_id\n                 left join sys_user u on sur.user_id = u.user_id\n        where u.user_id = #{userId}\n          and m.menu_type in ('M', 'C')\n          and m.status = '0'\n          and ro.status = '0'\n        order by m.parent_id, m.order_num\n    </select>\n\n    <select id=\"selectMenuListByRoleId\" resultType=\"Long\">\n        select m.menu_id\n        from sys_menu m\n        left join sys_role_menu rm on m.menu_id = rm.menu_id\n        where rm.role_id = #{roleId}\n        <if test=\"menuCheckStrictly\">\n            and m.menu_id not in (select m.parent_id from sys_menu m inner join sys_role_menu rm on m.menu_id =\n            rm.menu_id and rm.role_id = #{roleId})\n        </if>\n        order by m.parent_id, m.order_num\n    </select>\n\n    <select id=\"selectMenuPerms\" resultType=\"String\">\n        select distinct m.perms\n        from sys_menu m\n                 left join sys_role_menu rm on m.menu_id = rm.menu_id\n                 left join sys_user_role sur on rm.role_id = sur.role_id\n    </select>\n\n    <select id=\"selectMenuPermsByUserId\" parameterType=\"Long\" resultType=\"String\">\n        select distinct m.perms\n        from sys_menu m\n                 left join sys_role_menu rm on m.menu_id = rm.menu_id\n                 left join sys_user_role sur on rm.role_id = sur.role_id\n                 left join sys_role r on r.role_id = sur.role_id\n        where m.status = '0'\n          and r.status = '0'\n          and sur.user_id = #{userId}\n    </select>\n\n    <select id=\"selectMenuPermsByRoleId\" parameterType=\"Long\" resultType=\"String\">\n        select distinct m.perms\n        from sys_menu m\n                 left join sys_role_menu rm on m.menu_id = rm.menu_id\n        where m.status = '0' and rm.role_id = #{roleId}\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysNoticeMapper\">\n\n    <resultMap type=\"SysNotice\" id=\"SysNoticeResult\">\n        <result property=\"noticeId\" column=\"notice_id\"/>\n        <result property=\"noticeTitle\" column=\"notice_title\"/>\n        <result property=\"noticeType\" column=\"notice_type\"/>\n        <result property=\"noticeContent\" column=\"notice_content\"/>\n        <result property=\"status\" column=\"status\"/>\n     <!--        <result property=\"createBy\" column=\"create_by\"/>-->\n        <result property=\"createTime\" column=\"create_time\"/>\n  <!--        <result property=\"updateBy\" column=\"update_by\"/>-->\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"remark\" column=\"remark\"/>\n    </resultMap>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysOperLogMapper\">\n\n    <resultMap type=\"SysOperLog\" id=\"SysOperLogResult\">\n        <id property=\"operId\" column=\"oper_id\"/>\n        <result property=\"title\" column=\"title\"/>\n        <result property=\"businessType\" column=\"business_type\"/>\n        <result property=\"method\" column=\"method\"/>\n        <result property=\"requestMethod\" column=\"request_method\"/>\n        <result property=\"operatorType\" column=\"operator_type\"/>\n        <result property=\"operName\" column=\"oper_name\"/>\n        <result property=\"deptName\" column=\"dept_name\"/>\n        <result property=\"operUrl\" column=\"oper_url\"/>\n        <result property=\"operIp\" column=\"oper_ip\"/>\n        <result property=\"operLocation\" column=\"oper_location\"/>\n        <result property=\"operParam\" column=\"oper_param\"/>\n        <result property=\"jsonResult\" column=\"json_result\"/>\n        <result property=\"status\" column=\"status\"/>\n        <result property=\"errorMsg\" column=\"error_msg\"/>\n        <result property=\"operTime\" column=\"oper_time\"/>\n    </resultMap>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysOssConfigMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\nPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysOssConfigMapper\">\n\n    <resultMap type=\"top.flya.system.domain.SysOssConfig\" id=\"SysOssConfigResult\">\n        <result property=\"ossConfigId\" column=\"oss_config_id\"/>\n        <result property=\"configKey\" column=\"config_key\"/>\n        <result property=\"accessKey\" column=\"access_key\"/>\n        <result property=\"secretKey\" column=\"secret_key\"/>\n        <result property=\"bucketName\" column=\"bucket_name\"/>\n        <result property=\"prefix\" column=\"prefix\"/>\n        <result property=\"endpoint\" column=\"endpoint\"/>\n        <result property=\"isHttps\" column=\"is_https\"/>\n        <result property=\"region\" column=\"region\"/>\n        <result property=\"status\" column=\"status\"/>\n        <result property=\"ext1\" column=\"ext1\"/>\n     <!--        <result property=\"createBy\" column=\"create_by\"/>-->\n        <result property=\"createTime\" column=\"create_time\"/>\n  <!--        <result property=\"updateBy\" column=\"update_by\"/>-->\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"remark\" column=\"remark\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysOssMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysOssMapper\">\n\n    <resultMap type=\"top.flya.system.domain.SysOss\" id=\"SysOssResult\">\n        <result property=\"ossId\" column=\"oss_id\"/>\n        <result property=\"fileName\" column=\"file_name\"/>\n        <result property=\"fileSuffix\" column=\"file_suffix\"/>\n        <result property=\"url\" column=\"url\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n     <!--        <result property=\"createBy\" column=\"create_by\"/>-->\n        <result property=\"updateTime\" column=\"update_time\"/>\n  <!--        <result property=\"updateBy\" column=\"update_by\"/>-->\n        <result property=\"service\" column=\"service\"/>\n    </resultMap>\n\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysPostMapper\">\n\n    <resultMap type=\"SysPost\" id=\"SysPostResult\">\n        <id property=\"postId\" column=\"post_id\"/>\n        <result property=\"postCode\" column=\"post_code\"/>\n        <result property=\"postName\" column=\"post_name\"/>\n        <result property=\"postSort\" column=\"post_sort\"/>\n        <result property=\"status\" column=\"status\"/>\n     <!--        <result property=\"createBy\" column=\"create_by\"/>-->\n        <result property=\"createTime\" column=\"create_time\"/>\n  <!--        <result property=\"updateBy\" column=\"update_by\"/>-->\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"remark\" column=\"remark\"/>\n    </resultMap>\n\n    <select id=\"selectPostListByUserId\" parameterType=\"Long\" resultType=\"Long\">\n        select p.post_id\n        from sys_post p\n                 left join sys_user_post up on up.post_id = p.post_id\n                 left join sys_user u on u.user_id = up.user_id\n        where u.user_id = #{userId}\n    </select>\n\n    <select id=\"selectPostsByUserName\" parameterType=\"String\" resultMap=\"SysPostResult\">\n        select p.post_id, p.post_name, p.post_code\n        from sys_post p\n                 left join sys_user_post up on up.post_id = p.post_id\n                 left join sys_user u on u.user_id = up.user_id\n        where u.user_name = #{userName}\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysRoleDeptMapper\">\n\n    <resultMap type=\"SysRoleDept\" id=\"SysRoleDeptResult\">\n        <result property=\"roleId\" column=\"role_id\"/>\n        <result property=\"deptId\" column=\"dept_id\"/>\n    </resultMap>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysRoleMapper\">\n\n    <resultMap type=\"SysRole\" id=\"SysRoleResult\">\n        <id property=\"roleId\" column=\"role_id\"/>\n        <result property=\"roleName\" column=\"role_name\"/>\n        <result property=\"roleKey\" column=\"role_key\"/>\n        <result property=\"roleSort\" column=\"role_sort\"/>\n        <result property=\"dataScope\" column=\"data_scope\"/>\n        <result property=\"menuCheckStrictly\" column=\"menu_check_strictly\"/>\n        <result property=\"deptCheckStrictly\" column=\"dept_check_strictly\"/>\n        <result property=\"status\" column=\"status\"/>\n        <result property=\"delFlag\" column=\"del_flag\"/>\n     <!--        <result property=\"createBy\" column=\"create_by\"/>-->\n        <result property=\"createTime\" column=\"create_time\"/>\n  <!--        <result property=\"updateBy\" column=\"update_by\"/>-->\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"remark\" column=\"remark\"/>\n    </resultMap>\n\n    <sql id=\"selectRoleVo\">\n        select distinct r.role_id,\n                        r.role_name,\n                        r.role_key,\n                        r.role_sort,\n                        r.data_scope,\n                        r.menu_check_strictly,\n                        r.dept_check_strictly,\n                        r.status,\n                        r.del_flag,\n                        r.create_time,\n                        r.remark\n        from sys_role r\n                 left join sys_user_role sur on sur.role_id = r.role_id\n                 left join sys_user u on u.user_id = sur.user_id\n                 left join sys_dept d on u.dept_id = d.dept_id\n    </sql>\n\n    <select id=\"selectPageRoleList\" resultMap=\"SysRoleResult\">\n        <include refid=\"selectRoleVo\"/>\n        ${ew.getCustomSqlSegment}\n    </select>\n\n    <select id=\"selectRoleList\" resultMap=\"SysRoleResult\">\n        <include refid=\"selectRoleVo\"/>\n        ${ew.getCustomSqlSegment}\n    </select>\n\n    <select id=\"selectRolePermissionByUserId\" parameterType=\"Long\" resultMap=\"SysRoleResult\">\n        <include refid=\"selectRoleVo\"/>\n        WHERE r.del_flag = '0' and sur.user_id = #{userId}\n    </select>\n\n    <select id=\"selectRoleListByUserId\" parameterType=\"Long\" resultType=\"Long\">\n        select r.role_id\n        from sys_role r\n                 left join sys_user_role sur on sur.role_id = r.role_id\n                 left join sys_user u on u.user_id = sur.user_id\n        where u.user_id = #{userId}\n    </select>\n\n    <select id=\"selectRolesByUserName\" parameterType=\"String\" resultMap=\"SysRoleResult\">\n        <include refid=\"selectRoleVo\"/>\n        WHERE r.del_flag = '0' and u.user_name = #{userName}\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysRoleMenuMapper\">\n\n    <resultMap type=\"SysRoleMenu\" id=\"SysRoleMenuResult\">\n        <result property=\"roleId\" column=\"role_id\"/>\n        <result property=\"menuId\" column=\"menu_id\"/>\n    </resultMap>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysUserMapper\">\n\n    <resultMap type=\"SysUser\" id=\"SysUserResult\">\n        <id property=\"userId\" column=\"user_id\"/>\n        <result property=\"deptId\" column=\"dept_id\"/>\n        <result property=\"userName\" column=\"user_name\"/>\n        <result property=\"nickName\" column=\"nick_name\"/>\n        <result property=\"userType\" column=\"user_type\"/>\n        <result property=\"email\" column=\"email\"/>\n        <result property=\"phonenumber\" column=\"phonenumber\"/>\n        <result property=\"sex\" column=\"sex\"/>\n        <result property=\"avatar\" column=\"avatar\"/>\n        <result property=\"password\" column=\"password\"/>\n        <result property=\"status\" column=\"status\"/>\n        <result property=\"delFlag\" column=\"del_flag\"/>\n        <result property=\"loginIp\" column=\"login_ip\"/>\n        <result property=\"loginDate\" column=\"login_date\"/>\n     <!--        <result property=\"createBy\" column=\"create_by\"/>-->\n        <result property=\"createTime\" column=\"create_time\"/>\n  <!--        <result property=\"updateBy\" column=\"update_by\"/>-->\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"remark\" column=\"remark\"/>\n        <association property=\"dept\" column=\"dept_id\" javaType=\"SysDept\" resultMap=\"deptResult\"/>\n        <collection property=\"roles\" javaType=\"java.util.List\" resultMap=\"RoleResult\"/>\n    </resultMap>\n\n    <resultMap id=\"deptResult\" type=\"SysDept\">\n        <id property=\"deptId\" column=\"dept_id\"/>\n        <result property=\"parentId\" column=\"parent_id\"/>\n        <result property=\"deptName\" column=\"dept_name\"/>\n        <result property=\"ancestors\" column=\"ancestors\"/>\n        <result property=\"orderNum\" column=\"order_num\"/>\n        <result property=\"leader\" column=\"leader\"/>\n        <result property=\"status\" column=\"dept_status\"/>\n    </resultMap>\n\n    <resultMap id=\"RoleResult\" type=\"SysRole\">\n        <id property=\"roleId\" column=\"role_id\"/>\n        <result property=\"roleName\" column=\"role_name\"/>\n        <result property=\"roleKey\" column=\"role_key\"/>\n        <result property=\"roleSort\" column=\"role_sort\"/>\n        <result property=\"dataScope\" column=\"data_scope\"/>\n        <result property=\"status\" column=\"role_status\"/>\n    </resultMap>\n\n    <sql id=\"selectUserVo\">\n        select u.user_id,\n               u.dept_id,\n               u.user_name,\n               u.nick_name,\n               u.user_type,\n               u.email,\n               u.avatar,\n               u.phonenumber,\n               u.password,\n               u.sex,\n               u.status,\n               u.del_flag,\n               u.login_ip,\n               u.login_date,\n--                u.create_by,\n               u.create_time,\n               u.remark,\n               d.dept_id,\n               d.parent_id,\n               d.ancestors,\n               d.dept_name,\n               d.order_num,\n               d.leader,\n               d.status as dept_status,\n               r.role_id,\n               r.role_name,\n               r.role_key,\n               r.role_sort,\n               r.data_scope,\n               r.status as role_status\n        from sys_user u\n            left join sys_dept d on u.dept_id = d.dept_id\n            left join sys_user_role sur on u.user_id = sur.user_id\n            left join sys_role r on r.role_id = sur.role_id\n    </sql>\n\n    <select id=\"selectPageUserList\" resultMap=\"SysUserResult\">\n        select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,\n            u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader\n        from sys_user u\n            left join sys_dept d on u.dept_id = d.dept_id\n        ${ew.getCustomSqlSegment}\n    </select>\n\n    <select id=\"selectUserList\" resultMap=\"SysUserResult\">\n        select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,\n            u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader\n        from sys_user u\n            left join sys_dept d on u.dept_id = d.dept_id\n        ${ew.getCustomSqlSegment}\n    </select>\n\n    <select id=\"selectAllocatedList\" resultMap=\"SysUserResult\">\n        select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time\n        from sys_user u\n             left join sys_dept d on u.dept_id = d.dept_id\n             left join sys_user_role sur on u.user_id = sur.user_id\n             left join sys_role r on r.role_id = sur.role_id\n        ${ew.getCustomSqlSegment}\n    </select>\n\n    <select id=\"selectUnallocatedList\" resultMap=\"SysUserResult\">\n        select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time\n        from sys_user u\n             left join sys_dept d on u.dept_id = d.dept_id\n             left join sys_user_role sur on u.user_id = sur.user_id\n             left join sys_role r on r.role_id = sur.role_id\n        ${ew.getCustomSqlSegment}\n    </select>\n\n    <select id=\"selectUserByUserName\" parameterType=\"String\" resultMap=\"SysUserResult\">\n        <include refid=\"selectUserVo\"/>\n        where u.del_flag = '0' and u.user_name = #{userName}\n    </select>\n\n    <select id=\"selectUserByPhonenumber\" parameterType=\"String\" resultMap=\"SysUserResult\">\n        <include refid=\"selectUserVo\"/>\n        where u.del_flag = '0' and u.phonenumber = #{phonenumber}\n    </select>\n\n    <select id=\"selectUserByEmail\" parameterType=\"String\" resultMap=\"SysUserResult\">\n        <include refid=\"selectUserVo\"/>\n        where u.del_flag = '0' and u.email = #{email}\n    </select>\n\n    <select id=\"selectUserById\" parameterType=\"Long\" resultMap=\"SysUserResult\">\n        <include refid=\"selectUserVo\"/>\n        where u.del_flag = '0' and u.user_id = #{userId}\n    </select>\n\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysUserPostMapper\">\n\n    <resultMap type=\"SysUserPost\" id=\"SysUserPostResult\">\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"postId\" column=\"post_id\"/>\n    </resultMap>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"top.flya.system.mapper.SysUserRoleMapper\">\n\n    <resultMap type=\"SysUserRole\" id=\"SysUserRoleResult\">\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"roleId\" column=\"role_id\"/>\n    </resultMap>\n    <select id=\"selectUserIdsByRoleId\" resultType=\"Long\">\n        select u.user_id from sys_user u\n        inner join sys_user_role sur\n            on u.user_id = sur.user_id and sur.role_id = #{roleId}\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "ruoyi-ui/.editorconfig",
    "content": "# 告诉EditorConfig插件，这是根文件，不用继续往上查找\nroot = true\n\n# 匹配全部文件\n[*]\n# 设置字符集\ncharset = utf-8\n# 缩进风格，可选space、tab\nindent_style = space\n# 缩进的空格数\nindent_size = 2\n# 结尾换行符，可选lf、cr、crlf\nend_of_line = lf\n# 在文件结尾插入新行\ninsert_final_newline = true\n# 删除一行中的前后空格\ntrim_trailing_whitespace = true\n\n# 匹配md结尾的文件\n[*.md]\ninsert_final_newline = false\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": "ruoyi-ui/.eslintignore",
    "content": "# 忽略build目录下类型为js的文件的语法检查\nbuild/*.js\n# 忽略src/assets目录下文件的语法检查\nsrc/assets\n# 忽略public目录下文件的语法检查\npublic\n# 忽略当前目录下为js的文件的语法检查\n*.js\n# 忽略当前目录下为vue的文件的语法检查\n*.vue"
  },
  {
    "path": "ruoyi-ui/.eslintrc.js",
    "content": "// ESlint 检查配置\nmodule.exports = {\n  root: true,\n  parserOptions: {\n    parser: 'babel-eslint',\n    sourceType: 'module'\n  },\n  env: {\n    browser: true,\n    node: true,\n    es6: true,\n  },\n  extends: ['plugin:vue/recommended', 'eslint:recommended'],\n\n  // add your custom rules here\n  //it is base on https://github.com/vuejs/eslint-config-vue\n  rules: {\n    \"vue/max-attributes-per-line\": [2, {\n      \"singleline\": 10,\n      \"multiline\": {\n        \"max\": 1,\n        \"allowFirstLine\": false\n      }\n    }],\n    \"vue/singleline-html-element-content-newline\": \"off\",\n    \"vue/multiline-html-element-content-newline\":\"off\",\n    \"vue/name-property-casing\": [\"error\", \"PascalCase\"],\n    \"vue/no-v-html\": \"off\",\n    'accessor-pairs': 2,\n    'arrow-spacing': [2, {\n      'before': true,\n      'after': true\n    }],\n    'block-spacing': [2, 'always'],\n    'brace-style': [2, '1tbs', {\n      'allowSingleLine': true\n    }],\n    'camelcase': [0, {\n      'properties': 'always'\n    }],\n    'comma-dangle': [2, 'never'],\n    'comma-spacing': [2, {\n      'before': false,\n      'after': true\n    }],\n    'comma-style': [2, 'last'],\n    'constructor-super': 2,\n    'curly': [2, 'multi-line'],\n    'dot-location': [2, 'property'],\n    'eol-last': 2,\n    'eqeqeq': [\"error\", \"always\", {\"null\": \"ignore\"}],\n    'generator-star-spacing': [2, {\n      'before': true,\n      'after': true\n    }],\n    'handle-callback-err': [2, '^(err|error)$'],\n    'indent': [2, 2, {\n      'SwitchCase': 1\n    }],\n    'jsx-quotes': [2, 'prefer-single'],\n    'key-spacing': [2, {\n      'beforeColon': false,\n      'afterColon': true\n    }],\n    'keyword-spacing': [2, {\n      'before': true,\n      'after': true\n    }],\n    'new-cap': [2, {\n      'newIsCap': true,\n      'capIsNew': false\n    }],\n    'new-parens': 2,\n    'no-array-constructor': 2,\n    'no-caller': 2,\n    'no-console': 'off',\n    'no-class-assign': 2,\n    'no-cond-assign': 2,\n    'no-const-assign': 2,\n    'no-control-regex': 0,\n    'no-delete-var': 2,\n    'no-dupe-args': 2,\n    'no-dupe-class-members': 2,\n    'no-dupe-keys': 2,\n    'no-duplicate-case': 2,\n    'no-empty-character-class': 2,\n    'no-empty-pattern': 2,\n    'no-eval': 2,\n    'no-ex-assign': 2,\n    'no-extend-native': 2,\n    'no-extra-bind': 2,\n    'no-extra-boolean-cast': 2,\n    'no-extra-parens': [2, 'functions'],\n    'no-fallthrough': 2,\n    'no-floating-decimal': 2,\n    'no-func-assign': 2,\n    'no-implied-eval': 2,\n    'no-inner-declarations': [2, 'functions'],\n    'no-invalid-regexp': 2,\n    'no-irregular-whitespace': 2,\n    'no-iterator': 2,\n    'no-label-var': 2,\n    'no-labels': [2, {\n      'allowLoop': false,\n      'allowSwitch': false\n    }],\n    'no-lone-blocks': 2,\n    'no-mixed-spaces-and-tabs': 2,\n    'no-multi-spaces': 2,\n    'no-multi-str': 2,\n    'no-multiple-empty-lines': [2, {\n      'max': 1\n    }],\n    'no-native-reassign': 2,\n    'no-negated-in-lhs': 2,\n    'no-new-object': 2,\n    'no-new-require': 2,\n    'no-new-symbol': 2,\n    'no-new-wrappers': 2,\n    'no-obj-calls': 2,\n    'no-octal': 2,\n    'no-octal-escape': 2,\n    'no-path-concat': 2,\n    'no-proto': 2,\n    'no-redeclare': 2,\n    'no-regex-spaces': 2,\n    'no-return-assign': [2, 'except-parens'],\n    'no-self-assign': 2,\n    'no-self-compare': 2,\n    'no-sequences': 2,\n    'no-shadow-restricted-names': 2,\n    'no-spaced-func': 2,\n    'no-sparse-arrays': 2,\n    'no-this-before-super': 2,\n    'no-throw-literal': 2,\n    'no-trailing-spaces': 2,\n    'no-undef': 2,\n    'no-undef-init': 2,\n    'no-unexpected-multiline': 2,\n    'no-unmodified-loop-condition': 2,\n    'no-unneeded-ternary': [2, {\n      'defaultAssignment': false\n    }],\n    'no-unreachable': 2,\n    'no-unsafe-finally': 2,\n    'no-unused-vars': [2, {\n      'vars': 'all',\n      'args': 'none'\n    }],\n    'no-useless-call': 2,\n    'no-useless-computed-key': 2,\n    'no-useless-constructor': 2,\n    'no-useless-escape': 0,\n    'no-whitespace-before-property': 2,\n    'no-with': 2,\n    'one-var': [2, {\n      'initialized': 'never'\n    }],\n    'operator-linebreak': [2, 'after', {\n      'overrides': {\n        '?': 'before',\n        ':': 'before'\n      }\n    }],\n    'padded-blocks': [2, 'never'],\n    'quotes': [2, 'single', {\n      'avoidEscape': true,\n      'allowTemplateLiterals': true\n    }],\n    'semi': [2, 'never'],\n    'semi-spacing': [2, {\n      'before': false,\n      'after': true\n    }],\n    'space-before-blocks': [2, 'always'],\n    'space-before-function-paren': [2, 'never'],\n    'space-in-parens': [2, 'never'],\n    'space-infix-ops': 2,\n    'space-unary-ops': [2, {\n      'words': true,\n      'nonwords': false\n    }],\n    'spaced-comment': [2, 'always', {\n      'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']\n    }],\n    'template-curly-spacing': [2, 'never'],\n    'use-isnan': 2,\n    'valid-typeof': 2,\n    'wrap-iife': [2, 'any'],\n    'yield-star-spacing': [2, 'both'],\n    'yoda': [2, 'never'],\n    'prefer-const': 2,\n    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,\n    'object-curly-spacing': [2, 'always', {\n      objectsInObjects: false\n    }],\n    'array-bracket-spacing': [2, 'never']\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/.gitignore",
    "content": ".DS_Store\nnode_modules/\ndist/\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n**/*.log\n\ntests/**/coverage/\ntests/e2e/reports\nselenium-debug.log\n\n# Editor directories and files\n.idea\n.vscode\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.local\n\npackage-lock.json\nyarn.lock\n"
  },
  {
    "path": "ruoyi-ui/Dockerfile",
    "content": "# 基础镜像\nFROM node:14.15.1-alpine as build-stage\n\n# 设置工作目录\nWORKDIR /app\n\n# 拷贝 package.json 和 package-lock.json 到工作目录\nCOPY ./ruoyi-ui/package*.json ./\n\n\n# 设置npm的镜像源为淘宝镜像\nRUN npm config set registry https://registry.npm.taobao.org/\n\n\n# 安装依赖\nRUN npm install\n\n# 拷贝所有文件到工作目录\nCOPY ./ruoyi-ui .\n\n# 构建生产环境的代码\nRUN npm run build:prod\n\n# 生产环境镜像\nFROM nginx:1.19.6-alpine\n\n# 将构建好的代码拷贝到 Nginx 的默认目录\nCOPY --from=build-stage /app/dist /usr/share/nginx/html\n\n# 复制自定义 Nginx 配置文件到容器中\nCOPY ./ruoyi-ui/nginx.conf /etc/nginx/conf.d/default.conf\n\n# 暴露80端口\nEXPOSE 80\n\n# 启动Nginx服务\nCMD [\"nginx\", \"-g\", \"daemon off;\"]\n"
  },
  {
    "path": "ruoyi-ui/README.md",
    "content": "## 开发\n\n```bash\n# 克隆项目\ngit clone https://gitee.com/y_project/RuoYi-Vue\n\n# 进入项目目录\ncd ruoyi-ui\n\n# 安装依赖\nnpm install\n\n# 建议不要直接使用 cnpm 安装依赖，会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题\nnpm install --registry=https://registry.npmmirror.com\n\n# 启动服务\nnpm run dev\n```\n\n浏览器访问 http://localhost:80\n\n## 发布\n\n```bash\n# 构建测试环境\nnpm run build:stage\n\n# 构建生产环境\nnpm run build:prod\n```"
  },
  {
    "path": "ruoyi-ui/babel.config.js",
    "content": "module.exports = {\n  presets: [\n    // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app\n    '@vue/cli-plugin-babel/preset'\n  ],\n  'env': {\n    'development': {\n      // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require().\n      // This plugin can significantly increase the speed of hot updates, when you have a large number of pages.\n      'plugins': ['dynamic-import-node']\n    }\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/bin/build.bat",
    "content": "@echo off\necho.\necho [Ϣ] Weḅdistļ\necho.\n\n%~d0\ncd %~dp0\n\ncd ..\nnpm run build:prod\n\npause"
  },
  {
    "path": "ruoyi-ui/bin/package.bat",
    "content": "@echo off\necho.\necho [信息] 安装Web工程，生成node_modules文件。\necho.\n\n%~d0\ncd %~dp0\n\ncd ..\nnpm install --registry=https://registry.npmmirror.com\n\npause\n"
  },
  {
    "path": "ruoyi-ui/bin/run-web.bat",
    "content": "@echo off\necho.\necho [信息] 使用 Vue CLI 命令运行 Web 工程。\necho.\n\n%~d0\ncd %~dp0\n\ncd ..\nnpm run dev\n\npause"
  },
  {
    "path": "ruoyi-ui/build/index.js",
    "content": "const { run } = require('runjs')\nconst chalk = require('chalk')\nconst config = require('../vue.config.js')\nconst rawArgv = process.argv.slice(2)\nconst args = rawArgv.join(' ')\n\nif (process.env.npm_config_preview || rawArgv.includes('--preview')) {\n  const report = rawArgv.includes('--report')\n\n  run(`vue-cli-service build ${args}`)\n\n  const port = 9526\n  const publicPath = config.publicPath\n\n  var connect = require('connect')\n  var serveStatic = require('serve-static')\n  const app = connect()\n\n  app.use(\n    publicPath,\n    serveStatic('./dist', {\n      index: ['index.html', '/']\n    })\n  )\n\n  app.listen(port, function () {\n    console.log(chalk.green(`> Preview at  http://localhost:${port}${publicPath}`))\n    if (report) {\n      console.log(chalk.green(`> Report at  http://localhost:${port}${publicPath}report.html`))\n    }\n\n  })\n} else {\n  run(`vue-cli-service build ${args}`)\n}\n"
  },
  {
    "path": "ruoyi-ui/nginx.conf",
    "content": "\n# nginx配置\nserver {\n    listen       80;\n    server_name  localhost;\n    client_max_body_size 100m;         #主要是这个参数，限制了上传文件大大小\n\n    #charset koi8-r;\n    access_log  /var/log/nginx/host.access.log  main;\n    error_log  /var/log/nginx/error.log  error;\n\n    location / {\n        # root 根目录，默认nginx镜像的html文件夹，可以指定其他\n        root   /usr/share/nginx/html;\n        index  index.html index.htm;\n        # 如果vue-router使用的是history模式，需要设置这个\n        try_files $uri $uri/ /index.html;\n    }\n    location /prod-api {\n        rewrite ^/prod-api(.*)$ $1 break;\n        proxy_pass http://paizhicheng-backend-service:9393;\n    }\n\n    # redirect server error pages to the static page /50x.html\n    #\n    error_page   500 502 503 504  /50x.html;\n    location = /50x.html {\n        root   /usr/share/nginx/html;\n    }\n}\n\n"
  },
  {
    "path": "ruoyi-ui/package.json",
    "content": "{\n  \"name\": \"ruoyi-vue-plus\",\n  \"version\": \"4.7.0\",\n  \"description\": \"派之城后台管理系统\",\n  \"author\": \"LionLi\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"dev\": \"vue-cli-service serve\",\n    \"build:prod\": \"vue-cli-service build\",\n    \"preview\": \"node build/index.js --preview\",\n    \"lint\": \"eslint --ext .js,.vue src\"\n  },\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"lint-staged\"\n    }\n  },\n  \"lint-staged\": {\n    \"src/**/*.{js,vue}\": [\n      \"eslint --fix\",\n      \"git add\"\n    ]\n  },\n  \"keywords\": [\n    \"vue\",\n    \"admin\",\n    \"dashboard\",\n    \"element-ui\",\n    \"boilerplate\",\n    \"admin-template\",\n    \"management-system\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://gitee.com/dromara/RuoYi-Vue-Plus.git\"\n  },\n  \"dependencies\": {\n    \"@amap/amap-jsapi-loader\": \"^1.0.1\",\n    \"@riophae/vue-treeselect\": \"0.4.0\",\n    \"axios\": \"0.24.0\",\n    \"clipboard\": \"2.0.8\",\n    \"cnpm\": \"^9.2.0\",\n    \"core-js\": \"3.25.3\",\n    \"dayjs\": \"^1.11.9\",\n    \"echarts\": \"5.4.0\",\n    \"element-ui\": \"2.15.12\",\n    \"file-saver\": \"2.0.5\",\n    \"fuse.js\": \"6.4.3\",\n    \"highlight.js\": \"9.18.5\",\n    \"js-beautify\": \"1.13.0\",\n    \"js-cookie\": \"3.0.1\",\n    \"jsencrypt\": \"3.0.0-rc.1\",\n    \"nprogress\": \"0.2.0\",\n    \"quill\": \"1.3.7\",\n    \"screenfull\": \"5.0.2\",\n    \"sortablejs\": \"1.10.2\",\n    \"vue\": \"2.6.12\",\n    \"vue-amap\": \"^0.5.10\",\n    \"vue-count-to\": \"1.0.13\",\n    \"vue-cropper\": \"0.5.5\",\n    \"vue-meta\": \"2.4.0\",\n    \"vue-router\": \"3.4.9\",\n    \"vuedraggable\": \"2.24.3\",\n    \"vuex\": \"3.6.0\"\n  },\n  \"devDependencies\": {\n    \"@vue/cli-plugin-babel\": \"4.4.6\",\n    \"@vue/cli-plugin-eslint\": \"4.4.6\",\n    \"@vue/cli-service\": \"4.4.6\",\n    \"babel-eslint\": \"10.1.0\",\n    \"babel-plugin-dynamic-import-node\": \"2.3.3\",\n    \"chalk\": \"4.1.0\",\n    \"compression-webpack-plugin\": \"5.0.2\",\n    \"connect\": \"3.6.6\",\n    \"eslint\": \"7.15.0\",\n    \"eslint-plugin-vue\": \"7.2.0\",\n    \"lint-staged\": \"10.5.3\",\n    \"runjs\": \"4.4.2\",\n    \"sass\": \"1.32.13\",\n    \"sass-loader\": \"10.1.1\",\n    \"script-ext-html-webpack-plugin\": \"2.1.5\",\n    \"svg-sprite-loader\": \"5.1.1\",\n    \"vue-template-compiler\": \"2.6.12\"\n  },\n  \"engines\": {\n    \"node\": \">=8.9\",\n    \"npm\": \">= 3.0.0\"\n  },\n  \"browserslist\": [\n    \"> 1%\",\n    \"last 2 versions\"\n  ]\n}\n"
  },
  {
    "path": "ruoyi-ui/public/html/ie.html",
    "content": "\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\" />\n    <title>请升级您的浏览器</title>\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge,chrome=1\" >\n    <meta name=\"renderer\" content=\"webkit\">\n    <base target=\"_blank\" />\n    <style type=\"text/css\">\n        html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{border:0;font-size:100%;font:inherit;vertical-align:baseline;margin:0;padding:0}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:none}table{border-collapse:collapse;border-spacing:0}\n        a{text-decoration:none;color:#0072c6;}a:hover{text-decoration:none;color:#004d8c;}\n        body{width:960px;margin:0 auto;padding:10px;font-size:14px;line-height:24px;color:#454545;font-family:'Microsoft YaHei UI','Microsoft YaHei',DengXian,SimSun,'Segoe UI',Tahoma,Helvetica,sans-serif;overflow-y:scroll}\n        h1{font-size:40px;line-height:80px;font-weight:100;margin-bottom:10px;}\n        h2{font-size:20px;line-height:25px;font-weight:100;margin:10px 0;}\n        em{color:red}\n        p{margin-bottom:10px;}\n        hr{margin:20px 0;border:0;border-top:1px solid #dadada}\n        span{display:block;font-size:12px;line-height:12px;}\n        .clean{clear:both;}\n        .browser{padding:10px 10px;}\n        .browser li{width:auto;padding:0 80px;margin-top:30px;height:34px;line-height:22px;float:left;list-style:none;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAADMCAYAAAAWCXEwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAC7ESURBVHja5Lx5dFRV1rBfgHwYRQQVtB26ZWhtabtfeUGxGxFbUGZF8RMHGkVbRkekVYiKisicVhE0gEwBokgDAhEMMSSQkAECwcxkrlRSqVTqJqnxzs/vj5t7qUyAvr9e37fWV2vtleSm6p6n9t5nn733OVU2RaUaEP5PiqJSbeMXPBTA5/Xhzk9Vnd9vo3HFx21E2LYJX9IRgh6npvyCe9uaqS4K4C3IpXHFx9S99CTuJ8Z0KLVjRlA7ZgTuJ8ZgXxmJL+kIlwAkXBQk6HFq9pWRVA8fSvXwodYgdS892a6EA1UNvouqwXdR99KTeAtyfz2IL+kI1cOHYh9wqwVwKWJqpXbMCOv19gG3Imzb1JF2OgZxfr/NukH4jcNVfyEAE8IU+4BbKet1PfaVke3BtA/i/H6b8aIBt7a4mWmaC0nr55vmqRp8F5V33Mm5LhHtwbQF8SUdsSDCb1I1+K42g1xIWgOYYh9wK+e6RCBs29QxSIWus37aJM51iWjx4so77mwD1d5AHQ1eecedlN9yuyVlva6nrNf14Q7cEmRn4W7u3T2E9ME3UX7L7W1uZg5Weced1s3sA2613ql5LXzQjuRclwjcT4wxTXQeRHC7GLdnHPeensiCVwa3e0PznZk3EbZtwluQa0kofz8NcVNxr++Ce30XnNuv61Bcu7viXt8Fvyu7JYipjfGHxzD+8Bh2j+7fAiZcC+Y0zPDIbCyD6DyV6DyVeDcIQR2C39J4oieNJ3oSOnkVcnZ35Ozu6MVdDHF0N6S4C43OqJYg/0ydzb27hzDx0FjuPT2R+asfa6OVsl7X40s6QoWus/CQk6fWZPHChhxe3lbMCxtyrN9TyxSQSwidvMoC0XK6tRGybPjSRmOuNUKVo4Zxe8YxIu4+Jh4ay/jDY7j39MQWWjnXJYLGFR9Toes8tSaLiavTrIHDxfxfapkCwW8hy9YuhCmhk1fR1FRnaCS1NM4yy8RDYy2tjIkZRXq/HtYsCnqc2sJDTkYsTrU00J6YkEJQR7M/eEGY0MmrcOenqjZA2JmyzTJLuJiOe65LBHUvPUmGR2bE4lQmrk7jqTVZHcrE1WkMWpRIdJ4KnpUXBCHLRl3e16EWIOEaMU00/vAY9na/gsYVH/NdgYe+8w9bMBeSQYsSWXjICcFvL2ga+dhlFwcJ10rjio/ZklprgbSWiavTWvzdd/5hXt5W/OtATC201sq9u4eQ+PVijmSW0nf+YQYtSmTQosR2gUYsTmXQokT6zj9saeRCpmkJ0hxD2gOZeGgsI+Lu45+ps7FXlFmDmDDtSd/5h+k7/zCpZQpa9cwOQciyIR+77LyzFhXlMyZmFOP2jLP8orVWRsTdR2ppHFtSa+k6ZZM1WHvSdcomwyxySceayO4OWTY88TdirygzUkWf18eL2//RQiutYcwYE/Q4tagDOUQ8uo6uUzbRZ3qMJV2nbCLi0XU8tSbrolNXzu6OfOyylgEN4NOkaO5acw/j9ozr0ET37h5imehIZimPL91rAfSZHsOQBfuISS7E7vaTETeX0MmrOoQInbwK+dhlNKWsahni0zPSuGvNPW1M1BrI1NrOwt0WkCn2ijJSS+MYt2ccuQk3oxd36RCi8URPY+HLT1VbgGiSzPsx71laCddMe2Yygf6ZOtuScXvG0XfJn/n8YL+LQnjibyQ34WZ8Xl/bfKSoKL+FVi4EYwKZcu/uIQzaPoExMaPQcrq1ADFX33AI1+6u1OV9HVI6ShU/TYqm75I/dwjTHtDEQ2MZt2ccg7ZPaGGScIDWEBlxc42UoSMQ00StYdoDCgcbtH0Cbx+8p40ZTIBwiFM7RmB3+y+exZvT2YRpDdR6ZoVrw1xRWwN44m/Euf06A6Ki7NLrmnDNmH7TEdSg7RP4/GA/yLK1GdwEKNzSk1M7RlDlqPl1JefOlG2MXTGmXaAxMaMsB/XE34h4tH+7ANlrB7T2iV8OAlDlqOH9mPcsIBPKlF3R16Ad7GwlxoVberYAKCrKv1ghfmkg5sPldLIzZVsLqLErxpC9doAlp3aMICNurlGyVpRdSAu/HqS1Q58rd1JUlI87P1UtKsrHXlGG3e1HCOoov+x2wiX3RxT+o49L1IgutXxVUCfDIxNfLraQDI+M3e3/NdCXbhohqBNfLrIsVzZqmoT6dmXG0SBLTrmJLxd/CVRLECXcDGFaSC1TmHE0yKg4B0P2uxiy38WoOAePHaptAfHYoVqG7HcxcGc5o+IcfFfgsbQUPoYoSa213BbE78oGucTSwpJTbobFFjNgbQHdvi6g8/Z6Om+vZ8h+VxsQE7T/97UMWFvA+Og0UvIryfDIZBQ4CeXvt8a5IAhAY/RImlJWUaHrPHaolhuXFXHN+8e58qNcbomq5P6t3xG973WePLzPgnnsUG0LiP7f1zJwZzk3LisyctfSOFxOJ4lfLzYToQubxu/KpmpWBFWzInguOokrP8ql7/zDRMxLpFfUabasHwlZNnITbmbgznI6b6+3Bu7/fa2lrW5fF9Ar6jQD1hYwLLaYx5fupdi+EiGok748koa4qa010xKkKWUV2UM7kd6vB7tH9yfpnUFkLzQiZOGWnmgHO9N4oie9ok5bA4YPbkqvqNNc8/5xIuYl8tSaLOLLRXambENXF+PxNJD0ziAanVHhYaEliH1lJD/1iqD0qSsIzu2M/N550TZ3QjvYmS3rR1qDtwdhgpgwnabGMj46zRjQsxJdXYw7P1X1pY0GuaRjkMKxPah5qxuV8y6nct7l1LzVDfdyo6miHexM+ou9mblwKfdv/Y77t37HNe8fbwMQDhIxL5FOU2PZklqLJjUYdU7wWxBuN+ricBAF0KQG6pcNovZpw0fCQao/MEBcu7tSOLYHjnu7EZzbmeDczqyfNokrP8ptMXi4XDnzAJ0n72TIgn1oUoMB4VlpgIjj24I0payi9KkrqHj+Ssth2wM5c38f8p68D2nbHKRtc3h86d42A/eZHsOVMw9Y0nXKJmxDvyS1NA70z8Gz0qh5hNvbzpr6ZYMofzyiBUwLkOVdjfR/eVcao0dSl/d1aHx0GhHzEi0TXDnzAJ2mxtJpaixdp2yypM/0GLrcs5D3Y94ztNDsK7qjuxmzDBBz2rYGqZoVQc1b3dr4yfppk+g8eWeLd91aAxGPrqPbyKV0G7mUiEfXMWdz+nmQ0Jsgn1AbT/SkMXrkeZC6vK9DpU9d0S5I5bzLqf6gq6UV7WBn5q9+zDJBuEQ8us4SE6LLPQvpcs9CjmSW4ndlo1XPNBxWLiE34WbSX+wNapEBEsrfT/njERSO7WGBmDA1b3Wj9KkrSO/Xg1WjBjJl/CT+8sQ8a0BT/eGDhwN0uWchXe94ia07YkE+oSLc3gxyQt2yfiSrRg0E+YRqgRSO7UHh2B4UT7ragqmcdznFk67mp14ROO7txpTxk7AN/bLFgN1GLsU29EvrejiACdG59xQjKgu3GzVP9UwIvcmCVwYb102NmBHVBDFNVDUrgjP39yF98E0E5xox5Dcj5lsDhwOYQObg4dK59xR2RV8D4njEo/0NIEd3dkVfgy9t9HkfMTWSO6pXG63kjupF8aSrqXj+SoJzO1M573KmjJ/Eb0bM5y9PzGPBK4Mp3GKUEFvWj+Q3I+a3AOjcewp/eWKesUQ0T1mz2att7oSU9+F5EE2SqXvpSbKHdrIGNmHCoapmRVgh33LezZ3QNncyloGDnVnwyuA2IFvWj0Q+dplREzu6Wy0r9/KubVvg9pWRpPfrwZn7+1haMSHCxdSM/J4RWWufjiC9Xw/m9PgtN9w0uo1JbrhpNI0njAXTrAIbT/TEvb4LjdEj2641vqQjpPfrQfrgm1qYKHxKlz51BbmjerFj4G2WtAYwtWDKglcGG2ZoXrldu43AWDUrAmnbnLaRVZMayHvyPn7qZThoa38pfeoKap+OIDi3M6tGDeSGm0a3GTT82g03jeaGm0bj3H4d8rHLrN0I93LDpDsG3kb68si2a425hfZTrwjSB9/UBiZcM+YM6ghoyvhJpL/Ym+yFhknc67tYQVF+z3gjc3r8Fuf32zpOFTMeHXpRGDNfMYF2j+7PqlEDWTVqIOkv9rZ8SNvcCff6LlTOu9yK1Okv9mZOj9+S8ehQNKmBDhs17vxU9adeES1gwoHKH49oFyhcwhfKynmXWzOu4vkryR7aieyhnQjl7+84QzNNJGzbxN7uV1gw7WmntYZaLw2mmNdrn44ge2gnztzfx9od7zBnDa9t0pdHtgsTDhRustaaCndwEyLj0aG481PVS9r3FSUJj6eBrConMZHvnodpntrh2gkHCgcLl/TBN7G3+xXGLMlIo0LXjU7ixeoaUZIQ3C7OlTtJya8kJvJddgy8DctvWgGFaylcHPd2Y2/3K5jT47esGjWQrTtiyapy4nI6jUrvUmpfUytFRfmkZ6SxdUcs66dNYsfA2ywNtQBrJeb/dgy8jZjId/kx4YgF4fP6Ln1L3uyhhWvnSGYpOw6lEBP5LuunTWLDAw+x4YGHrAi74YGHWD9tEuunTSIm8l227ohtAyBK0i8/pNDagTVJxuf1YXf7OVfuJKvKMF16RhrpGWkcySwlJb+SrCqn1awRgjqaJP9nO0b/Zxo1v+ahS0ZqKJ9QCX5rJMyhN42aRj6h/udB5BKjiAp+i64uNrJ2M0Vs3rUiy4aU92G42X49iCYZDZjUMoX4ctFIcILfGgVU6E0LwEyCxKP98aWNxpc2GvFof+RjlyHlfdjxWnOxh93tJya5kIWHnDx2qJbnopP4NCmaYvtKC0LL6WYkQps70RA3laaUVbjzU1V7RRn2ijK8BbkWUJsM7VIAog7k8MyuPKtD1AJA/9zQQpYN9/oubFk/kpkLl7J4a0KbtrdZa/vSRrfMWS8GcSSzlGd25TH5VIjptTpR9T5SS+OMsrHZD3RHd7SDnTm1YwSzY2KsTtL46DSei07iSGZpm/tKeR8a5gnf0+vI8zfE5zAstpjptTrvifBJeeZ5LTQDkGXDtbsr0fte59mjDmaWaUyv1ZlZpvH3XJlRcQ6Grj5OTHJhy/t7VhrpwMVAog7kMCrOwcs+nZWaccak2L7S0oLpC6d2jGDJiUyWN8E6FVZqsLwJ5ruwYO5O9jFoUSIb4nPOT+/gtxf3kZjkQobFFreAaHRGGZoQbm+hhWd25fHsUQevHilgbo7bAmoNM2S/i6Grj3Mks9Tolcgn1Hb39MzHuXInw9edZrJd4z3xPISuLrYgCrf0ZOuOWKLzVFLLFDIKmlfr5EJmHMxhfoWvDczkUyELxl5RduFUUZNkIvdm8+BpkZd9eocQPyYc6XDnocpRQ+TebObmuFmptdTK5FMhBqwt4K1vMi4cWTMKnIyKczDZrvFJeWaHEBdrbVc5aphxMIflTR1rJaPA2TFI1IEc7k72tZwdYRCLtyZc6h4MMcmF7WrlwRSRAWsLiNyb3T6Iz+vjmV15jIpztIHwxN/I7JgY4svFS47CHk9DG62Y5hm4s5zx0Wntb0CnlikMiy3m06ToFpFSO9iZnSnbeGZXHkcyS8kocF6SHMksZc7m9AuaJyW/si3IltRaZsfEGNM09KZVs2bEzWV5EyzLlXn1SEG7MuNgTruy5JS73dlzd7IvPMi1BIlJLmRnyjbLJFawar7ZHi5NdrSS9jRyd7KPXlGnzQDXyjSlcYY2mk1SuKUnS05kslI7f9M9/HKgdaoh74nn/cR02NV7M9t2A9A/t/qf2uZOvB/zHvNdxk3Mm0bV+36VzK8wxHTWVutPmEbkE6q1hjQ3/yefCvGeeB7k1SPGlLsUeeubDOtnezJnczpvfZPBuXJnGEjzAqSri9FyulG4pSf3b/3OCvErNQNmxsEczpU70ST5kuWXJc9yiZXemQ3du5N9TK/VedmnW1qZm+M+v3r+gpTS42nA42nA5XRa4vE0hFd8zSDBb63cInvtAAYtSuTuZB+T7ZoFYy7tz+zK6+igQZtHRoGTyL3ZLab4M7vyGB+dxpAF+1i8NaEliLmWyNndsa+MZPi60/T/vpaJhTKT7ZqllZWaoZW3vsnA42m4IMS5cifPRScxN8fNeyK87NOZXqszsdDITa55/3i4dgVb0OPUTG2IR/vjzk9Vt6Qau5R3J/uYWCi3MJEJM2dzOkcyS80Q3WKrPia50IIIX2cmnwrxYIpIr6jTPBed1Mo0apFgpv0NcVMR3C5ESWLO5nS6fV3Ag6fFdmHmV/iYcTCHyL3ZRB3IsSRybzbP7MpjfoWvXYj+39cyZME+c7aEgTQ36smy0RA31dostrv9DF193IIJ9xcTxgSam+O2xAQwg9fMMo2JhTIPnjYgBi1KbC+RPq8REyR8iT9X7rRgWptpvssYLBwqHGB6rc7fc2ULYsh+F4MWJbLjUErH09c8ytcaxNTMCxtyGLC2oIUDT6/VO5TJdkMLJsTAneUMWpTYNotvE0eaj3rKxy6zun2t69mdKdt4fOley4lN35ls11pIOIC51D8XnWQu9xcGUQCteibyscuM5n31TKNqD5fm1H9DfA7PRScxdPVxhsUWMyy22Dq4MGS/i2GxxQxfd9oC2HEopb1WVcdtCU2Sqcv7OmTWpGbRLOV9SCh/P0GPUwvPvDIKnMQkFxK5N5s5m9N5LjqJ56KTeOubDFbvzSQlv7LN1P5FxzZ8Xp918v8SWk5WsWStLbr0a5oLHRdY/+GjPP8vtq7+0yCiJOHz+hDcLlxOJ2bzxeV0Irhdlk/9x0B8Xh9VjhoEt6s5rZTaFU1qQHC7qHLU/PpZ05EGqhw1uJxO0CVESSIlv5KoAznM2ZxufTJgzuZ0og7kkJJfaR1mcjmdVDlqflkc6ahSs1eUWdMzJrmQQYsSrYMJNy4raiHmYQWzD2IC2SvKLpa/dAzi8/qsc6cZBU6GLNjHlTMPcEtUJVMSdd45qRGdp7KxDOvDPu+c1JhxNMgtUZVcOfMAQxbss0K7vaLsQqbq+GCtCbEhPodOU2O58qNcZhwNsrMK4t0Xlp1VMONokCs/yqXT1FgrE7sATPvbJK0hblxWxDsnNWugvc7zcqFry3JlbomqbANzSdskpk9kFDjpOmWTpQnzne6sMgbbWWWYY8kpN0tOuYnOU1v8z9TcOyc1blxWRNcpmwwz6dLFjxr7vD5rY+eO13YSMS+Rh/co1iAby4wBluXKLDnl5rsCD1lVxk7FdwUelpxysyxXbvHcjWUwYb9CxLxE7nhtp7X10spELUHMMiHqQA6dJ+9k8KYaJh1u6ZRLTrnZklrb+hS3lURtSa1lySm39fyNZTAlUWfwpho6T95p1rqtS5LzICapJsmWNkbEBpiSqLMs1/gY3DsntfAuT4tDlkrYtci92bxzUmNjmaG9KYk6I2IDbbTStsBqjhma1EBKfiVdp2xiwNoCHt6jMOmwxjsnNev46KWUkaIksfCQk2W5Mu+c1Jh0WGPCfoUBawvoOmWT1d4Miy3nQczIuXpvJp2mxjJ4Uw0T9hsg09KM6fhcdBIxyYWXJM9FJzHjaJBpaTDpsAEzeFMNnabGGhVec+RtA1LlqAFd4vGley0Q8wZTEnWmpWGdWX3sUC3PHnW0K+b/n0qoZ1oaTEszfCQc5PGle0GXwv0k7PxI87S9EMjMMo35rvMdILPDbErrzlA4iOmw4SBh0/iXgUxLg8mnQvw9V2Zmmdau/D1XtpoxpiYe3qPw8B6FW6IqreOCvwpkWhqMinMwaFEi46PTfrFMXG38HLr6OHe8ttPykXZNYzrr4q0JdJoay4C1BS2cdfCmGuZsTrd6Hv/T5ozZJ7no9L1xWZE1fU0bD193unXx3GESFZNcyIb4nDazaUN8Dh6PkTy1O307CmgT9itM2K9YWnkuOumi26wTV6dZR43NXOXKj3LpPHknEY+us0DaDWiWnwCr92bSdcomBm+q4eE9ShsThTXh2jRn5mxOZ/CmmjYzZkRsgE5TY40Q33bhu/iiF66VcJjh604TuTfbUnnk3myGrzttQZgzZtJhzQrvfabHWGNccNELnz2tfSUcJjxADVhbwIC1BdYsMyOp+fyH9yhWGnAks/TS0gDTV4qK8q2NxU5TY7klqrIFTDhQ6+gZ/hwzdoSbpKgo/9LPj5hnR8yUwEwVw810MRkRG7BSRXPpLyrKv/RUsT2YI5mlLZLnEbEBK1q2lhGxASt5vuO1nZY5ioryL5TJX7icENwuioryjV1rr4+oAzkMWbDvouXEkAX7iDqQg8/rQ5MaLgZxaQWWJslWSWkWWBkFzl9UYP2PvgjFPNrj8/osM/2YcIQfE46QnpFmfL7K7SLocWpBj1Mz6+D0jLQWzzPb3b/6aI8SVnCbvXTTVOZxno6kqCjfKlPNUH4pIP9XPGz/N319UFnrf2iKLGi6LmggqCBoIOi6JuiqIqCrgqIrgqyrgoYu6JpiiK4LKgigCpquCCEdQdVVAU0VdP2iMGW29tplmtbcQNQ1QEXXNDQdQGsWHZBbvdQsKkTQfaiaBJrc/PyLPpQ2zqqbL9U10GV0TUbTZUCyQAoaJPaVinx5RmbVKZnVWRpf56r8WKlQFww2Q4bf8VdMXwsEtfkdGb97xSAb8yRG7df4zYYQ3deEsK2WsK1UsK1U6LIqxJWfKQzcEODVw0GS7KbG1F8Pout6C7WuL5Dpv1PBtlLEFgWXfyHTY61Ery91rvkiwLWfB7h6jcxV/5LoskLF9gl0+tjLI7FesuuxzKnrHeqneQdL143Bjacj6wqg4ZFUph8JYvusCdsXIldvhGvXi/T+SuS6dQrXrZO4fp3Ib76UuH5NiD6fi1z/mcgNnwa5epWMbbHG1StEvsoSjbeoq2i60h6MYNN1XTAhNF1vdlBoVFSG7/Nh+1Ti2o1Brl8v03uDyDVfN3DDVz5u+FKh15cKvdbp9FoHvT5X6PW5wjVr4LrPda6NkugTJdL1EwXbIpkVx5sdGaXZ8S9gGgNIJ6ipPHgghO3TED23h+ixTafXZpmb1ofos0ml+9dw1VcaV3wapMvKIF1WSVz+qULPzxV6faZw9Wc613yq0Xt1iN9Ehei+WMG2QObz03JHDtxsGk07P2XRmZ/hx7ZG5rqtMjdubqTHFonrNov8doPMZRvA9pmPqz8X+MNWhb/tkrg/VuGWaJXLPmmk85Imen6m0+sz6BMlcsNqP9etVujysU63jwIcrwy1N6UFm6Zrgma4KKBxrE7lyq999PnaT58dcMNWjV5bFa7d6sP2lcj/+szP6/FNHK2SqQtpSKqIKItUN2psyJH52yYXtkV+uq9UuP5fMj1XqVy9WuWGFSE6LQgxbHMQv6kVXW92B12wKZouSEjGNNMVJvwgYdugcGOsym+2q/TZqnD9dh3bVz5u3h4guVJtnpJa808zkJlBMMS7SQG6vB/gimUKvVdK9Fmu0nu5zLXLZGzvaWzLDhggmoysqwYIKoKqG+rKqVO5douP62JUfvutxg2xCn1iZTpv0rgpRuF0XQAIgRJElSUURUWWZWRZRpFlgrIKeIEg7yaC7X2FXkslei+XDVkmY1sQ4pFNDaA3hwcdNF0XbGjNZwNQWXZaxrZV5XexMjftFLnpW4ne34rYNvjZUywBQUJqEEkMoEk6oqIgySqipCCKEt6Qis8fRNEaAB+TtijYInV6Lwtx7VKRPstkIj5S6PGBRGFtwFCgApquCDYFTQANXZeZkiARsVPnlu9kfhcr0/cbiYivA4w94DM0oet4VQVJUQiJGiFRIiTKBEMSAX+QhoBIvU/C1SQCfpIKGrl8kZerFitcu0Tkuk9ErlsiYXtDYuMpYyobE0gVbIouC6DiDsgMiwtx406Z/rs0+u6WGPCNSI8tIZbnSoCCEvITkBRkWSMUkAgEJbz+EE2+IA3eAPUNjTR6fNTWSni9PuoFN/d8KtBpkcg1n3jp82GQ3h/6sc33seAHb/P6pYOmCTY0VQCNEkHhrgMhfrdL5k/fafT/XqT/boU+sRI/2r0AhESFYFDCF1TwBSWa/CE8TQHcjQFcDX6cdQGq63w43PWU1AoEmup4emMjtvl+enzop/d7Aa57N4Btvo/Z37jCHBzBpuqaAHDOHWDo/iD99in8+XuZO/er/H6fxsB/h0irDgGqoYGAhOAL0eALUd/oo87TRK2nCUddI3anQKXTTUl1DUVVNXga6nh2mwvb6066L3Bz3btOekU2YXtd5MVNDmuVVtEFm6brAmiUu4OMPODnjv0idx+UGHpQ4q6DEnf928+h0iCg0egN0OgXqW8MUCd4cXm81LgbqHIJlDs9lFd5KK90U1hWQ3GlgLOqlrs/rsQ2q45rFjq57q0yukc6sL3iYc62akBDR0fRNcGmq5oAQYSAyuQEibsPhnjgkMYD8T4ePOTnv/ZrfJrtBTWE4A3ibvRTJ3hx1jdRXddApbOeMoebEruL3Ao3p8vqOVVSR1JuDZkFtbywvgDb0zl0eqmanm+Wct2bFdhmlvP2Po/hH6qIrmiCTdNUAVVElTVeyfTz10My435UGHNE5JGfJIYf1ZiV4kFo8uILBKirD+LwBHC43Dhq6ymurqfAUU9ORS05RSU0NHmQVRW/JCMqOho6354U6DEri04z8+nxWim2fxSx8ZgLEAlJCqoiCzZZUwVZVECDjUVNDD8s8sRRlSmJOs8mwbPHZJ466iO2yI8aDNJU56a8tpGqaicOZx2FVfWcLa8lq7CMBn8IHfAGZQKSik/SQAoBOjEZtdiezqTTS/l0fzmPrFIBNB9CUCcoSYJN0TTBKymgS5TXBXn8pwCTj8lMT1WZmarx+nGR2Rk680+GOFleh9/bgMtZR3V1HYWVLvJKajiTV0pVjRsV8IVEgrLaLApeERSCAAx5/xS2+48zZvlZAmKIQFMTHq+PppAi2DRdFQIyyKIfRImoMz6ePO7lpUyJl08r/PN0iMjTEh9kS6zNEUgp92GvaaDAXstZh4DLG0JoChAMyviDMn5Jxi/K+EISIVnFr0h4JWPZ33a8mNteSCI6vhpZbMDhaqChyYfHHxRsmhYURBECoRDoMvkukVfTFN7IlHk7W+aDXIlVOSHW5ob4qhi2F4v8WNLIiSov5wLgkVVERSUYMqa2LyTjF1UCkkpQ1vGLImJAxCsai2SdKFJQ6aG0ooqK+gBuVxOCTxBsuq4IkqQSFCVCkgyqzg8lXt5J9/H+WViVJ7G+KMSOEoVdJSp77DJxdRrH3Rq5goLDJyMERRqCIt6QbPiHqBAQFSRJJSCrhGSZJklDUs/nIefsNRRXe3DWefE0NjUf21BURFEiGDRWVH9I5Nu8Rt7Pk/lXocbWIpFvKzT2VSr8YJdIcEqk1Svke2TsPhV3SMYTEmkISngDCr6QTFBSCUkqQUnFL2kEJUNLflFF1aGuyUepow6HuxG34DdyVkVRkCQFUVLxBWR0ScEfFPmuuIG1hTIxpSr/rpA46FBIqJHJdGmcqVPJa1Co9MrUBiTcQQlPQKYhoNAUUvCJCn5JJSApBCTZEr8oEVJU/IpKiaOOmnov9Q1+QyOqqiErGqKiIYk6/mAATQ4QalRItPvZU+EnvkrmxxqJRJdIVp1KTr1GQaNChVei2idTE9BwBRTqAzKeoEyjKNMkKvglhaCkNAMZogAeX4DS6npcDQE8jYHmM0aajqLqyLJOSNbwSTJev0woEKCxyU9OdZCEkgAJ1UGSBYWsBo3cRihq0qj0KVT5ZBwBjdqQRn1IRhBVGiWVRlklqOiIikZQ1hAV4ytjJE2n0ummqt6LU/AjNAYEm64jaBqoqo6iaEiKhiirBESVhkAQr9eH0ChSUu3nVGkdGY4mUmt8ZLoC5DWoFDUplHpVKnw6VT6ZWn+IuqCEJ6TQEFINzUgSflXFJ8nUe304XALVdQ3UNwaob/TT5A0ZILoO4TCyrBKSZHxBGcEfxNPgpdETwO32U+ZoIKesnrPlHrLtbn6urCfPXk+B3U2R3cO5qgbOVTVQUilwrkKgtEqguLKe4sp6yhwNlNg9VLkEhKYgjd4QTX6RYFA+X2Dpuo6maaiqiqqqKIqGKKn4QwrekERjIIC70YenMUBjk0S9EKK23our3ovb48Xj8SI0BfD4ROq9IdyNQeoa/Lg8AZxuPzV1PuobRASfguAN0egP4Q1KBEMykqwKNkAxMnpDNM1oSxhQGrKiI6oqTapIkyTiDYUIiDLBkEwoICOGjHghKxqKqqCoEooqEVJFgkqIkBIiKIsEpBB+MYA/FMAXkgiICiHRmK2KoilWo6bZRIKu61bjRdd1QdEQVBVBkzRBlVRBFhVBVTRBUXRBknVB1hAUECQQNF0XUHVB13RB0XRBVDRBUjRBUTVBUlRBlBRBlGQhJGuCJOuCouiCpuqCqqpl/7Eemqor5HnS2Ja/hPezpvCP1PuYlfo3vvo5EnfA0baH9qs+CKZpBIIh7DUuyuw1lNprqHDU4mnwoqoamq5xyn2YVTkv8cKJO3n+TH+eTB7Ao/H9eSr+TnbmrfyfgdiddZzKKaK0yklhuYN6oWVfvabay+6Tu3gzaSJPpPZm9E9XMmnvH1n60wKSanZypuEg35WuZlrCMLb9vPSXgzicdWTkFLX7vya5Dq/spk62s8v1AW+cu53ns29kSd6z/Fi9mZ/L8tpqVFfZeHYxBe7MSwdJy85v8Xd1oJwDFRtZlTeTD88+wcKsMSzMGsv8rL8wNbMnc7LuJN6xg6AcsF6TW1xBkzfQct9P8pDrSkfT1QuDKKrKz8UV1t+V3kKi89/m1YyhvHlyMPOz/ouFZ4fwYe59fJAzjLfO3s66wuep8p7jbF0iUTkzOe76/rzZ6jxUVteGtch06gL2C4PIikJFtcv6e3/ZeuamDOHNU//NivwxfFY8jnXlE/iyYiKflz/Eh4WD2Gv/CL/YQIJjI2+dvJvXTt7FtJS+LPt5OvVBY383KEoUlFaGzSz5wqb5ubC0WSsyG3PfZUbKnXzw8wOsKX6EdWUT+NI+nq8cY1nrGMnikjuJd0Xhld1sr3iTt37+IyuLHmZN0WMszxnPzLSBvJnxMMWNPxv7vUITLrdw8VlzMswnNud+xD+O3cGy3LF8ce5R1pZN4IuKsXzlGM0X1SP4uPJ2jgpraJAcfFb+CJHnbuOz8pF8UT6OL0om8nnRJFblPcrLaXfxxolROHzGd2idq7xIHBEavTQFQwAcLNvMP5Lu5JOcsawpmsRnJROIKnuYtVWjWVP9Vz6q7McRz0pUTSa2Zh6LSgeytOJPfGa/j3UVY1lTMoFPz01kdcEjLM95hNmp/8UHmU+j6MYnlrJyz3UMknHW0IbDW8rLyfexIGs4nxU8zqqi8Xx07gGiKkfyheN+ltnvJEFYGdYOFWlUqjniWcGK8iFElQ1jTek4Pi2awOqCR1iZ9wgfnx3Hs4l9+aHc+BqH2voGRFFqC+JpaEKSjOR2Y84iZqX8majcx1ieN57Xc+/hvXPD+aziAZaX30VGY0yH0/1s00E+KR7KquL7+ezceFbnT2BFzkSW5Uzg7VP38UbKQ3hCdc1aKWoLktHsG06/nbnJ9/H+6VGsyJnIC9l38kreMNaUPsKSkkHsdy26aABMcK3lw4L/5l9FY1mdP56lOeP55Ox4Psh+mOeT7+BAyUZj17O8qiWIKMkUlNoBOFQaw4zkQSw+M5bZp+7in7mPsKnkFVade4DPSsfTJNVeFCSk+lhbPIVl+Q+wMnccS8+OY/GZsXxwZjTTj9/OkqwXACi3O/H5A+dBKhy1lFQac33t2bf5R/KdvJnxFxadnkSyYzuf5j3BssIR/Kt4DBvLp/NF2dOsqXiSNRVPsKbyCeNnxZN8XjaFz4ufJrr4Bf5V8Agr8h5iWc5YPs4ey4enR/P+6YeYnfZn3kh9CAUfqgz2Gtd5kLOFpZTYjUMHH516jmlJA3jjxHCO2XexteBtFpwZyqqC0awo+huLCv7Eu4W38V7x73mvtD/vl/Xl/bJ+vFfye94tuo2F+X/g3dw/szT/b6zIHcMnZ0fz0ZmHWXT6ISKzRvJq5mBeSh5MSeNZyzyyrBggWTlFlNsNssiMKYz9oQe7i/9FmmM/r6bezZKfx7Is5yGW5f+NFYUjWHXuflaXDmN12V+JKhtGVNkwVpX9lZXFw1lRNILl+Q/ySc6DfHRmFIuyRhF5ciRvZ/6NNzPvZ3baIJ5N+AM/1xsfXcg9V47XH2wLMidpFE/9eAcVQg7Lsp7j9fShfHTmIT4+M4rIrKG8ljGAeSf78eaZfszP7sc/z/bln9n9mH+mH29m9eO1jP7MPfF7ZibfxvSE3zP1UD+eiruVxw/cxIT9fRj+764Mje3M6bqjAOQVl+MPhgyQvHPllFQapnkhfgRf5y7haNV3PJvwe945+QDvnnyAf2bcQ0zR22S7fySzbj+Z7n2cdO/jZP1eTtbvI9O9j8y6fWS49pHm3Edq9T6OV+0luXIPRyt2k1C+i/jybzhYupUfSrfjV40wX1zhQNN0A8RR66bEbjjr5p+Xc9IRz9snJvJ88h94O/N+3s64j1dS7mJLXuT/v0e/vT6qa93nnVXXdXLOlRtJi6qSWLmL8Yd682rGvcxLG8qbJ4byRuoQXj56L+UNuRcdoDHk5kDJNvaXbuZA2Rb2l21hX9nX7C3byNaCKJKr4pqnbw3+QLBlQDttxn4dPsh4hseP3sjcjP/m5dRBvJYymNdTBjMtvh8rT865KMja0wsZvqsr4/f3ZNyBnjx88CpGxV3BiAM2bt5iY8PPKwz/KKlsG1lDooTgCRJAYPKR/jyb2pcZaQOZdfyPzDn+J145/l/MSfojU364lW05yzuE2F30FU/80JcZSQN5+fifmH38Tmam3MGM1Dt4LOE6pv90DyHFCGLZ+SXtL3pn88rJCR5hbPy1TEq6jqnJv2XGsduZdfwPzD52By8n/5FZSX9g8sGbeDflGU7VHMUTqKMhVM/Z2hMsSZ/JY3G38I/E25l77I/MOv4HZhy/nRkptzE1+Rbu+beNhMrvjLEKSi+cj0T+8AaPZfTi2eQ/8Gj89fz96C3MSB7AjOTfMzPpNmYn3c7MowN4/IdrmXKoPy8l3MtLP/2Fpw7fxiMHr+HFxH7MTrqNmUm/56XkAbyY3I/pyb/jr/tsRJ542hqnOGydaRdkxv6J/DXBxvflX/Fd0Rru2W3jmYTrmZnUnxlJ/ZhxtB+zjg5g1tH+vJBwM1Pjr+fZ+Ot5PuFmZiX2Y9ZR43kvJfXlpeR+PJ90M3/da2Nm4gME5MZ2c5F2QV5OeYA/7rZxrOYgANE/f8S933ViTFxXZiX1ZfbRvsxK7MusxFuZnXgrs8JkZuKtzEi8lZlHf8espL48Gd+Lu3fbeDVpLA1BY+kvc7T7ZTktQUQlyLQjg/nzv20cyo+zrsdX7OKR/bcybLeNp368hpd+uok5ib9lbuKtzfI75ib+jtmJv2PGT7fwfMJveOj7zty/O4JPs+YjKsYUdTc04Wloav/YRusLz/04lAeTIsgsPENewfnc0is1EH32Qx47MICH913F+O//F+O/t/H4wW7877gIHtnfhXHfd2Hs91cyZl9v3k19lgLPaev15TV1NDR6Oz4/0vrC26ceYVhcL45X/GB4d2Eljf7Q+cJI9pHqiGPVqVeZd+wRZicOZ0bCvbyS9DAfpD3PnnNfUuO3ny9NVI2T+eVI8oVPGrUB2ZsfzX1HehJTtMK6FgyJZOYW0+gXf1EIz8wro9LhvKTn2lrugkMoFOS5n/7C0APXYK8tb3GepMrh5HB8Cmknz5JbXEpBSQVlFbVU2N0UlVWRW1RK1s95/JCQzMkzPyPLMpqm4ff7CQQChEIhJElCURQ0TcPsVOm6fn6tCT+oUOkq4bGE27n/qzv4KeMIwVCQQCBAbV0ttXW1VFRWkJ19lrS0DJKSj5F4NInk5OOcPHmK/Px8amtrcbvd1NTU4HQ6cbvdNDU1WTCyLKOqaguYDmvfgNzE4bIYdpWv4UT5EezuMkQl9B877PT/DQC7cLwx8LR3hQAAAABJRU5ErkJggg==) no-repeat;padding-left:40px}\n        .browser .browser-firefox{background-position:0 -34px}\n        .browser .browser-ie{background-position:0 -68px;margin-left:0px}\n        .browser .browser-360{background-position:0 -170px;margin-left: -27px}\n    </style>\n</head>\n<body style=\"margin-top:50px\">\n<h1>请升级您的浏览器，以便我们更好的为您提供服务！</h1>\n<p>您正在使用 Internet Explorer 的早期版本（IE11以下版本或使用该内核的浏览器）。这意味着在升级浏览器前，您将无法访问此网站。</p>\n<hr>\n<h2>请注意：微软公司对Windows XP 及 Internet Explorer 早期版本的支持已经结束</h2>\n<p>自 2016 年 1 月 12 日起，Microsoft 不再为 IE 11 以下版本提供相应支持和更新。没有关键的浏览器安全更新，您的电脑可能易受有害病毒、间谍软件和其他恶意软件的攻击，它们可以窃取或损害您的业务数据和信息。请参阅 <a href=\"https://www.microsoft.com/zh-cn/WindowsForBusiness/End-of-IE-support\">微软对 Internet Explorer 早期版本的支持将于 2016 年 1 月 12 日结束的说明</a> 。</p>\n<hr>\n<h2>您可以选择更先进的浏览器</h2>\n<p>推荐使用以下浏览器的最新版本。如果您的电脑已有以下浏览器的最新版本则直接使用该浏览器访问即可。</p>\n<ul class=\"browser\">\n    <li class=\"browser-chrome\"><a href=\"https://www.google.cn/chrome/browser/desktop/index.html?hl=zh-CN&standalone=1\"> 谷歌浏览器<span>Google Chrome</span></a></li>\n    <li class=\"browser-firefox\"><a href=\"https://www.mozilla.org/zh-CN/firefox/new/\"> 火狐浏览器<span>Mozilla Firefox</span></a></li>\n    <li class=\"browser-ie\"><a href=\"https://windows.microsoft.com/zh-cn/internet-explorer/download-ie\"> IE 11 浏览器<span>Internet Explorer</span></a></li>\n    <li class=\"browser-360\"><a href=\"http://se.360.cn/\"> 360安全浏览器<span>360 Chrome</span></a></li>\n    <div class=\"clean\"></div>\n</ul>\n<hr>\n</body>\n</html>"
  },
  {
    "path": "ruoyi-ui/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\">\n    <meta name=\"renderer\" content=\"webkit\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\">\n    <link rel=\"icon\" href=\"<%= BASE_URL %>favicon.ico\">\n    <title><%= webpackConfig.name %></title>\n    <!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->\n    <style>\n    html,\n    body,\n    #app {\n      height: 100%;\n      margin: 0px;\n      padding: 0px;\n    }\n    .chromeframe {\n      margin: 0.2em 0;\n      background: #ccc;\n      color: #000;\n      padding: 0.2em 0;\n    }\n\n    #loader-wrapper {\n      position: fixed;\n      top: 0;\n      left: 0;\n      width: 100%;\n      height: 100%;\n      z-index: 999999;\n    }\n\n    #loader {\n      display: block;\n      position: relative;\n      left: 50%;\n      top: 50%;\n      width: 150px;\n      height: 150px;\n      margin: -75px 0 0 -75px;\n      border-radius: 50%;\n      border: 3px solid transparent;\n      border-top-color: #FFF;\n      -webkit-animation: spin 2s linear infinite;\n      -ms-animation: spin 2s linear infinite;\n      -moz-animation: spin 2s linear infinite;\n      -o-animation: spin 2s linear infinite;\n      animation: spin 2s linear infinite;\n      z-index: 1001;\n    }\n\n    #loader:before {\n      content: \"\";\n      position: absolute;\n      top: 5px;\n      left: 5px;\n      right: 5px;\n      bottom: 5px;\n      border-radius: 50%;\n      border: 3px solid transparent;\n      border-top-color: #FFF;\n      -webkit-animation: spin 3s linear infinite;\n      -moz-animation: spin 3s linear infinite;\n      -o-animation: spin 3s linear infinite;\n      -ms-animation: spin 3s linear infinite;\n      animation: spin 3s linear infinite;\n    }\n\n    #loader:after {\n      content: \"\";\n      position: absolute;\n      top: 15px;\n      left: 15px;\n      right: 15px;\n      bottom: 15px;\n      border-radius: 50%;\n      border: 3px solid transparent;\n      border-top-color: #FFF;\n      -moz-animation: spin 1.5s linear infinite;\n      -o-animation: spin 1.5s linear infinite;\n      -ms-animation: spin 1.5s linear infinite;\n      -webkit-animation: spin 1.5s linear infinite;\n      animation: spin 1.5s linear infinite;\n    }\n\n\n    @-webkit-keyframes spin {\n      0% {\n        -webkit-transform: rotate(0deg);\n        -ms-transform: rotate(0deg);\n        transform: rotate(0deg);\n      }\n      100% {\n        -webkit-transform: rotate(360deg);\n        -ms-transform: rotate(360deg);\n        transform: rotate(360deg);\n      }\n    }\n\n    @keyframes spin {\n      0% {\n        -webkit-transform: rotate(0deg);\n        -ms-transform: rotate(0deg);\n        transform: rotate(0deg);\n      }\n      100% {\n        -webkit-transform: rotate(360deg);\n        -ms-transform: rotate(360deg);\n        transform: rotate(360deg);\n      }\n    }\n\n\n    #loader-wrapper .loader-section {\n      position: fixed;\n      top: 0;\n      width: 51%;\n      height: 100%;\n      background: #7171C6;\n      z-index: 1000;\n      -webkit-transform: translateX(0);\n      -ms-transform: translateX(0);\n      transform: translateX(0);\n    }\n\n    #loader-wrapper .loader-section.section-left {\n      left: 0;\n    }\n\n    #loader-wrapper .loader-section.section-right {\n      right: 0;\n    }\n\n\n    .loaded #loader-wrapper .loader-section.section-left {\n      -webkit-transform: translateX(-100%);\n      -ms-transform: translateX(-100%);\n      transform: translateX(-100%);\n      -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);\n      transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);\n    }\n\n    .loaded #loader-wrapper .loader-section.section-right {\n      -webkit-transform: translateX(100%);\n      -ms-transform: translateX(100%);\n      transform: translateX(100%);\n      -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);\n      transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);\n    }\n\n    .loaded #loader {\n      opacity: 0;\n      -webkit-transition: all 0.3s ease-out;\n      transition: all 0.3s ease-out;\n    }\n\n    .loaded #loader-wrapper {\n      visibility: hidden;\n      -webkit-transform: translateY(-100%);\n      -ms-transform: translateY(-100%);\n      transform: translateY(-100%);\n      -webkit-transition: all 0.3s 1s ease-out;\n      transition: all 0.3s 1s ease-out;\n    }\n\n    .no-js #loader-wrapper {\n      display: none;\n    }\n\n    .no-js h1 {\n      color: #222222;\n    }\n\n    #loader-wrapper .load_title {\n      font-family: 'Open Sans';\n      color: #FFF;\n      font-size: 19px;\n      width: 100%;\n      text-align: center;\n      z-index: 9999999999999;\n      position: absolute;\n      top: 60%;\n      opacity: 1;\n      line-height: 30px;\n    }\n\n    #loader-wrapper .load_title span {\n      font-weight: normal;\n      font-style: italic;\n      font-size: 13px;\n      color: #FFF;\n      opacity: 0.5;\n    }\n  </style>\n  </head>\n  <body>\n  <noscript>\n    <div class=\"global-site-notice noscript\">\n      <div class=\"notice-inner\">\n        <p>\n          <strong>JavaScript seems to be disabled in your browser.</strong><br />\n          You must have JavaScript enabled in your browser to utilize the functionality of this website. </p>\n      </div>\n    </div>\n  </noscript>\n    <div id=\"app\">\n        <div id=\"loader-wrapper\">\n            <div id=\"loader\"></div>\n            <div class=\"loader-section section-left\"></div>\n            <div class=\"loader-section section-right\"></div>\n            <div class=\"load_title\">正在加载系统资源，请耐心等待</div>\n        </div>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "ruoyi-ui/public/robots.txt",
    "content": "User-agent: *\nDisallow: /"
  },
  {
    "path": "ruoyi-ui/src/App.vue",
    "content": "<template>\n  <div id=\"app\">\n    <router-view/>\n    <theme-picker/>\n  </div>\n</template>\n\n<script>\nimport ThemePicker from \"@/components/ThemePicker\";\n\nexport default {\n  name: \"App\",\n  components: {ThemePicker},\n  metaInfo() {\n    return {\n      title: this.$store.state.settings.dynamicTitle && this.$store.state.settings.title,\n      titleTemplate: title => {\n        return title ? `${title} - ${process.env.VUE_APP_TITLE}` : process.env.VUE_APP_TITLE\n      }\n    }\n  }\n};\n</script>\n<style scoped>\n#app .theme-picker {\n  display: none;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/api/demo/demo.js",
    "content": "import request from '@/utils/request'\n\n// 查询测试单表列表\nexport function listDemo(query) {\n  return request({\n    url: '/demo/demo/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 自定义分页接口\nexport function pageDemo(query) {\n  return request({\n    url: '/demo/demo/page',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询测试单表详细\nexport function getDemo(id) {\n  return request({\n    url: '/demo/demo/' + id,\n    method: 'get'\n  })\n}\n\n// 新增测试单表\nexport function addDemo(data) {\n  return request({\n    url: '/demo/demo',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改测试单表\nexport function updateDemo(data) {\n  return request({\n    url: '/demo/demo',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除测试单表\nexport function delDemo(id) {\n  return request({\n    url: '/demo/demo/' + id,\n    method: 'delete'\n  })\n}\n\n"
  },
  {
    "path": "ruoyi-ui/src/api/demo/tree.js",
    "content": "import request from '@/utils/request'\n\n// 查询测试树表列表\nexport function listTree(query) {\n  return request({\n    url: '/demo/tree/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询测试树表详细\nexport function getTree(id) {\n  return request({\n    url: '/demo/tree/' + id,\n    method: 'get'\n  })\n}\n\n// 新增测试树表\nexport function addTree(data) {\n  return request({\n    url: '/demo/tree',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改测试树表\nexport function updateTree(data) {\n  return request({\n    url: '/demo/tree',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除测试树表\nexport function delTree(id) {\n  return request({\n    url: '/demo/tree/' + id,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/login.js",
    "content": "import request from '@/utils/request'\n\n// 登录方法\nexport function login(username, password, code, uuid) {\n  const data = {\n    username,\n    password,\n    code,\n    uuid\n  }\n  return request({\n    url: '/login',\n    headers: {\n      isToken: false\n    },\n    method: 'post',\n    data: data\n  })\n}\n\n// 注册方法\nexport function register(data) {\n  return request({\n    url: '/register',\n    headers: {\n      isToken: false\n    },\n    method: 'post',\n    data: data\n  })\n}\n\n// 获取用户详细信息\nexport function getInfo() {\n  return request({\n    url: '/getInfo',\n    method: 'get'\n  })\n}\n\n// 退出方法\nexport function logout() {\n  return request({\n    url: '/logout',\n    method: 'post'\n  })\n}\n\n// 获取验证码\nexport function getCodeImg() {\n  return request({\n    url: '/captchaImage',\n    headers: {\n      isToken: false\n    },\n    method: 'get',\n    timeout: 20000\n  })\n}\n\n// 短信验证码\nexport function getCodeSms() {\n  return request({\n    url: '/captchaSms',\n    headers: {\n      isToken: false\n    },\n    method: 'get',\n    timeout: 20000\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/menu.js",
    "content": "import request from '@/utils/request'\n\n// 获取路由\nexport const getRouters = () => {\n  return request({\n    url: '/getRouters',\n    method: 'get'\n  })\n}"
  },
  {
    "path": "ruoyi-ui/src/api/monitor/cache.js",
    "content": "import request from '@/utils/request'\n\n// 查询缓存详细\nexport function getCache() {\n  return request({\n    url: '/monitor/cache',\n    method: 'get'\n  })\n}\n\n// 查询缓存名称列表\nexport function listCacheName() {\n  return request({\n    url: '/monitor/cache/getNames',\n    method: 'get'\n  })\n}\n\n// 查询缓存键名列表\nexport function listCacheKey(cacheName) {\n  return request({\n    url: '/monitor/cache/getKeys/' + cacheName,\n    method: 'get'\n  })\n}\n\n// 查询缓存内容\nexport function getCacheValue(cacheName, cacheKey) {\n  return request({\n    url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey,\n    method: 'get'\n  })\n}\n\n// 清理指定名称缓存\nexport function clearCacheName(cacheName) {\n  return request({\n    url: '/monitor/cache/clearCacheName/' + cacheName,\n    method: 'delete'\n  })\n}\n\n// 清理指定键名缓存\nexport function clearCacheKey(cacheName, cacheKey) {\n  return request({\n    url: '/monitor/cache/clearCacheKey/'+ cacheName + \"/\" + cacheKey,\n    method: 'delete'\n  })\n}\n\n// 清理全部缓存\nexport function clearCacheAll() {\n  return request({\n    url: '/monitor/cache/clearCacheAll',\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/monitor/logininfor.js",
    "content": "import request from '@/utils/request'\n\n// 查询登录日志列表\nexport function list(query) {\n  return request({\n    url: '/monitor/logininfor/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 删除登录日志\nexport function delLogininfor(infoId) {\n  return request({\n    url: '/monitor/logininfor/' + infoId,\n    method: 'delete'\n  })\n}\n\n// 解锁用户登录状态\nexport function unlockLogininfor(userName) {\n  return request({\n    url: '/monitor/logininfor/unlock/' + userName,\n    method: 'get'\n  })\n}\n\n// 清空登录日志\nexport function cleanLogininfor() {\n  return request({\n    url: '/monitor/logininfor/clean',\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/monitor/online.js",
    "content": "import request from '@/utils/request'\n\n// 查询在线用户列表\nexport function list(query) {\n  return request({\n    url: '/monitor/online/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 强退用户\nexport function forceLogout(tokenId) {\n  return request({\n    url: '/monitor/online/' + tokenId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/monitor/operlog.js",
    "content": "import request from '@/utils/request'\n\n// 查询操作日志列表\nexport function list(query) {\n  return request({\n    url: '/monitor/operlog/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 删除操作日志\nexport function delOperlog(operId) {\n  return request({\n    url: '/monitor/operlog/' + operId,\n    method: 'delete'\n  })\n}\n\n// 清空操作日志\nexport function cleanOperlog() {\n  return request({\n    url: '/monitor/operlog/clean',\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/activity.js",
    "content": "import request from '@/utils/request'\n\n// 查询活动列表\nexport function listActivity(query) {\n  return request({\n    url: '/system/activity/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询活动详细\nexport function getActivity(activityId) {\n  return request({\n    url: '/system/activity/' + activityId,\n    method: 'get'\n  })\n}\n\n// 新增活动\nexport function addActivity(data) {\n  return request({\n    url: '/system/activity',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改活动\nexport function updateActivity(data) {\n  return request({\n    url: '/system/activity',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除活动\nexport function delActivity(activityId) {\n  return request({\n    url: '/system/activity/' + activityId,\n    method: 'delete'\n  })\n}\n\n// 获取地址列表\nexport function getAddressList() {\n  return request({\n    url: '/system/region/list',\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/activityConnArtist.js",
    "content": "import request from '@/utils/request'\n\n// 查询活动关联艺人列表\nexport function listActivityConnArtist(query) {\n  return request({\n    url: '/system/activityConnArtist/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询活动关联艺人详细\nexport function getActivityConnArtist(activityConnArtistId) {\n  return request({\n    url: '/system/activityConnArtist/' + activityConnArtistId,\n    method: 'get'\n  })\n}\n\n// 新增活动关联艺人\nexport function addActivityConnArtist(data) {\n  return request({\n    url: '/system/activityConnArtist',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改活动关联艺人\nexport function updateActivityConnArtist(data) {\n  return request({\n    url: '/system/activityConnArtist',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除活动关联艺人\nexport function delActivityConnArtist(activityConnArtistId) {\n  return request({\n    url: '/system/activityConnArtist/' + activityConnArtistId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/activityConnIntro.js",
    "content": "import request from '@/utils/request'\n\n// 查询活动介绍与活动关联列表\nexport function listActivityConnIntro(query) {\n  return request({\n    url: '/system/activityConnIntro/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询活动介绍与活动关联详细\nexport function getActivityConnIntro(activityConnIntroId) {\n  return request({\n    url: '/system/activityConnIntro/' + activityConnIntroId,\n    method: 'get'\n  })\n}\n\n// 新增活动介绍与活动关联\nexport function addActivityConnIntro(data) {\n  return request({\n    url: '/system/activityConnIntro',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改活动介绍与活动关联\nexport function updateActivityConnIntro(data) {\n  return request({\n    url: '/system/activityConnIntro',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除活动介绍与活动关联\nexport function delActivityConnIntro(activityConnIntroId) {\n  return request({\n    url: '/system/activityConnIntro/' + activityConnIntroId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/activityConnTag.js",
    "content": "import request from '@/utils/request'\n\n// 查询活动标签与活动关联列表\nexport function listActivityConnTag(query) {\n  return request({\n    url: '/system/activityConnTag/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询活动标签与活动关联详细\nexport function getActivityConnTag(activityConnTagId) {\n  return request({\n    url: '/system/activityConnTag/' + activityConnTagId,\n    method: 'get'\n  })\n}\n\n// 新增活动标签与活动关联\nexport function addActivityConnTag(data) {\n  return request({\n    url: '/system/activityConnTag',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改活动标签与活动关联\nexport function updateActivityConnTag(data) {\n  return request({\n    url: '/system/activityConnTag',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除活动标签与活动关联\nexport function delActivityConnTag(activityConnTagId) {\n  return request({\n    url: '/system/activityConnTag/' + activityConnTagId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/activityGroup.js",
    "content": "import request from '@/utils/request'\n\n// 查询活动组队列表\nexport function listActivityGroup(query) {\n  return request({\n    url: '/system/activityGroup/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询活动组队详细\nexport function getActivityGroup(groupId) {\n  return request({\n    url: '/system/activityGroup/' + groupId,\n    method: 'get'\n  })\n}\n\n// 新增活动组队\nexport function addActivityGroup(data) {\n  return request({\n    url: '/system/activityGroup',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改活动组队\nexport function updateActivityGroup(data) {\n  return request({\n    url: '/system/activityGroup',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除活动组队\nexport function delActivityGroup(groupId) {\n  return request({\n    url: '/system/activityGroup/' + groupId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/activityGroupApply.js",
    "content": "import request from '@/utils/request'\n\n// 查询活动组队申请列列表\nexport function listActivityGroupApply(query) {\n  return request({\n    url: '/system/activityGroupApply/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询活动组队申请列详细\nexport function getActivityGroupApply(applyId) {\n  return request({\n    url: '/system/activityGroupApply/' + applyId,\n    method: 'get'\n  })\n}\n\n// 新增活动组队申请列\nexport function addActivityGroupApply(data) {\n  return request({\n    url: '/system/activityGroupApply',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改活动组队申请列\nexport function updateActivityGroupApply(data) {\n  return request({\n    url: '/system/activityGroupApply',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除活动组队申请列\nexport function delActivityGroupApply(applyId) {\n  return request({\n    url: '/system/activityGroupApply/' + applyId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/artist.js",
    "content": "import request from '@/utils/request'\n\n// 查询艺人列表\nexport function listArtist(query) {\n  return request({\n    url: '/system/artist/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询艺人详细\nexport function getArtist(artistId) {\n  return request({\n    url: '/system/artist/' + artistId,\n    method: 'get'\n  })\n}\n\n// 新增艺人\nexport function addArtist(data) {\n  return request({\n    url: '/system/artist',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改艺人\nexport function updateArtist(data) {\n  return request({\n    url: '/system/artist',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除艺人\nexport function delArtist(artistId) {\n  return request({\n    url: '/system/artist/' + artistId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/config.js",
    "content": "import request from '@/utils/request'\n\n// 查询参数列表\nexport function listConfig(query) {\n  return request({\n    url: '/system/config/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询参数详细\nexport function getConfig(configId) {\n  return request({\n    url: '/system/config/' + configId,\n    method: 'get'\n  })\n}\n\n// 根据参数键名查询参数值\nexport function getConfigKey(configKey) {\n  return request({\n    url: '/system/config/configKey/' + configKey,\n    method: 'get'\n  })\n}\n\n// 新增参数配置\nexport function addConfig(data) {\n  return request({\n    url: '/system/config',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改参数配置\nexport function updateConfig(data) {\n  return request({\n    url: '/system/config',\n    method: 'put',\n    data: data\n  })\n}\n\n// 修改参数配置\nexport function updateConfigByKey(key, value) {\n  return request({\n    url: '/system/config/updateByKey',\n    method: 'put',\n    data: {\n      configKey: key,\n      configValue: value\n    }\n  })\n}\n\n// 删除参数配置\nexport function delConfig(configId) {\n  return request({\n    url: '/system/config/' + configId,\n    method: 'delete'\n  })\n}\n\n// 刷新参数缓存\nexport function refreshCache() {\n  return request({\n    url: '/system/config/refreshCache',\n    method: 'delete'\n  })\n}\n\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/dept.js",
    "content": "import request from '@/utils/request'\n\n// 查询部门列表\nexport function listDept(query) {\n  return request({\n    url: '/system/dept/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询部门列表（排除节点）\nexport function listDeptExcludeChild(deptId) {\n  return request({\n    url: '/system/dept/list/exclude/' + deptId,\n    method: 'get'\n  })\n}\n\n// 查询部门详细\nexport function getDept(deptId) {\n  return request({\n    url: '/system/dept/' + deptId,\n    method: 'get'\n  })\n}\n\n// 新增部门\nexport function addDept(data) {\n  return request({\n    url: '/system/dept',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改部门\nexport function updateDept(data) {\n  return request({\n    url: '/system/dept',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除部门\nexport function delDept(deptId) {\n  return request({\n    url: '/system/dept/' + deptId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/dict/data.js",
    "content": "import request from '@/utils/request'\n\n// 查询字典数据列表\nexport function listData(query) {\n  return request({\n    url: '/system/dict/data/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询字典数据详细\nexport function getData(dictCode) {\n  return request({\n    url: '/system/dict/data/' + dictCode,\n    method: 'get'\n  })\n}\n\n// 根据字典类型查询字典数据信息\nexport function getDicts(dictType) {\n  return request({\n    url: '/system/dict/data/type/' + dictType,\n    method: 'get'\n  })\n}\n\n// 新增字典数据\nexport function addData(data) {\n  return request({\n    url: '/system/dict/data',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改字典数据\nexport function updateData(data) {\n  return request({\n    url: '/system/dict/data',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除字典数据\nexport function delData(dictCode) {\n  return request({\n    url: '/system/dict/data/' + dictCode,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/dict/type.js",
    "content": "import request from '@/utils/request'\n\n// 查询字典类型列表\nexport function listType(query) {\n  return request({\n    url: '/system/dict/type/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询字典类型详细\nexport function getType(dictId) {\n  return request({\n    url: '/system/dict/type/' + dictId,\n    method: 'get'\n  })\n}\n\n// 新增字典类型\nexport function addType(data) {\n  return request({\n    url: '/system/dict/type',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改字典类型\nexport function updateType(data) {\n  return request({\n    url: '/system/dict/type',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除字典类型\nexport function delType(dictId) {\n  return request({\n    url: '/system/dict/type/' + dictId,\n    method: 'delete'\n  })\n}\n\n// 刷新字典缓存\nexport function refreshCache() {\n  return request({\n    url: '/system/dict/type/refreshCache',\n    method: 'delete'\n  })\n}\n\n// 获取字典选择框列表\nexport function optionselect() {\n  return request({\n    url: '/system/dict/type/optionselect',\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/intro.js",
    "content": "import request from '@/utils/request'\n\n// 查询活动介绍列表\nexport function listIntro(query) {\n  return request({\n    url: '/system/intro/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询活动介绍详细\nexport function getIntro(introId) {\n  return request({\n    url: '/system/intro/' + introId,\n    method: 'get'\n  })\n}\n\n// 新增活动介绍\nexport function addIntro(data) {\n  return request({\n    url: '/system/intro',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改活动介绍\nexport function updateIntro(data) {\n  return request({\n    url: '/system/intro',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除活动介绍\nexport function delIntro(introId) {\n  return request({\n    url: '/system/intro/' + introId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/menu.js",
    "content": "import request from '@/utils/request'\n\n// 查询菜单列表\nexport function listMenu(query) {\n  return request({\n    url: '/system/menu/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询菜单详细\nexport function getMenu(menuId) {\n  return request({\n    url: '/system/menu/' + menuId,\n    method: 'get'\n  })\n}\n\n// 查询菜单下拉树结构\nexport function treeselect() {\n  return request({\n    url: '/system/menu/treeselect',\n    method: 'get'\n  })\n}\n\n// 根据角色ID查询菜单下拉树结构\nexport function roleMenuTreeselect(roleId) {\n  return request({\n    url: '/system/menu/roleMenuTreeselect/' + roleId,\n    method: 'get'\n  })\n}\n\n// 新增菜单\nexport function addMenu(data) {\n  return request({\n    url: '/system/menu',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改菜单\nexport function updateMenu(data) {\n  return request({\n    url: '/system/menu',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除菜单\nexport function delMenu(menuId) {\n  return request({\n    url: '/system/menu/' + menuId,\n    method: 'delete'\n  })\n}"
  },
  {
    "path": "ruoyi-ui/src/api/system/notice.js",
    "content": "import request from '@/utils/request'\n\n// 查询公告列表\nexport function listNotice(query) {\n  return request({\n    url: '/system/notice/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询公告详细\nexport function getNotice(noticeId) {\n  return request({\n    url: '/system/notice/' + noticeId,\n    method: 'get'\n  })\n}\n\n// 新增公告\nexport function addNotice(data) {\n  return request({\n    url: '/system/notice',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改公告\nexport function updateNotice(data) {\n  return request({\n    url: '/system/notice',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除公告\nexport function delNotice(noticeId) {\n  return request({\n    url: '/system/notice/' + noticeId,\n    method: 'delete'\n  })\n}"
  },
  {
    "path": "ruoyi-ui/src/api/system/official.js",
    "content": "import request from '@/utils/request'\n\n// 查询官方消息列表\nexport function listOfficial(query) {\n  return request({\n    url: '/system/official/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询官方消息详细\nexport function getOfficial(officialId) {\n  return request({\n    url: '/system/official/' + officialId,\n    method: 'get'\n  })\n}\n\n// 新增官方消息\nexport function addOfficial(data) {\n  return request({\n    url: '/system/official',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改官方消息\nexport function updateOfficial(data) {\n  return request({\n    url: '/system/official',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除官方消息\nexport function delOfficial(officialId) {\n  return request({\n    url: '/system/official/' + officialId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/organizer.js",
    "content": "import request from '@/utils/request'\n\n// 查询活动主办方列表\nexport function listOrganizer(query) {\n  return request({\n    url: '/system/organizer/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询活动主办方详细\nexport function getOrganizer(organizerId) {\n  return request({\n    url: '/system/organizer/' + organizerId,\n    method: 'get'\n  })\n}\n\n// 新增活动主办方\nexport function addOrganizer(data) {\n  return request({\n    url: '/system/organizer',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改活动主办方\nexport function updateOrganizer(data) {\n  return request({\n    url: '/system/organizer',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除活动主办方\nexport function delOrganizer(organizerId) {\n  return request({\n    url: '/system/organizer/' + organizerId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/organizerTicket.js",
    "content": "import request from '@/utils/request'\n\n// 查询主办方票务列表\nexport function listOrganizerTicket(query) {\n  return request({\n    url: '/system/organizerTicket/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询主办方票务详细\nexport function getOrganizerTicket(organizerTicketId) {\n  return request({\n    url: '/system/organizerTicket/' + organizerTicketId,\n    method: 'get'\n  })\n}\n\n// 新增主办方票务\nexport function addOrganizerTicket(data) {\n  return request({\n    url: '/system/organizerTicket',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改主办方票务\nexport function updateOrganizerTicket(data) {\n  return request({\n    url: '/system/organizerTicket',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除主办方票务\nexport function delOrganizerTicket(organizerTicketId) {\n  return request({\n    url: '/system/organizerTicket/' + organizerTicketId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/oss.js",
    "content": "import request from '@/utils/request'\n\n// 查询OSS对象存储列表\nexport function listOss(query) {\n  return request({\n    url: '/system/oss/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询OSS对象基于id串\nexport function listByIds(ossId) {\n  return request({\n    url: '/system/oss/listByIds/' + ossId,\n    method: 'get'\n  })\n}\n\n// 删除OSS对象存储\nexport function delOss(ossId) {\n  return request({\n    url: '/system/oss/' + ossId,\n    method: 'delete'\n  })\n}\n\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/ossConfig.js",
    "content": "import request from '@/utils/request'\n\n// 查询对象存储配置列表\nexport function listOssConfig(query) {\n  return request({\n    url: '/system/oss/config/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询对象存储配置详细\nexport function getOssConfig(ossConfigId) {\n  return request({\n    url: '/system/oss/config/' + ossConfigId,\n    method: 'get'\n  })\n}\n\n// 新增对象存储配置\nexport function addOssConfig(data) {\n  return request({\n    url: '/system/oss/config',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改对象存储配置\nexport function updateOssConfig(data) {\n  return request({\n    url: '/system/oss/config',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除对象存储配置\nexport function delOssConfig(ossConfigId) {\n  return request({\n    url: '/system/oss/config/' + ossConfigId,\n    method: 'delete'\n  })\n}\n\n// 对象存储状态修改\nexport function changeOssConfigStatus(ossConfigId, status, configKey) {\n  const data = {\n    ossConfigId,\n    status,\n    configKey\n  }\n  return request({\n    url: '/system/oss/config/changeStatus',\n    method: 'put',\n    data: data\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/post.js",
    "content": "import request from '@/utils/request'\n\n// 查询岗位列表\nexport function listPost(query) {\n  return request({\n    url: '/system/post/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询岗位详细\nexport function getPost(postId) {\n  return request({\n    url: '/system/post/' + postId,\n    method: 'get'\n  })\n}\n\n// 新增岗位\nexport function addPost(data) {\n  return request({\n    url: '/system/post',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改岗位\nexport function updatePost(data) {\n  return request({\n    url: '/system/post',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除岗位\nexport function delPost(postId) {\n  return request({\n    url: '/system/post/' + postId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/pzc_order.js",
    "content": "import request from '@/utils/request'\n\n// 查询订单列表\nexport function listPzc_order(query) {\n  return request({\n    url: '/system/pzc_order/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询订单详细\nexport function getPzc_order(orderId) {\n  return request({\n    url: '/system/pzc_order/' + orderId,\n    method: 'get'\n  })\n}\n\n// 新增订单\nexport function addPzc_order(data) {\n  return request({\n    url: '/system/pzc_order',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改订单\nexport function updatePzc_order(data) {\n  return request({\n    url: '/system/pzc_order',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除订单\nexport function delPzc_order(orderId) {\n  return request({\n    url: '/system/pzc_order/' + orderId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/pzc_user.js",
    "content": "import request from '@/utils/request'\n\n// 查询用户列表\nexport function listPzc_user(query) {\n  return request({\n    url: '/system/pzc_user/list',\n    method: 'get',\n    params: query\n  })\n}\n\n//更新用户余额\nexport function update_money(data) {\n  return request({\n    url: '/system/pzc_user/updateMoney',\n    method: 'post',\n    data: data\n  })\n}\n\n// 查询用户详细\nexport function getPzc_user(userId) {\n  return request({\n    url: '/system/pzc_user/' + userId,\n    method: 'get'\n  })\n}\n\n// 新增用户\nexport function addPzc_user(data) {\n  return request({\n    url: '/system/pzc_user',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改用户\nexport function updatePzc_user(data) {\n  return request({\n    url: '/system/pzc_user',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除用户\nexport function delPzc_user(userId) {\n  return request({\n    url: '/system/pzc_user/' + userId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/region.js",
    "content": "import request from '@/utils/request'\n\n// 查询地区列表\nexport function listRegion(query) {\n  return request({\n    url: '/system/region/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询地区详细\nexport function getRegion(regionId) {\n  return request({\n    url: '/system/region/' + regionId,\n    method: 'get'\n  })\n}\n\n// 新增地区\nexport function addRegion(data) {\n  return request({\n    url: '/system/region',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改地区\nexport function updateRegion(data) {\n  return request({\n    url: '/system/region',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除地区\nexport function delRegion(regionId) {\n  return request({\n    url: '/system/region/' + regionId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/role.js",
    "content": "import request from '@/utils/request'\n\n// 查询角色列表\nexport function listRole(query) {\n  return request({\n    url: '/system/role/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询角色详细\nexport function getRole(roleId) {\n  return request({\n    url: '/system/role/' + roleId,\n    method: 'get'\n  })\n}\n\n// 新增角色\nexport function addRole(data) {\n  return request({\n    url: '/system/role',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改角色\nexport function updateRole(data) {\n  return request({\n    url: '/system/role',\n    method: 'put',\n    data: data\n  })\n}\n\n// 角色数据权限\nexport function dataScope(data) {\n  return request({\n    url: '/system/role/dataScope',\n    method: 'put',\n    data: data\n  })\n}\n\n// 角色状态修改\nexport function changeRoleStatus(roleId, status) {\n  const data = {\n    roleId,\n    status\n  }\n  return request({\n    url: '/system/role/changeStatus',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除角色\nexport function delRole(roleId) {\n  return request({\n    url: '/system/role/' + roleId,\n    method: 'delete'\n  })\n}\n\n// 查询角色已授权用户列表\nexport function allocatedUserList(query) {\n  return request({\n    url: '/system/role/authUser/allocatedList',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询角色未授权用户列表\nexport function unallocatedUserList(query) {\n  return request({\n    url: '/system/role/authUser/unallocatedList',\n    method: 'get',\n    params: query\n  })\n}\n\n// 取消用户授权角色\nexport function authUserCancel(data) {\n  return request({\n    url: '/system/role/authUser/cancel',\n    method: 'put',\n    data: data\n  })\n}\n\n// 批量取消用户授权角色\nexport function authUserCancelAll(data) {\n  return request({\n    url: '/system/role/authUser/cancelAll',\n    method: 'put',\n    params: data\n  })\n}\n\n// 授权用户选择\nexport function authUserSelectAll(data) {\n  return request({\n    url: '/system/role/authUser/selectAll',\n    method: 'put',\n    params: data\n  })\n}\n\n// 根据角色ID查询部门树结构\nexport function deptTreeSelect(roleId) {\n  return request({\n    url: '/system/role/deptTree/' + roleId,\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/tag.js",
    "content": "import request from '@/utils/request'\n\n// 查询活动标签列表\nexport function listTag(query) {\n  return request({\n    url: '/system/tag/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询活动标签详细\nexport function getTag(tagId) {\n  return request({\n    url: '/system/tag/' + tagId,\n    method: 'get'\n  })\n}\n\n// 新增活动标签\nexport function addTag(data) {\n  return request({\n    url: '/system/tag',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改活动标签\nexport function updateTag(data) {\n  return request({\n    url: '/system/tag',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除活动标签\nexport function delTag(tagId) {\n  return request({\n    url: '/system/tag/' + tagId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/user.js",
    "content": "import request from '@/utils/request'\nimport { parseStrEmpty } from \"@/utils/ruoyi\";\n\n// 查询用户列表\nexport function listUser(query) {\n  return request({\n    url: '/system/user/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询用户详细\nexport function getUser(userId) {\n  return request({\n    url: '/system/user/' + parseStrEmpty(userId),\n    method: 'get'\n  })\n}\n\n// 新增用户\nexport function addUser(data) {\n  return request({\n    url: '/system/user',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改用户\nexport function updateUser(data) {\n  return request({\n    url: '/system/user',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除用户\nexport function delUser(userId) {\n  return request({\n    url: '/system/user/' + userId,\n    method: 'delete'\n  })\n}\n\n// 用户密码重置\nexport function resetUserPwd(userId, password) {\n  const data = {\n    userId,\n    password\n  }\n  return request({\n    url: '/system/user/resetPwd',\n    method: 'put',\n    data: data\n  })\n}\n\n// 用户状态修改\nexport function changeUserStatus(userId, status) {\n  const data = {\n    userId,\n    status\n  }\n  return request({\n    url: '/system/user/changeStatus',\n    method: 'put',\n    data: data\n  })\n}\n\n// 查询用户个人信息\nexport function getUserProfile() {\n  return request({\n    url: '/system/user/profile',\n    method: 'get'\n  })\n}\n\n// 修改用户个人信息\nexport function updateUserProfile(data) {\n  return request({\n    url: '/system/user/profile',\n    method: 'put',\n    data: data\n  })\n}\n\n// 用户密码重置\nexport function updateUserPwd(oldPassword, newPassword) {\n  const data = {\n    oldPassword,\n    newPassword\n  }\n  return request({\n    url: '/system/user/profile/updatePwd',\n    method: 'put',\n    params: data\n  })\n}\n\n// 用户头像上传\nexport function uploadAvatar(data) {\n  return request({\n    url: '/system/user/profile/avatar',\n    method: 'post',\n    data: data\n  })\n}\n\n// 查询授权角色\nexport function getAuthRole(userId) {\n  return request({\n    url: '/system/user/authRole/' + userId,\n    method: 'get'\n  })\n}\n\n// 保存授权角色\nexport function updateAuthRole(data) {\n  return request({\n    url: '/system/user/authRole',\n    method: 'put',\n    params: data\n  })\n}\n\n// 查询部门下拉树结构\nexport function deptTreeSelect() {\n  return request({\n    url: '/system/user/deptTree',\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/userCollect.js",
    "content": "import request from '@/utils/request'\n\n// 查询用户收藏活动列表\nexport function listUserCollect(query) {\n  return request({\n    url: '/system/userCollect/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询用户收藏活动详细\nexport function getUserCollect(collectId) {\n  return request({\n    url: '/system/userCollect/' + collectId,\n    method: 'get'\n  })\n}\n\n// 新增用户收藏活动\nexport function addUserCollect(data) {\n  return request({\n    url: '/system/userCollect',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改用户收藏活动\nexport function updateUserCollect(data) {\n  return request({\n    url: '/system/userCollect',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除用户收藏活动\nexport function delUserCollect(collectId) {\n  return request({\n    url: '/system/userCollect/' + collectId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/userHistory.js",
    "content": "import request from '@/utils/request'\n\n// 查询用户操作历史记录列表\nexport function listUserHistory(query) {\n  return request({\n    url: '/system/userHistory/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询用户操作历史记录详细\nexport function getUserHistory(historyId) {\n  return request({\n    url: '/system/userHistory/' + historyId,\n    method: 'get'\n  })\n}\n\n// 新增用户操作历史记录\nexport function addUserHistory(data) {\n  return request({\n    url: '/system/userHistory',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改用户操作历史记录\nexport function updateUserHistory(data) {\n  return request({\n    url: '/system/userHistory',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除用户操作历史记录\nexport function delUserHistory(historyId) {\n  return request({\n    url: '/system/userHistory/' + historyId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/userPhoto.js",
    "content": "import request from '@/utils/request'\n\n// 查询用户资料相册列表\nexport function listUserPhoto(query) {\n  return request({\n    url: '/system/userPhoto/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询用户资料相册详细\nexport function getUserPhoto(photoId) {\n  return request({\n    url: '/system/userPhoto/' + photoId,\n    method: 'get'\n  })\n}\n\n// 新增用户资料相册\nexport function addUserPhoto(data) {\n  return request({\n    url: '/system/userPhoto',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改用户资料相册\nexport function updateUserPhoto(data) {\n  return request({\n    url: '/system/userPhoto',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除用户资料相册\nexport function delUserPhoto(photoId) {\n  return request({\n    url: '/system/userPhoto/' + photoId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/userTalk.js",
    "content": "import request from '@/utils/request'\n\n// 查询用户聊天列表\nexport function listUserTalk(query) {\n  return request({\n    url: '/system/userTalk/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询用户聊天详细\nexport function getUserTalk(talkId) {\n  return request({\n    url: '/system/userTalk/' + talkId,\n    method: 'get'\n  })\n}\n\n// 新增用户聊天\nexport function addUserTalk(data) {\n  return request({\n    url: '/system/userTalk',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改用户聊天\nexport function updateUserTalk(data) {\n  return request({\n    url: '/system/userTalk',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除用户聊天\nexport function delUserTalk(talkId) {\n  return request({\n    url: '/system/userTalk/' + talkId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/system/viewPager.js",
    "content": "import request from '@/utils/request'\n\n// 查询轮播图列表\nexport function listViewPager(query) {\n  return request({\n    url: '/system/viewPager/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询轮播图详细\nexport function getViewPager(viewPagerId) {\n  return request({\n    url: '/system/viewPager/' + viewPagerId,\n    method: 'get'\n  })\n}\n\n// 新增轮播图\nexport function addViewPager(data) {\n  return request({\n    url: '/system/viewPager',\n    method: 'post',\n    data: data\n  })\n}\n\n// 修改轮播图\nexport function updateViewPager(data) {\n  return request({\n    url: '/system/viewPager',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除轮播图\nexport function delViewPager(viewPagerId) {\n  return request({\n    url: '/system/viewPager/' + viewPagerId,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/api/tool/gen.js",
    "content": "import request from '@/utils/request'\n\n// 查询生成表数据\nexport function listTable(query) {\n  return request({\n    headers: { 'datasource': localStorage.getItem(\"dataName\") },\n    url: '/tool/gen/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询db数据库列表\nexport function listDbTable(query) {\n  return request({\n    headers: { 'datasource': localStorage.getItem(\"dataName\") },\n    url: '/tool/gen/db/list',\n    method: 'get',\n    params: query\n  })\n}\n\n// 查询表详细信息\nexport function getGenTable(tableId) {\n  return request({\n    headers: { 'datasource': localStorage.getItem(\"dataName\") },\n    url: '/tool/gen/' + tableId,\n    method: 'get'\n  })\n}\n\n// 修改代码生成信息\nexport function updateGenTable(data) {\n  return request({\n    headers: { 'datasource': localStorage.getItem(\"dataName\") },\n    url: '/tool/gen',\n    method: 'put',\n    data: data\n  })\n}\n\n// 导入表\nexport function importTable(data) {\n  return request({\n    headers: { 'datasource': localStorage.getItem(\"dataName\") },\n    url: '/tool/gen/importTable',\n    method: 'post',\n    params: data\n  })\n}\n\n// 预览生成代码\nexport function previewTable(tableId) {\n  return request({\n    headers: { 'datasource': localStorage.getItem(\"dataName\") },\n    url: '/tool/gen/preview/' + tableId,\n    method: 'get'\n  })\n}\n\n// 删除表数据\nexport function delTable(tableId) {\n  return request({\n    headers: { 'datasource': localStorage.getItem(\"dataName\") },\n    url: '/tool/gen/' + tableId,\n    method: 'delete'\n  })\n}\n\n// 生成代码（自定义路径）\nexport function genCode(tableName) {\n  return request({\n    headers: { 'datasource': localStorage.getItem(\"dataName\") },\n    url: '/tool/gen/genCode/' + tableName,\n    method: 'get'\n  })\n}\n\n// 同步数据库\nexport function synchDb(tableName) {\n  return request({\n    headers: { 'datasource': localStorage.getItem(\"dataName\") },\n    url: '/tool/gen/synchDb/' + tableName,\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/assets/icons/index.js",
    "content": "import Vue from 'vue'\nimport SvgIcon from '@/components/SvgIcon'// svg component\n\n// register globally\nVue.component('svg-icon', SvgIcon)\n\nconst req = require.context('./svg', false, /\\.svg$/)\nconst requireAll = requireContext => requireContext.keys().map(requireContext)\nrequireAll(req)\n"
  },
  {
    "path": "ruoyi-ui/src/assets/icons/svgo.yml",
    "content": "# replace default config\n\n# multipass: true\n# full: true\n\nplugins:\n\n  # - name\n  #\n  # or:\n  # - name: false\n  # - name: true\n  #\n  # or:\n  # - name:\n  #     param1: 1\n  #     param2: 2\n\n- removeAttrs:\n    attrs:\n      - 'fill'\n      - 'fill-rule'\n"
  },
  {
    "path": "ruoyi-ui/src/assets/styles/btn.scss",
    "content": "@import './variables.scss';\n\n@mixin colorBtn($color) {\n  background: $color;\n\n  &:hover {\n    color: $color;\n\n    &:before,\n    &:after {\n      background: $color;\n    }\n  }\n}\n\n.blue-btn {\n  @include colorBtn($blue)\n}\n\n.light-blue-btn {\n  @include colorBtn($light-blue)\n}\n\n.red-btn {\n  @include colorBtn($red)\n}\n\n.pink-btn {\n  @include colorBtn($pink)\n}\n\n.green-btn {\n  @include colorBtn($green)\n}\n\n.tiffany-btn {\n  @include colorBtn($tiffany)\n}\n\n.yellow-btn {\n  @include colorBtn($yellow)\n}\n\n.pan-btn {\n  font-size: 14px;\n  color: #fff;\n  padding: 14px 36px;\n  border-radius: 8px;\n  border: none;\n  outline: none;\n  transition: 600ms ease all;\n  position: relative;\n  display: inline-block;\n\n  &:hover {\n    background: #fff;\n\n    &:before,\n    &:after {\n      width: 100%;\n      transition: 600ms ease all;\n    }\n  }\n\n  &:before,\n  &:after {\n    content: '';\n    position: absolute;\n    top: 0;\n    right: 0;\n    height: 2px;\n    width: 0;\n    transition: 400ms ease all;\n  }\n\n  &::after {\n    right: inherit;\n    top: inherit;\n    left: 0;\n    bottom: 0;\n  }\n}\n\n.custom-button {\n  display: inline-block;\n  line-height: 1;\n  white-space: nowrap;\n  cursor: pointer;\n  background: #fff;\n  color: #fff;\n  -webkit-appearance: none;\n  text-align: center;\n  box-sizing: border-box;\n  outline: 0;\n  margin: 0;\n  padding: 10px 15px;\n  font-size: 14px;\n  border-radius: 4px;\n}\n"
  },
  {
    "path": "ruoyi-ui/src/assets/styles/element-ui.scss",
    "content": "// cover some element-ui styles\n\n.el-breadcrumb__inner,\n.el-breadcrumb__inner a {\n  font-weight: 400 !important;\n}\n\n.el-upload {\n  input[type=\"file\"] {\n    display: none !important;\n  }\n}\n\n.el-upload__input {\n  display: none;\n}\n\n.cell {\n  .el-tag {\n    margin-right: 0px;\n  }\n}\n\n.small-padding {\n  .cell {\n    padding-left: 5px;\n    padding-right: 5px;\n  }\n}\n\n.fixed-width {\n  .el-button--mini {\n    padding: 7px 10px;\n    width: 60px;\n  }\n}\n\n.status-col {\n  .cell {\n    padding: 0 10px;\n    text-align: center;\n\n    .el-tag {\n      margin-right: 0px;\n    }\n  }\n}\n\n// to fixed https://github.com/ElemeFE/element/issues/2461\n.el-dialog {\n  transform: none;\n  left: 0;\n  position: relative;\n  margin: 0 auto;\n}\n\n// refine element ui upload\n.upload-container {\n  .el-upload {\n    width: 100%;\n\n    .el-upload-dragger {\n      width: 100%;\n      height: 200px;\n    }\n  }\n}\n\n// dropdown\n.el-dropdown-menu {\n  a {\n    display: block\n  }\n}\n\n// fix date-picker ui bug in filter-item\n.el-range-editor.el-input__inner {\n  display: inline-flex !important;\n}\n\n// to fix el-date-picker css style\n.el-range-separator {\n  box-sizing: content-box;\n}\n\n.el-menu--collapse\n  > div\n  > .el-submenu\n  > .el-submenu__title\n  .el-submenu__icon-arrow {\n  display: none;\n}"
  },
  {
    "path": "ruoyi-ui/src/assets/styles/element-variables.scss",
    "content": "/**\n* I think element-ui's default theme color is too light for long-term use.\n* So I modified the default color and you can modify it to your liking.\n**/\n\n/* theme color */\n$--color-primary: #1890ff;\n$--color-success: #13ce66;\n$--color-warning: #ffba00;\n$--color-danger: #ff4949;\n// $--color-info: #1E1E1E;\n\n$--button-font-weight: 400;\n\n// $--color-text-regular: #1f2d3d;\n\n$--border-color-light: #dfe4ed;\n$--border-color-lighter: #e6ebf5;\n\n$--table-border: 1px solid #dfe6ec;\n\n/* icon font path, required */\n$--font-path: '~element-ui/lib/theme-chalk/fonts';\n\n@import \"~element-ui/packages/theme-chalk/src/index\";\n\n// the :export directive is the magic sauce for webpack\n// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass\n:export {\n  theme: $--color-primary;\n}\n"
  },
  {
    "path": "ruoyi-ui/src/assets/styles/index.scss",
    "content": "@import './variables.scss';\n@import './mixin.scss';\n@import './transition.scss';\n@import './element-ui.scss';\n@import './sidebar.scss';\n@import './btn.scss';\n\nbody {\n  height: 100%;\n  -moz-osx-font-smoothing: grayscale;\n  -webkit-font-smoothing: antialiased;\n  text-rendering: optimizeLegibility;\n  font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;\n}\n\nlabel {\n  font-weight: 700;\n}\n\nhtml {\n  height: 100%;\n  box-sizing: border-box;\n}\n\n#app {\n  height: 100%;\n}\n\n*,\n*:before,\n*:after {\n  box-sizing: inherit;\n}\n\n.no-padding {\n  padding: 0px !important;\n}\n\n.padding-content {\n  padding: 4px 0;\n}\n\na:focus,\na:active {\n  outline: none;\n}\n\na,\na:focus,\na:hover {\n  cursor: pointer;\n  color: inherit;\n  text-decoration: none;\n}\n\ndiv:focus {\n  outline: none;\n}\n\n.fr {\n  float: right;\n}\n\n.fl {\n  float: left;\n}\n\n.pr-5 {\n  padding-right: 5px;\n}\n\n.pl-5 {\n  padding-left: 5px;\n}\n\n.block {\n  display: block;\n}\n\n.pointer {\n  cursor: pointer;\n}\n\n.inlineBlock {\n  display: block;\n}\n\n.clearfix {\n  &:after {\n    visibility: hidden;\n    display: block;\n    font-size: 0;\n    content: \" \";\n    clear: both;\n    height: 0;\n  }\n}\n\naside {\n  background: #eef1f6;\n  padding: 8px 24px;\n  margin-bottom: 20px;\n  border-radius: 2px;\n  display: block;\n  line-height: 32px;\n  font-size: 16px;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, Cantarell, \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;\n  color: #2c3e50;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n\n  a {\n    color: #337ab7;\n    cursor: pointer;\n\n    &:hover {\n      color: rgb(32, 160, 255);\n    }\n  }\n}\n\n//main-container全局样式\n.app-container {\n  padding: 20px;\n}\n\n.components-container {\n  margin: 30px 50px;\n  position: relative;\n}\n\n.pagination-container {\n  margin-top: 30px;\n}\n\n.text-center {\n  text-align: center\n}\n\n.sub-navbar {\n  height: 50px;\n  line-height: 50px;\n  position: relative;\n  width: 100%;\n  text-align: right;\n  padding-right: 20px;\n  transition: 600ms ease position;\n  background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);\n\n  .subtitle {\n    font-size: 20px;\n    color: #fff;\n  }\n\n  &.draft {\n    background: #d0d0d0;\n  }\n\n  &.deleted {\n    background: #d0d0d0;\n  }\n}\n\n.link-type,\n.link-type:focus {\n  color: #337ab7;\n  cursor: pointer;\n\n  &:hover {\n    color: rgb(32, 160, 255);\n  }\n}\n\n.filter-container {\n  padding-bottom: 10px;\n\n  .filter-item {\n    display: inline-block;\n    vertical-align: middle;\n    margin-bottom: 10px;\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/src/assets/styles/mixin.scss",
    "content": "@mixin clearfix {\n  &:after {\n    content: \"\";\n    display: table;\n    clear: both;\n  }\n}\n\n@mixin scrollBar {\n  &::-webkit-scrollbar-track-piece {\n    background: #d3dce6;\n  }\n\n  &::-webkit-scrollbar {\n    width: 6px;\n  }\n\n  &::-webkit-scrollbar-thumb {\n    background: #99a9bf;\n    border-radius: 20px;\n  }\n}\n\n@mixin relative {\n  position: relative;\n  width: 100%;\n  height: 100%;\n}\n\n@mixin pct($pct) {\n  width: #{$pct};\n  position: relative;\n  margin: 0 auto;\n}\n\n@mixin triangle($width, $height, $color, $direction) {\n  $width: $width/2;\n  $color-border-style: $height solid $color;\n  $transparent-border-style: $width solid transparent;\n  height: 0;\n  width: 0;\n\n  @if $direction==up {\n    border-bottom: $color-border-style;\n    border-left: $transparent-border-style;\n    border-right: $transparent-border-style;\n  }\n\n  @else if $direction==right {\n    border-left: $color-border-style;\n    border-top: $transparent-border-style;\n    border-bottom: $transparent-border-style;\n  }\n\n  @else if $direction==down {\n    border-top: $color-border-style;\n    border-left: $transparent-border-style;\n    border-right: $transparent-border-style;\n  }\n\n  @else if $direction==left {\n    border-right: $color-border-style;\n    border-top: $transparent-border-style;\n    border-bottom: $transparent-border-style;\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/src/assets/styles/ruoyi.scss",
    "content": "/**\n* 通用css样式布局处理\n* Copyright (c) 2019 ruoyi\n*/\n\n/** 基础通用 **/\n.pt5 {\n  padding-top: 5px;\n}\n\n.pr5 {\n  padding-right: 5px;\n}\n\n.pb5 {\n  padding-bottom: 5px;\n}\n\n.mt5 {\n  margin-top: 5px;\n}\n\n.mr5 {\n  margin-right: 5px;\n}\n\n.mb5 {\n  margin-bottom: 5px;\n}\n\n.mb8 {\n  margin-bottom: 8px;\n}\n\n.ml5 {\n  margin-left: 5px;\n}\n\n.mt10 {\n  margin-top: 10px;\n}\n\n.mr10 {\n  margin-right: 10px;\n}\n\n.mb10 {\n  margin-bottom: 10px;\n}\n.ml10 {\n\tmargin-left: 10px;\n}\n\n.mt20 {\n  margin-top: 20px;\n}\n\n.mr20 {\n  margin-right: 20px;\n}\n\n.mb20 {\n  margin-bottom: 20px;\n}\n.ml20 {\n\tmargin-left: 20px;\n}\n\n.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {\n  font-family: inherit;\n  font-weight: 500;\n  line-height: 1.1;\n  color: inherit;\n}\n\n.el-message-box__status + .el-message-box__message{\n  word-break: break-word;\n}\n\n.el-dialog:not(.is-fullscreen) {\n  margin-top: 6vh !important;\n}\n\n.el-dialog__wrapper.scrollbar .el-dialog .el-dialog__body {\n  overflow: auto;\n  overflow-x: hidden;\n  max-height: 70vh;\n  padding: 10px 20px 0;\n}\n\n.el-table {\n  .el-table__header-wrapper, .el-table__fixed-header-wrapper {\n    th {\n      word-break: break-word;\n      background-color: #f8f8f9;\n      color: #515a6e;\n      height: 40px;\n      font-size: 13px;\n    }\n  }\n\n  .el-table__body-wrapper {\n    .el-button [class*=\"el-icon-\"] + span {\n      margin-left: 1px;\n    }\n  }\n}\n\n/** 表单布局 **/\n.form-header {\n  font-size: 15px;\n  color: #6379bb;\n  border-bottom: 1px solid #ddd;\n  margin: 8px 10px 25px 10px;\n  padding-bottom: 5px\n}\n\n/** 表格布局 **/\n.pagination-container {\n  position: relative;\n  height: 25px;\n  margin-bottom: 10px;\n  margin-top: 15px;\n  padding: 10px 20px !important;\n}\n\n/* tree border */\n.tree-border {\n  margin-top: 5px;\n  border: 1px solid #e5e6e7;\n  background: #FFFFFF none;\n  border-radius: 4px;\n}\n\n.pagination-container .el-pagination {\n  right: 0;\n  position: absolute;\n}\n\n@media (max-width: 768px) {\n  .pagination-container .el-pagination > .el-pagination__jump {\n    display: none !important;\n  }\n  .pagination-container .el-pagination > .el-pagination__sizes {\n    display: none !important;\n  }\n}\n\n.el-table .fixed-width .el-button--mini {\n  padding-left: 0;\n  padding-right: 0;\n  width: inherit;\n}\n\n/** 表格更多操作下拉样式 */\n.el-table .el-dropdown-link,.el-table .el-dropdown-selfdefine {\n\tcursor: pointer;\n\tmargin-left: 5px;\n}\n\n.el-table .el-dropdown, .el-icon-arrow-down {\n  font-size: 12px;\n}\n\n.el-tree-node__content > .el-checkbox {\n  margin-right: 8px;\n}\n\n.list-group-striped > .list-group-item {\n  border-left: 0;\n  border-right: 0;\n  border-radius: 0;\n  padding-left: 0;\n  padding-right: 0;\n}\n\n.list-group {\n  padding-left: 0px;\n  list-style: none;\n}\n\n.list-group-item {\n  border-bottom: 1px solid #e7eaec;\n  border-top: 1px solid #e7eaec;\n  margin-bottom: -1px;\n  padding: 11px 0px;\n  font-size: 13px;\n}\n\n.pull-right {\n  float: right !important;\n}\n\n.el-card__header {\n  padding: 14px 15px 7px;\n  min-height: 40px;\n}\n\n.el-card__body {\n  padding: 15px 20px 20px 20px;\n}\n\n.card-box {\n  padding-right: 15px;\n  padding-left: 15px;\n  margin-bottom: 10px;\n}\n\n/* button color */\n.el-button--cyan.is-active,\n.el-button--cyan:active {\n  background: #20B2AA;\n  border-color: #20B2AA;\n  color: #FFFFFF;\n}\n\n.el-button--cyan:focus,\n.el-button--cyan:hover {\n  background: #48D1CC;\n  border-color: #48D1CC;\n  color: #FFFFFF;\n}\n\n.el-button--cyan {\n  background-color: #20B2AA;\n  border-color: #20B2AA;\n  color: #FFFFFF;\n}\n\n/* text color */\n.text-navy {\n  color: #1ab394;\n}\n\n.text-primary {\n  color: inherit;\n}\n\n.text-success {\n  color: #1c84c6;\n}\n\n.text-info {\n  color: #23c6c8;\n}\n\n.text-warning {\n  color: #f8ac59;\n}\n\n.text-danger {\n  color: #ed5565;\n}\n\n.text-muted {\n  color: #888888;\n}\n\n/* image */\n.img-circle {\n  border-radius: 50%;\n}\n\n.img-lg {\n  width: 120px;\n  height: 120px;\n}\n\n.avatar-upload-preview {\n  position: relative;\n  top: 50%;\n  left: 50%;\n  transform: translate(-50%, -50%);\n  width: 200px;\n  height: 200px;\n  border-radius: 50%;\n  box-shadow: 0 0 4px #ccc;\n  overflow: hidden;\n}\n\n/* 拖拽列样式 */\n.sortable-ghost {\n  opacity: .8;\n  color: #fff !important;\n  background: #42b983 !important;\n}\n\n.top-right-btn {\n  position: relative;\n  float: right;\n}\n"
  },
  {
    "path": "ruoyi-ui/src/assets/styles/sidebar.scss",
    "content": "#app {\n\n  .main-container {\n    height: 100%;\n    transition: margin-left .28s;\n    margin-left: $base-sidebar-width;\n    position: relative;\n  }\n\n  .sidebarHide {\n    margin-left: 0!important;\n  }\n\n  .sidebar-container {\n    -webkit-transition: width .28s;\n    transition: width 0.28s;\n    width: $base-sidebar-width !important;\n    background-color: $base-menu-background;\n    height: 100%;\n    position: fixed;\n    font-size: 0px;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    z-index: 1001;\n    overflow: hidden;\n    -webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35);\n    box-shadow: 2px 0 6px rgba(0,21,41,.35);\n\n    // reset element-ui css\n    .horizontal-collapse-transition {\n      transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;\n    }\n\n    .scrollbar-wrapper {\n      overflow-x: hidden !important;\n    }\n\n    .el-scrollbar__bar.is-vertical {\n      right: 0px;\n    }\n\n    .el-scrollbar {\n      height: 100%;\n    }\n\n    &.has-logo {\n      .el-scrollbar {\n        height: calc(100% - 50px);\n      }\n    }\n\n    .is-horizontal {\n      display: none;\n    }\n\n    a {\n      display: inline-block;\n      width: 100%;\n      overflow: hidden;\n    }\n\n    .svg-icon {\n      margin-right: 16px;\n    }\n\n    .el-menu {\n      border: none;\n      height: 100%;\n      width: 100% !important;\n    }\n\n    .el-menu-item, .el-submenu__title {\n      overflow: hidden !important;\n      text-overflow: ellipsis !important;\n      white-space: nowrap !important;\n    }\n\n    // menu hover\n    .submenu-title-noDropdown,\n    .el-submenu__title {\n      &:hover {\n        background-color: rgba(0, 0, 0, 0.06) !important;\n      }\n    }\n\n    & .theme-dark .is-active > .el-submenu__title {\n      color: $base-menu-color-active !important;\n    }\n\n    & .nest-menu .el-submenu>.el-submenu__title,\n    & .el-submenu .el-menu-item {\n      min-width: $base-sidebar-width !important;\n\n      &:hover {\n        background-color: rgba(0, 0, 0, 0.06) !important;\n      }\n    }\n\n    & .theme-dark .nest-menu .el-submenu>.el-submenu__title,\n    & .theme-dark .el-submenu .el-menu-item {\n      background-color: $base-sub-menu-background !important;\n\n      &:hover {\n        background-color: $base-sub-menu-hover !important;\n      }\n    }\n  }\n\n  .hideSidebar {\n    .sidebar-container {\n      width: 54px !important;\n    }\n\n    .main-container {\n      margin-left: 54px;\n    }\n\n    .submenu-title-noDropdown {\n      padding: 0 !important;\n      position: relative;\n\n      .el-tooltip {\n        padding: 0 !important;\n\n        .svg-icon {\n          margin-left: 20px;\n        }\n      }\n    }\n\n    .el-submenu {\n      overflow: hidden;\n\n      &>.el-submenu__title {\n        padding: 0 !important;\n\n        .svg-icon {\n          margin-left: 20px;\n        }\n\n      }\n    }\n\n    .el-menu--collapse {\n      .el-submenu {\n        &>.el-submenu__title {\n          &>span {\n            height: 0;\n            width: 0;\n            overflow: hidden;\n            visibility: hidden;\n            display: inline-block;\n          }\n        }\n      }\n    }\n  }\n\n  .el-menu--collapse .el-menu .el-submenu {\n    min-width: $base-sidebar-width !important;\n  }\n\n  // mobile responsive\n  .mobile {\n    .main-container {\n      margin-left: 0px;\n    }\n\n    .sidebar-container {\n      transition: transform .28s;\n      width: $base-sidebar-width !important;\n    }\n\n    &.hideSidebar {\n      .sidebar-container {\n        pointer-events: none;\n        transition-duration: 0.3s;\n        transform: translate3d(-$base-sidebar-width, 0, 0);\n      }\n    }\n  }\n\n  .withoutAnimation {\n\n    .main-container,\n    .sidebar-container {\n      transition: none;\n    }\n  }\n}\n\n// when menu collapsed\n.el-menu--vertical {\n  &>.el-menu {\n    .svg-icon {\n      margin-right: 16px;\n    }\n  }\n\n  .nest-menu .el-submenu>.el-submenu__title,\n  .el-menu-item {\n    &:hover {\n      // you can use $subMenuHover\n      background-color: rgba(0, 0, 0, 0.06) !important;\n    }\n  }\n\n  // the scroll bar appears when the subMenu is too long\n  >.el-menu--popup {\n    max-height: 100vh;\n    overflow-y: auto;\n\n    &::-webkit-scrollbar-track-piece {\n      background: #d3dce6;\n    }\n\n    &::-webkit-scrollbar {\n      width: 6px;\n    }\n\n    &::-webkit-scrollbar-thumb {\n      background: #99a9bf;\n      border-radius: 20px;\n    }\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/src/assets/styles/transition.scss",
    "content": "// global transition css\n\n/* fade */\n.fade-enter-active,\n.fade-leave-active {\n  transition: opacity 0.28s;\n}\n\n.fade-enter,\n.fade-leave-active {\n  opacity: 0;\n}\n\n/* fade-transform */\n.fade-transform--move,\n.fade-transform-leave-active,\n.fade-transform-enter-active {\n  transition: all .5s;\n}\n\n.fade-transform-enter {\n  opacity: 0;\n  transform: translateX(-30px);\n}\n\n.fade-transform-leave-to {\n  opacity: 0;\n  transform: translateX(30px);\n}\n\n/* breadcrumb transition */\n.breadcrumb-enter-active,\n.breadcrumb-leave-active {\n  transition: all .5s;\n}\n\n.breadcrumb-enter,\n.breadcrumb-leave-active {\n  opacity: 0;\n  transform: translateX(20px);\n}\n\n.breadcrumb-move {\n  transition: all .5s;\n}\n\n.breadcrumb-leave-active {\n  position: absolute;\n}\n"
  },
  {
    "path": "ruoyi-ui/src/assets/styles/variables.scss",
    "content": "// base color\n$blue:#324157;\n$light-blue:#3A71A8;\n$red:#C03639;\n$pink: #E65D6E;\n$green: #30B08F;\n$tiffany: #4AB7BD;\n$yellow:#FEC171;\n$panGreen: #30B08F;\n\n// 默认菜单主题风格\n$base-menu-color:#bfcbd9;\n$base-menu-color-active:#f4f4f5;\n$base-menu-background:#304156;\n$base-logo-title-color: #ffffff;\n\n$base-menu-light-color:rgba(0,0,0,.70);\n$base-menu-light-background:#ffffff;\n$base-logo-light-title-color: #001529;\n\n$base-sub-menu-background:#1f2d3d;\n$base-sub-menu-hover:#001528;\n\n// 自定义暗色菜单风格\n/**\n$base-menu-color:hsla(0,0%,100%,.65);\n$base-menu-color-active:#fff;\n$base-menu-background:#001529;\n$base-logo-title-color: #ffffff;\n\n$base-menu-light-color:rgba(0,0,0,.70);\n$base-menu-light-background:#ffffff;\n$base-logo-light-title-color: #001529;\n\n$base-sub-menu-background:#000c17;\n$base-sub-menu-hover:#001528;\n*/\n\n$base-sidebar-width: 200px;\n\n// the :export directive is the magic sauce for webpack\n// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass\n:export {\n  menuColor: $base-menu-color;\n  menuLightColor: $base-menu-light-color;\n  menuColorActive: $base-menu-color-active;\n  menuBackground: $base-menu-background;\n  menuLightBackground: $base-menu-light-background;\n  subMenuBackground: $base-sub-menu-background;\n  subMenuHover: $base-sub-menu-hover;\n  sideBarWidth: $base-sidebar-width;\n  logoTitleColor: $base-logo-title-color;\n  logoLightTitleColor: $base-logo-light-title-color\n}\n"
  },
  {
    "path": "ruoyi-ui/src/components/Breadcrumb/index.vue",
    "content": "<template>\n  <el-breadcrumb class=\"app-breadcrumb\" separator=\"/\">\n    <transition-group name=\"breadcrumb\">\n      <el-breadcrumb-item v-for=\"(item,index) in levelList\" :key=\"item.path\">\n        <span v-if=\"item.redirect === 'noRedirect' || index == levelList.length - 1\" class=\"no-redirect\">{{ item.meta.title }}</span>\n        <a v-else @click.prevent=\"handleLink(item)\">{{ item.meta.title }}</a>\n      </el-breadcrumb-item>\n    </transition-group>\n  </el-breadcrumb>\n</template>\n\n<script>\nexport default {\n  data() {\n    return {\n      levelList: null\n    }\n  },\n  watch: {\n    $route(route) {\n      // if you go to the redirect page, do not update the breadcrumbs\n      if (route.path.startsWith('/redirect/')) {\n        return\n      }\n      this.getBreadcrumb()\n    }\n  },\n  created() {\n    this.getBreadcrumb()\n  },\n  methods: {\n    getBreadcrumb() {\n      // only show routes with meta.title\n      let matched = this.$route.matched.filter(item => item.meta && item.meta.title)\n      const first = matched[0]\n\n      if (!this.isDashboard(first)) {\n        matched = [{ path: '/index', meta: { title: '首页' }}].concat(matched)\n      }\n\n      this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)\n    },\n    isDashboard(route) {\n      const name = route && route.name\n      if (!name) {\n        return false\n      }\n      return name.trim() === 'Index'\n    },\n    handleLink(item) {\n      const { redirect, path } = item\n      if (redirect) {\n        this.$router.push(redirect)\n        return\n      }\n      this.$router.push(path)\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.app-breadcrumb.el-breadcrumb {\n  display: inline-block;\n  font-size: 14px;\n  line-height: 50px;\n  margin-left: 8px;\n\n  .no-redirect {\n    color: #97a8be;\n    cursor: text;\n  }\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/Crontab/day.vue",
    "content": "<template>\n\t<el-form size=\"small\">\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"1\">\n\t\t\t\t日，允许的通配符[, - * ? / L W]\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"2\">\n\t\t\t\t不指定\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"3\">\n\t\t\t\t周期从\n\t\t\t\t<el-input-number v-model='cycle01' :min=\"1\" :max=\"30\" /> -\n\t\t\t\t<el-input-number v-model='cycle02' :min=\"cycle01 ? cycle01 + 1 : 2\" :max=\"31\" /> 日\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"4\">\n\t\t\t\t从\n\t\t\t\t<el-input-number v-model='average01' :min=\"1\" :max=\"30\" /> 号开始，每\n\t\t\t\t<el-input-number v-model='average02' :min=\"1\" :max=\"31 - average01 || 1\" /> 日执行一次\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"5\">\n\t\t\t\t每月\n\t\t\t\t<el-input-number v-model='workday' :min=\"1\" :max=\"31\" /> 号最近的那个工作日\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"6\">\n\t\t\t\t本月最后一天\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"7\">\n\t\t\t\t指定\n\t\t\t\t<el-select clearable v-model=\"checkboxList\" placeholder=\"可多选\" multiple style=\"width:100%\">\n\t\t\t\t\t<el-option v-for=\"item in 31\" :key=\"item\" :value=\"item\">{{item}}</el-option>\n\t\t\t\t</el-select>\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\t</el-form>\n</template>\n\n<script>\nexport default {\n\tdata() {\n\t\treturn {\n\t\t\tradioValue: 1,\n\t\t\tworkday: 1,\n\t\t\tcycle01: 1,\n\t\t\tcycle02: 2,\n\t\t\taverage01: 1,\n\t\t\taverage02: 1,\n\t\t\tcheckboxList: [],\n\t\t\tcheckNum: this.$options.propsData.check\n\t\t}\n\t},\n\tname: 'crontab-day',\n\tprops: ['check', 'cron'],\n\tmethods: {\n\t\t// 单选按钮值变化时\n\t\tradioChange() {\n\t\t\t('day rachange');\n\t\t\tif (this.radioValue !== 2 && this.cron.week !== '?') {\n\t\t\t\tthis.$emit('update', 'week', '?', 'day')\n\t\t\t}\n\n\t\t\tswitch (this.radioValue) {\n\t\t\t\tcase 1:\n\t\t\t\t\tthis.$emit('update', 'day', '*');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tthis.$emit('update', 'day', '?');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tthis.$emit('update', 'day', this.cycleTotal);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tthis.$emit('update', 'day', this.averageTotal);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 5:\n\t\t\t\t\tthis.$emit('update', 'day', this.workday + 'W');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 6:\n\t\t\t\t\tthis.$emit('update', 'day', 'L');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 7:\n\t\t\t\t\tthis.$emit('update', 'day', this.checkboxString);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\t('day rachange end');\n\t\t},\n\t\t// 周期两个值变化时\n\t\tcycleChange() {\n\t\t\tif (this.radioValue == '3') {\n\t\t\t\tthis.$emit('update', 'day', this.cycleTotal);\n\t\t\t}\n\t\t},\n\t\t// 平均两个值变化时\n\t\taverageChange() {\n\t\t\tif (this.radioValue == '4') {\n\t\t\t\tthis.$emit('update', 'day', this.averageTotal);\n\t\t\t}\n\t\t},\n\t\t// 最近工作日值变化时\n\t\tworkdayChange() {\n\t\t\tif (this.radioValue == '5') {\n\t\t\t\tthis.$emit('update', 'day', this.workdayCheck + 'W');\n\t\t\t}\n\t\t},\n\t\t// checkbox值变化时\n\t\tcheckboxChange() {\n\t\t\tif (this.radioValue == '7') {\n\t\t\t\tthis.$emit('update', 'day', this.checkboxString);\n\t\t\t}\n\t\t}\n\t},\n\twatch: {\n\t\t'radioValue': 'radioChange',\n\t\t'cycleTotal': 'cycleChange',\n\t\t'averageTotal': 'averageChange',\n\t\t'workdayCheck': 'workdayChange',\n\t\t'checkboxString': 'checkboxChange',\n\t},\n\tcomputed: {\n\t\t// 计算两个周期值\n\t\tcycleTotal: function () {\n\t\t\tconst cycle01 = this.checkNum(this.cycle01, 1, 30)\n\t\t\tconst cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 2, 31, 31)\n\t\t\treturn cycle01 + '-' + cycle02;\n\t\t},\n\t\t// 计算平均用到的值\n\t\taverageTotal: function () {\n\t\t\tconst average01 = this.checkNum(this.average01, 1, 30)\n\t\t\tconst average02 = this.checkNum(this.average02, 1, 31 - average01 || 0)\n\t\t\treturn average01 + '/' + average02;\n\t\t},\n\t\t// 计算工作日格式\n\t\tworkdayCheck: function () {\n\t\t\tconst workday = this.checkNum(this.workday, 1, 31)\n\t\t\treturn workday;\n\t\t},\n\t\t// 计算勾选的checkbox值合集\n\t\tcheckboxString: function () {\n\t\t\tlet str = this.checkboxList.join();\n\t\t\treturn str == '' ? '*' : str;\n\t\t}\n\t}\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/components/Crontab/hour.vue",
    "content": "<template>\n\t<el-form size=\"small\">\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"1\">\n\t\t\t\t小时，允许的通配符[, - * /]\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"2\">\n\t\t\t\t周期从\n\t\t\t\t<el-input-number v-model='cycle01' :min=\"0\" :max=\"22\" /> -\n\t\t\t\t<el-input-number v-model='cycle02' :min=\"cycle01 ? cycle01 + 1 : 1\" :max=\"23\" /> 小时\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"3\">\n\t\t\t\t从\n\t\t\t\t<el-input-number v-model='average01' :min=\"0\" :max=\"22\" /> 小时开始，每\n\t\t\t\t<el-input-number v-model='average02' :min=\"1\" :max=\"23 - average01 || 0\" /> 小时执行一次\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"4\">\n\t\t\t\t指定\n\t\t\t\t<el-select clearable v-model=\"checkboxList\" placeholder=\"可多选\" multiple style=\"width:100%\">\n\t\t\t\t\t<el-option v-for=\"item in 24\" :key=\"item\" :value=\"item-1\">{{item-1}}</el-option>\n\t\t\t\t</el-select>\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\t</el-form>\n</template>\n\n<script>\nexport default {\n\tdata() {\n\t\treturn {\n\t\t\tradioValue: 1,\n\t\t\tcycle01: 0,\n\t\t\tcycle02: 1,\n\t\t\taverage01: 0,\n\t\t\taverage02: 1,\n\t\t\tcheckboxList: [],\n\t\t\tcheckNum: this.$options.propsData.check\n\t\t}\n\t},\n\tname: 'crontab-hour',\n\tprops: ['check', 'cron'],\n\tmethods: {\n\t\t// 单选按钮值变化时\n\t\tradioChange() {\n\t\t\tswitch (this.radioValue) {\n\t\t\t\tcase 1:\n        \tthis.$emit('update', 'hour', '*')\n        \tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tthis.$emit('update', 'hour', this.cycleTotal);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tthis.$emit('update', 'hour', this.averageTotal);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tthis.$emit('update', 'hour', this.checkboxString);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\t\t// 周期两个值变化时\n\t\tcycleChange() {\n\t\t\tif (this.radioValue == '2') {\n\t\t\t\tthis.$emit('update', 'hour', this.cycleTotal);\n\t\t\t}\n\t\t},\n\t\t// 平均两个值变化时\n\t\taverageChange() {\n\t\t\tif (this.radioValue == '3') {\n\t\t\t\tthis.$emit('update', 'hour', this.averageTotal);\n\t\t\t}\n\t\t},\n\t\t// checkbox值变化时\n\t\tcheckboxChange() {\n\t\t\tif (this.radioValue == '4') {\n\t\t\t\tthis.$emit('update', 'hour', this.checkboxString);\n\t\t\t}\n\t\t}\n\t},\n\twatch: {\n\t\t'radioValue': 'radioChange',\n\t\t'cycleTotal': 'cycleChange',\n\t\t'averageTotal': 'averageChange',\n\t\t'checkboxString': 'checkboxChange'\n\t},\n\tcomputed: {\n\t\t// 计算两个周期值\n\t\tcycleTotal: function () {\n\t\t\tconst cycle01 = this.checkNum(this.cycle01, 0, 22)\n\t\t\tconst cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 1, 23)\n\t\t\treturn cycle01 + '-' + cycle02;\n\t\t},\n\t\t// 计算平均用到的值\n\t\taverageTotal: function () {\n\t\t\tconst average01 = this.checkNum(this.average01, 0, 22)\n\t\t\tconst average02 = this.checkNum(this.average02, 1, 23 - average01 || 0)\n\t\t\treturn average01 + '/' + average02;\n\t\t},\n\t\t// 计算勾选的checkbox值合集\n\t\tcheckboxString: function () {\n\t\t\tlet str = this.checkboxList.join();\n\t\t\treturn str == '' ? '*' : str;\n\t\t}\n\t}\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/components/Crontab/index.vue",
    "content": "<template>\n  <div>\n    <el-tabs type=\"border-card\">\n      <el-tab-pane label=\"秒\" v-if=\"shouldHide('second')\">\n        <CrontabSecond\n          @update=\"updateCrontabValue\"\n          :check=\"checkNumber\"\n          :cron=\"crontabValueObj\"\n          ref=\"cronsecond\"\n        />\n      </el-tab-pane>\n\n      <el-tab-pane label=\"分钟\" v-if=\"shouldHide('min')\">\n        <CrontabMin\n          @update=\"updateCrontabValue\"\n          :check=\"checkNumber\"\n          :cron=\"crontabValueObj\"\n          ref=\"cronmin\"\n        />\n      </el-tab-pane>\n\n      <el-tab-pane label=\"小时\" v-if=\"shouldHide('hour')\">\n        <CrontabHour\n          @update=\"updateCrontabValue\"\n          :check=\"checkNumber\"\n          :cron=\"crontabValueObj\"\n          ref=\"cronhour\"\n        />\n      </el-tab-pane>\n\n      <el-tab-pane label=\"日\" v-if=\"shouldHide('day')\">\n        <CrontabDay\n          @update=\"updateCrontabValue\"\n          :check=\"checkNumber\"\n          :cron=\"crontabValueObj\"\n          ref=\"cronday\"\n        />\n      </el-tab-pane>\n\n      <el-tab-pane label=\"月\" v-if=\"shouldHide('month')\">\n        <CrontabMonth\n          @update=\"updateCrontabValue\"\n          :check=\"checkNumber\"\n          :cron=\"crontabValueObj\"\n          ref=\"cronmonth\"\n        />\n      </el-tab-pane>\n\n      <el-tab-pane label=\"周\" v-if=\"shouldHide('week')\">\n        <CrontabWeek\n          @update=\"updateCrontabValue\"\n          :check=\"checkNumber\"\n          :cron=\"crontabValueObj\"\n          ref=\"cronweek\"\n        />\n      </el-tab-pane>\n\n      <el-tab-pane label=\"年\" v-if=\"shouldHide('year')\">\n        <CrontabYear\n          @update=\"updateCrontabValue\"\n          :check=\"checkNumber\"\n          :cron=\"crontabValueObj\"\n          ref=\"cronyear\"\n        />\n      </el-tab-pane>\n    </el-tabs>\n\n    <div class=\"popup-main\">\n      <div class=\"popup-result\">\n        <p class=\"title\">时间表达式</p>\n        <table>\n          <thead>\n            <th v-for=\"item of tabTitles\" width=\"40\" :key=\"item\">{{item}}</th>\n            <th>Cron 表达式</th>\n          </thead>\n          <tbody>\n            <td>\n              <span>{{crontabValueObj.second}}</span>\n            </td>\n            <td>\n              <span>{{crontabValueObj.min}}</span>\n            </td>\n            <td>\n              <span>{{crontabValueObj.hour}}</span>\n            </td>\n            <td>\n              <span>{{crontabValueObj.day}}</span>\n            </td>\n            <td>\n              <span>{{crontabValueObj.month}}</span>\n            </td>\n            <td>\n              <span>{{crontabValueObj.week}}</span>\n            </td>\n            <td>\n              <span>{{crontabValueObj.year}}</span>\n            </td>\n            <td>\n              <span>{{crontabValueString}}</span>\n            </td>\n          </tbody>\n        </table>\n      </div>\n      <CrontabResult :ex=\"crontabValueString\"></CrontabResult>\n\n      <div class=\"pop_btn\">\n        <el-button size=\"small\" type=\"primary\" @click=\"submitFill\">确定</el-button>\n        <el-button size=\"small\" type=\"warning\" @click=\"clearCron\">重置</el-button>\n        <el-button size=\"small\" @click=\"hidePopup\">取消</el-button>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport CrontabSecond from \"./second.vue\";\nimport CrontabMin from \"./min.vue\";\nimport CrontabHour from \"./hour.vue\";\nimport CrontabDay from \"./day.vue\";\nimport CrontabMonth from \"./month.vue\";\nimport CrontabWeek from \"./week.vue\";\nimport CrontabYear from \"./year.vue\";\nimport CrontabResult from \"./result.vue\";\n\nexport default {\n  data() {\n    return {\n      tabTitles: [\"秒\", \"分钟\", \"小时\", \"日\", \"月\", \"周\", \"年\"],\n      tabActive: 0,\n      myindex: 0,\n      crontabValueObj: {\n        second: \"*\",\n        min: \"*\",\n        hour: \"*\",\n        day: \"*\",\n        month: \"*\",\n        week: \"?\",\n        year: \"\",\n      },\n    };\n  },\n  name: \"vcrontab\",\n  props: [\"expression\", \"hideComponent\"],\n  methods: {\n    shouldHide(key) {\n      if (this.hideComponent && this.hideComponent.includes(key)) return false;\n      return true;\n    },\n    resolveExp() {\n      // 反解析 表达式\n      if (this.expression) {\n        let arr = this.expression.split(\" \");\n        if (arr.length >= 6) {\n          //6 位以上是合法表达式\n          let obj = {\n            second: arr[0],\n            min: arr[1],\n            hour: arr[2],\n            day: arr[3],\n            month: arr[4],\n            week: arr[5],\n            year: arr[6] ? arr[6] : \"\",\n          };\n          this.crontabValueObj = {\n            ...obj,\n          };\n          for (let i in obj) {\n            if (obj[i]) this.changeRadio(i, obj[i]);\n          }\n        }\n      } else {\n        // 没有传入的表达式 则还原\n        this.clearCron();\n      }\n    },\n    // tab切换值\n    tabCheck(index) {\n      this.tabActive = index;\n    },\n    // 由子组件触发，更改表达式组成的字段值\n    updateCrontabValue(name, value, from) {\n      \"updateCrontabValue\", name, value, from;\n      this.crontabValueObj[name] = value;\n      if (from && from !== name) {\n        console.log(`来自组件 ${from} 改变了 ${name} ${value}`);\n        this.changeRadio(name, value);\n      }\n    },\n    // 赋值到组件\n    changeRadio(name, value) {\n      let arr = [\"second\", \"min\", \"hour\", \"month\"],\n        refName = \"cron\" + name,\n        insValue;\n\n      if (!this.$refs[refName]) return;\n\n      if (arr.includes(name)) {\n        if (value === \"*\") {\n          insValue = 1;\n        } else if (value.indexOf(\"-\") > -1) {\n          let indexArr = value.split(\"-\");\n          isNaN(indexArr[0])\n            ? (this.$refs[refName].cycle01 = 0)\n            : (this.$refs[refName].cycle01 = indexArr[0]);\n          this.$refs[refName].cycle02 = indexArr[1];\n          insValue = 2;\n        } else if (value.indexOf(\"/\") > -1) {\n          let indexArr = value.split(\"/\");\n          isNaN(indexArr[0])\n            ? (this.$refs[refName].average01 = 0)\n            : (this.$refs[refName].average01 = indexArr[0]);\n          this.$refs[refName].average02 = indexArr[1];\n          insValue = 3;\n        } else {\n          insValue = 4;\n          this.$refs[refName].checkboxList = value.split(\",\");\n        }\n      } else if (name == \"day\") {\n        if (value === \"*\") {\n          insValue = 1;\n        } else if (value == \"?\") {\n          insValue = 2;\n        } else if (value.indexOf(\"-\") > -1) {\n          let indexArr = value.split(\"-\");\n          isNaN(indexArr[0])\n            ? (this.$refs[refName].cycle01 = 0)\n            : (this.$refs[refName].cycle01 = indexArr[0]);\n          this.$refs[refName].cycle02 = indexArr[1];\n          insValue = 3;\n        } else if (value.indexOf(\"/\") > -1) {\n          let indexArr = value.split(\"/\");\n          isNaN(indexArr[0])\n            ? (this.$refs[refName].average01 = 0)\n            : (this.$refs[refName].average01 = indexArr[0]);\n          this.$refs[refName].average02 = indexArr[1];\n          insValue = 4;\n        } else if (value.indexOf(\"W\") > -1) {\n          let indexArr = value.split(\"W\");\n          isNaN(indexArr[0])\n            ? (this.$refs[refName].workday = 0)\n            : (this.$refs[refName].workday = indexArr[0]);\n          insValue = 5;\n        } else if (value === \"L\") {\n          insValue = 6;\n        } else {\n          this.$refs[refName].checkboxList = value.split(\",\");\n          insValue = 7;\n        }\n      } else if (name == \"week\") {\n        if (value === \"*\") {\n          insValue = 1;\n        } else if (value == \"?\") {\n          insValue = 2;\n        } else if (value.indexOf(\"-\") > -1) {\n          let indexArr = value.split(\"-\");\n          isNaN(indexArr[0])\n            ? (this.$refs[refName].cycle01 = 0)\n            : (this.$refs[refName].cycle01 = indexArr[0]);\n          this.$refs[refName].cycle02 = indexArr[1];\n          insValue = 3;\n        } else if (value.indexOf(\"#\") > -1) {\n          let indexArr = value.split(\"#\");\n          isNaN(indexArr[0])\n            ? (this.$refs[refName].average01 = 1)\n            : (this.$refs[refName].average01 = indexArr[0]);\n          this.$refs[refName].average02 = indexArr[1];\n          insValue = 4;\n        } else if (value.indexOf(\"L\") > -1) {\n          let indexArr = value.split(\"L\");\n          isNaN(indexArr[0])\n            ? (this.$refs[refName].weekday = 1)\n            : (this.$refs[refName].weekday = indexArr[0]);\n          insValue = 5;\n        } else {\n          this.$refs[refName].checkboxList = value.split(\",\");\n          insValue = 6;\n        }\n      } else if (name == \"year\") {\n        if (value == \"\") {\n          insValue = 1;\n        } else if (value == \"*\") {\n          insValue = 2;\n        } else if (value.indexOf(\"-\") > -1) {\n          insValue = 3;\n        } else if (value.indexOf(\"/\") > -1) {\n          insValue = 4;\n        } else {\n          this.$refs[refName].checkboxList = value.split(\",\");\n          insValue = 5;\n        }\n      }\n      this.$refs[refName].radioValue = insValue;\n    },\n    // 表单选项的子组件校验数字格式（通过-props传递）\n    checkNumber(value, minLimit, maxLimit) {\n      // 检查必须为整数\n      value = Math.floor(value);\n      if (value < minLimit) {\n        value = minLimit;\n      } else if (value > maxLimit) {\n        value = maxLimit;\n      }\n      return value;\n    },\n    // 隐藏弹窗\n    hidePopup() {\n      this.$emit(\"hide\");\n    },\n    // 填充表达式\n    submitFill() {\n      this.$emit(\"fill\", this.crontabValueString);\n      this.hidePopup();\n    },\n    clearCron() {\n      // 还原选择项\n      (\"准备还原\");\n      this.crontabValueObj = {\n        second: \"*\",\n        min: \"*\",\n        hour: \"*\",\n        day: \"*\",\n        month: \"*\",\n        week: \"?\",\n        year: \"\",\n      };\n      for (let j in this.crontabValueObj) {\n        this.changeRadio(j, this.crontabValueObj[j]);\n      }\n    },\n  },\n  computed: {\n    crontabValueString: function() {\n      let obj = this.crontabValueObj;\n      let str =\n        obj.second +\n        \" \" +\n        obj.min +\n        \" \" +\n        obj.hour +\n        \" \" +\n        obj.day +\n        \" \" +\n        obj.month +\n        \" \" +\n        obj.week +\n        (obj.year == \"\" ? \"\" : \" \" + obj.year);\n      return str;\n    },\n  },\n  components: {\n    CrontabSecond,\n    CrontabMin,\n    CrontabHour,\n    CrontabDay,\n    CrontabMonth,\n    CrontabWeek,\n    CrontabYear,\n    CrontabResult,\n  },\n  watch: {\n    expression: \"resolveExp\",\n    hideComponent(value) {\n      // 隐藏部分组件\n    },\n  },\n  mounted: function() {\n    this.resolveExp();\n  },\n};\n</script>\n<style scoped>\n.pop_btn {\n  text-align: center;\n  margin-top: 20px;\n}\n.popup-main {\n  position: relative;\n  margin: 10px auto;\n  background: #fff;\n  border-radius: 5px;\n  font-size: 12px;\n  overflow: hidden;\n}\n.popup-title {\n  overflow: hidden;\n  line-height: 34px;\n  padding-top: 6px;\n  background: #f2f2f2;\n}\n.popup-result {\n  box-sizing: border-box;\n  line-height: 24px;\n  margin: 25px auto;\n  padding: 15px 10px 10px;\n  border: 1px solid #ccc;\n  position: relative;\n}\n.popup-result .title {\n  position: absolute;\n  top: -28px;\n  left: 50%;\n  width: 140px;\n  font-size: 14px;\n  margin-left: -70px;\n  text-align: center;\n  line-height: 30px;\n  background: #fff;\n}\n.popup-result table {\n  text-align: center;\n  width: 100%;\n  margin: 0 auto;\n}\n.popup-result table span {\n  display: block;\n  width: 100%;\n  font-family: arial;\n  line-height: 30px;\n  height: 30px;\n  white-space: nowrap;\n  overflow: hidden;\n  border: 1px solid #e8e8e8;\n}\n.popup-result-scroll {\n  font-size: 12px;\n  line-height: 24px;\n  height: 10em;\n  overflow-y: auto;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/Crontab/min.vue",
    "content": "<template>\n\t<el-form size=\"small\">\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"1\">\n\t\t\t\t分钟，允许的通配符[, - * /]\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"2\">\n\t\t\t\t周期从\n\t\t\t\t<el-input-number v-model='cycle01' :min=\"0\" :max=\"58\" /> -\n\t\t\t\t<el-input-number v-model='cycle02' :min=\"cycle01 ? cycle01 + 1 : 1\" :max=\"59\" /> 分钟\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"3\">\n\t\t\t\t从\n\t\t\t\t<el-input-number v-model='average01' :min=\"0\" :max=\"58\" /> 分钟开始，每\n\t\t\t\t<el-input-number v-model='average02' :min=\"1\" :max=\"59 - average01 || 0\" /> 分钟执行一次\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"4\">\n\t\t\t\t指定\n\t\t\t\t<el-select clearable v-model=\"checkboxList\" placeholder=\"可多选\" multiple style=\"width:100%\">\n\t\t\t\t\t<el-option v-for=\"item in 60\" :key=\"item\" :value=\"item-1\">{{item-1}}</el-option>\n\t\t\t\t</el-select>\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\t</el-form>\n\n</template>\n\n<script>\nexport default {\n\tdata() {\n\t\treturn {\n\t\t\tradioValue: 1,\n\t\t\tcycle01: 1,\n\t\t\tcycle02: 2,\n\t\t\taverage01: 0,\n\t\t\taverage02: 1,\n\t\t\tcheckboxList: [],\n\t\t\tcheckNum: this.$options.propsData.check\n\t\t}\n\t},\n\tname: 'crontab-min',\n\tprops: ['check', 'cron'],\n\tmethods: {\n\t\t// 单选按钮值变化时\n\t\tradioChange() {\n\t\t\tswitch (this.radioValue) {\n\t\t\t\tcase 1:\n\t\t\t\t\tthis.$emit('update', 'min', '*', 'min');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tthis.$emit('update', 'min', this.cycleTotal, 'min');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tthis.$emit('update', 'min', this.averageTotal, 'min');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tthis.$emit('update', 'min', this.checkboxString, 'min');\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\t\t// 周期两个值变化时\n\t\tcycleChange() {\n\t\t\tif (this.radioValue == '2') {\n\t\t\t\tthis.$emit('update', 'min', this.cycleTotal, 'min');\n\t\t\t}\n\t\t},\n\t\t// 平均两个值变化时\n\t\taverageChange() {\n\t\t\tif (this.radioValue == '3') {\n\t\t\t\tthis.$emit('update', 'min', this.averageTotal, 'min');\n\t\t\t}\n\t\t},\n\t\t// checkbox值变化时\n\t\tcheckboxChange() {\n\t\t\tif (this.radioValue == '4') {\n\t\t\t\tthis.$emit('update', 'min', this.checkboxString, 'min');\n\t\t\t}\n\t\t},\n\n\t},\n\twatch: {\n\t\t'radioValue': 'radioChange',\n\t\t'cycleTotal': 'cycleChange',\n\t\t'averageTotal': 'averageChange',\n\t\t'checkboxString': 'checkboxChange',\n\t},\n\tcomputed: {\n\t\t// 计算两个周期值\n\t\tcycleTotal: function () {\n\t\t\tconst cycle01 = this.checkNum(this.cycle01, 0, 58)\n\t\t\tconst cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 1, 59)\n\t\t\treturn cycle01 + '-' + cycle02;\n\t\t},\n\t\t// 计算平均用到的值\n\t\taverageTotal: function () {\n\t\t\tconst average01 = this.checkNum(this.average01, 0, 58)\n\t\t\tconst average02 = this.checkNum(this.average02, 1, 59 - average01 || 0)\n\t\t\treturn average01 + '/' + average02;\n\t\t},\n\t\t// 计算勾选的checkbox值合集\n\t\tcheckboxString: function () {\n\t\t\tlet str = this.checkboxList.join();\n\t\t\treturn str == '' ? '*' : str;\n\t\t}\n\t}\n}\n</script>"
  },
  {
    "path": "ruoyi-ui/src/components/Crontab/month.vue",
    "content": "<template>\n\t<el-form size='small'>\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"1\">\n\t\t\t\t月，允许的通配符[, - * /]\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"2\">\n\t\t\t\t周期从\n\t\t\t\t<el-input-number v-model='cycle01' :min=\"1\" :max=\"11\" /> -\n\t\t\t\t<el-input-number v-model='cycle02' :min=\"cycle01 ? cycle01 + 1 : 2\" :max=\"12\" /> 月\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"3\">\n\t\t\t\t从\n\t\t\t\t<el-input-number v-model='average01' :min=\"1\" :max=\"11\" /> 月开始，每\n\t\t\t\t<el-input-number v-model='average02' :min=\"1\" :max=\"12 - average01 || 0\" /> 月月执行一次\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"4\">\n\t\t\t\t指定\n\t\t\t\t<el-select clearable v-model=\"checkboxList\" placeholder=\"可多选\" multiple style=\"width:100%\">\n\t\t\t\t\t<el-option v-for=\"item in 12\" :key=\"item\" :value=\"item\">{{item}}</el-option>\n\t\t\t\t</el-select>\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\t</el-form>\n</template>\n\n<script>\nexport default {\n\tdata() {\n\t\treturn {\n\t\t\tradioValue: 1,\n\t\t\tcycle01: 1,\n\t\t\tcycle02: 2,\n\t\t\taverage01: 1,\n\t\t\taverage02: 1,\n\t\t\tcheckboxList: [],\n\t\t\tcheckNum: this.check\n\t\t}\n\t},\n\tname: 'crontab-month',\n\tprops: ['check', 'cron'],\n\tmethods: {\n\t\t// 单选按钮值变化时\n\t\tradioChange() {\n\t\t\tswitch (this.radioValue) {\n\t\t\t\tcase 1:\n\t\t\t\t\tthis.$emit('update', 'month', '*');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tthis.$emit('update', 'month', this.cycleTotal);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tthis.$emit('update', 'month', this.averageTotal);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tthis.$emit('update', 'month', this.checkboxString);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\t\t// 周期两个值变化时\n\t\tcycleChange() {\n\t\t\tif (this.radioValue == '2') {\n\t\t\t\tthis.$emit('update', 'month', this.cycleTotal);\n\t\t\t}\n\t\t},\n\t\t// 平均两个值变化时\n\t\taverageChange() {\n\t\t\tif (this.radioValue == '3') {\n\t\t\t\tthis.$emit('update', 'month', this.averageTotal);\n\t\t\t}\n\t\t},\n\t\t// checkbox值变化时\n\t\tcheckboxChange() {\n\t\t\tif (this.radioValue == '4') {\n\t\t\t\tthis.$emit('update', 'month', this.checkboxString);\n\t\t\t}\n\t\t}\n\t},\n\twatch: {\n\t\t'radioValue': 'radioChange',\n\t\t'cycleTotal': 'cycleChange',\n\t\t'averageTotal': 'averageChange',\n\t\t'checkboxString': 'checkboxChange'\n\t},\n\tcomputed: {\n\t\t// 计算两个周期值\n\t\tcycleTotal: function () {\n\t\t\tconst cycle01 = this.checkNum(this.cycle01, 1, 11)\n\t\t\tconst cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 2, 12)\n\t\t\treturn cycle01 + '-' + cycle02;\n\t\t},\n\t\t// 计算平均用到的值\n\t\taverageTotal: function () {\n\t\t\tconst average01 = this.checkNum(this.average01, 1, 11)\n\t\t\tconst average02 = this.checkNum(this.average02, 1, 12 - average01 || 0)\n\t\t\treturn average01 + '/' + average02;\n\t\t},\n\t\t// 计算勾选的checkbox值合集\n\t\tcheckboxString: function () {\n\t\t\tlet str = this.checkboxList.join();\n\t\t\treturn str == '' ? '*' : str;\n\t\t}\n\t}\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/components/Crontab/result.vue",
    "content": "<template>\n\t<div class=\"popup-result\">\n\t\t<p class=\"title\">最近5次运行时间</p>\n\t\t<ul class=\"popup-result-scroll\">\n\t\t\t<template v-if='isShow'>\n\t\t\t\t<li v-for='item in resultList' :key=\"item\">{{item}}</li>\n\t\t\t</template>\n\t\t\t<li v-else>计算结果中...</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script>\nexport default {\n\tdata() {\n\t\treturn {\n\t\t\tdayRule: '',\n\t\t\tdayRuleSup: '',\n\t\t\tdateArr: [],\n\t\t\tresultList: [],\n\t\t\tisShow: false\n\t\t}\n\t},\n\tname: 'crontab-result',\n\tmethods: {\n\t\t// 表达式值变化时，开始去计算结果\n\t\texpressionChange() {\n\n\t\t\t// 计算开始-隐藏结果\n\t\t\tthis.isShow = false;\n\t\t\t// 获取规则数组[0秒、1分、2时、3日、4月、5星期、6年]\n\t\t\tlet ruleArr = this.$options.propsData.ex.split(' ');\n\t\t\t// 用于记录进入循环的次数\n\t\t\tlet nums = 0;\n\t\t\t// 用于暂时存符号时间规则结果的数组\n\t\t\tlet resultArr = [];\n\t\t\t// 获取当前时间精确至[年、月、日、时、分、秒]\n\t\t\tlet nTime = new Date();\n\t\t\tlet nYear = nTime.getFullYear();\n\t\t\tlet nMonth = nTime.getMonth() + 1;\n\t\t\tlet nDay = nTime.getDate();\n\t\t\tlet nHour = nTime.getHours();\n\t\t\tlet nMin = nTime.getMinutes();\n\t\t\tlet nSecond = nTime.getSeconds();\n\t\t\t// 根据规则获取到近100年可能年数组、月数组等等\n\t\t\tthis.getSecondArr(ruleArr[0]);\n\t\t\tthis.getMinArr(ruleArr[1]);\n\t\t\tthis.getHourArr(ruleArr[2]);\n\t\t\tthis.getDayArr(ruleArr[3]);\n\t\t\tthis.getMonthArr(ruleArr[4]);\n\t\t\tthis.getWeekArr(ruleArr[5]);\n\t\t\tthis.getYearArr(ruleArr[6], nYear);\n\t\t\t// 将获取到的数组赋值-方便使用\n\t\t\tlet sDate = this.dateArr[0];\n\t\t\tlet mDate = this.dateArr[1];\n\t\t\tlet hDate = this.dateArr[2];\n\t\t\tlet DDate = this.dateArr[3];\n\t\t\tlet MDate = this.dateArr[4];\n\t\t\tlet YDate = this.dateArr[5];\n\t\t\t// 获取当前时间在数组中的索引\n\t\t\tlet sIdx = this.getIndex(sDate, nSecond);\n\t\t\tlet mIdx = this.getIndex(mDate, nMin);\n\t\t\tlet hIdx = this.getIndex(hDate, nHour);\n\t\t\tlet DIdx = this.getIndex(DDate, nDay);\n\t\t\tlet MIdx = this.getIndex(MDate, nMonth);\n\t\t\tlet YIdx = this.getIndex(YDate, nYear);\n\t\t\t// 重置月日时分秒的函数(后面用的比较多)\n\t\t\tconst resetSecond = function () {\n\t\t\t\tsIdx = 0;\n\t\t\t\tnSecond = sDate[sIdx]\n\t\t\t}\n\t\t\tconst resetMin = function () {\n\t\t\t\tmIdx = 0;\n\t\t\t\tnMin = mDate[mIdx]\n\t\t\t\tresetSecond();\n\t\t\t}\n\t\t\tconst resetHour = function () {\n\t\t\t\thIdx = 0;\n\t\t\t\tnHour = hDate[hIdx]\n\t\t\t\tresetMin();\n\t\t\t}\n\t\t\tconst resetDay = function () {\n\t\t\t\tDIdx = 0;\n\t\t\t\tnDay = DDate[DIdx]\n\t\t\t\tresetHour();\n\t\t\t}\n\t\t\tconst resetMonth = function () {\n\t\t\t\tMIdx = 0;\n\t\t\t\tnMonth = MDate[MIdx]\n\t\t\t\tresetDay();\n\t\t\t}\n\t\t\t// 如果当前年份不为数组中当前值\n\t\t\tif (nYear !== YDate[YIdx]) {\n\t\t\t\tresetMonth();\n\t\t\t}\n\t\t\t// 如果当前月份不为数组中当前值\n\t\t\tif (nMonth !== MDate[MIdx]) {\n\t\t\t\tresetDay();\n\t\t\t}\n\t\t\t// 如果当前“日”不为数组中当前值\n\t\t\tif (nDay !== DDate[DIdx]) {\n\t\t\t\tresetHour();\n\t\t\t}\n\t\t\t// 如果当前“时”不为数组中当前值\n\t\t\tif (nHour !== hDate[hIdx]) {\n\t\t\t\tresetMin();\n\t\t\t}\n\t\t\t// 如果当前“分”不为数组中当前值\n\t\t\tif (nMin !== mDate[mIdx]) {\n\t\t\t\tresetSecond();\n\t\t\t}\n\n\t\t\t// 循环年份数组\n\t\t\tgoYear: for (let Yi = YIdx; Yi < YDate.length; Yi++) {\n\t\t\t\tlet YY = YDate[Yi];\n\t\t\t\t// 如果到达最大值时\n\t\t\t\tif (nMonth > MDate[MDate.length - 1]) {\n\t\t\t\t\tresetMonth();\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// 循环月份数组\n\t\t\t\tgoMonth: for (let Mi = MIdx; Mi < MDate.length; Mi++) {\n\t\t\t\t\t// 赋值、方便后面运算\n\t\t\t\t\tlet MM = MDate[Mi];\n\t\t\t\t\tMM = MM < 10 ? '0' + MM : MM;\n\t\t\t\t\t// 如果到达最大值时\n\t\t\t\t\tif (nDay > DDate[DDate.length - 1]) {\n\t\t\t\t\t\tresetDay();\n\t\t\t\t\t\tif (Mi == MDate.length - 1) {\n\t\t\t\t\t\t\tresetMonth();\n\t\t\t\t\t\t\tcontinue goYear;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// 循环日期数组\n\t\t\t\t\tgoDay: for (let Di = DIdx; Di < DDate.length; Di++) {\n\t\t\t\t\t\t// 赋值、方便后面运算\n\t\t\t\t\t\tlet DD = DDate[Di];\n\t\t\t\t\t\tlet thisDD = DD < 10 ? '0' + DD : DD;\n\n\t\t\t\t\t\t// 如果到达最大值时\n\t\t\t\t\t\tif (nHour > hDate[hDate.length - 1]) {\n\t\t\t\t\t\t\tresetHour();\n\t\t\t\t\t\t\tif (Di == DDate.length - 1) {\n\t\t\t\t\t\t\t\tresetDay();\n\t\t\t\t\t\t\t\tif (Mi == MDate.length - 1) {\n\t\t\t\t\t\t\t\t\tresetMonth();\n\t\t\t\t\t\t\t\t\tcontinue goYear;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcontinue goMonth;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// 判断日期的合法性，不合法的话也是跳出当前循环\n\t\t\t\t\t\tif (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true && this.dayRule !== 'workDay' && this.dayRule !== 'lastWeek' && this.dayRule !== 'lastDay') {\n\t\t\t\t\t\t\tresetDay();\n\t\t\t\t\t\t\tcontinue goMonth;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// 如果日期规则中有值时\n\t\t\t\t\t\tif (this.dayRule == 'lastDay') {\n\t\t\t\t\t\t\t// 如果不是合法日期则需要将前将日期调到合法日期即月末最后一天\n\n\t\t\t\t\t\t\tif (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {\n\t\t\t\t\t\t\t\twhile (DD > 0 && this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {\n\t\t\t\t\t\t\t\t\tDD--;\n\n\t\t\t\t\t\t\t\t\tthisDD = DD < 10 ? '0' + DD : DD;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (this.dayRule == 'workDay') {\n\t\t\t\t\t\t\t// 校验并调整如果是2月30号这种日期传进来时需调整至正常月底\n\t\t\t\t\t\t\tif (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {\n\t\t\t\t\t\t\t\twhile (DD > 0 && this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {\n\t\t\t\t\t\t\t\t\tDD--;\n\t\t\t\t\t\t\t\t\tthisDD = DD < 10 ? '0' + DD : DD;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// 获取达到条件的日期是星期X\n\t\t\t\t\t\t\tlet thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week');\n\t\t\t\t\t\t\t// 当星期日时\n\t\t\t\t\t\t\tif (thisWeek == 1) {\n\t\t\t\t\t\t\t\t// 先找下一个日，并判断是否为月底\n\t\t\t\t\t\t\t\tDD++;\n\t\t\t\t\t\t\t\tthisDD = DD < 10 ? '0' + DD : DD;\n\t\t\t\t\t\t\t\t// 判断下一日已经不是合法日期\n\t\t\t\t\t\t\t\tif (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {\n\t\t\t\t\t\t\t\t\tDD -= 3;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if (thisWeek == 7) {\n\t\t\t\t\t\t\t\t// 当星期6时只需判断不是1号就可进行操作\n\t\t\t\t\t\t\t\tif (this.dayRuleSup !== 1) {\n\t\t\t\t\t\t\t\t\tDD--;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tDD += 2;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (this.dayRule == 'weekDay') {\n\t\t\t\t\t\t\t// 如果指定了是星期几\n\t\t\t\t\t\t\t// 获取当前日期是属于星期几\n\t\t\t\t\t\t\tlet thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week');\n\t\t\t\t\t\t\t// 校验当前星期是否在星期池（dayRuleSup）中\n\t\t\t\t\t\t\tif (this.dayRuleSup.indexOf(thisWeek) < 0) {\n\t\t\t\t\t\t\t\t// 如果到达最大值时\n\t\t\t\t\t\t\t\tif (Di == DDate.length - 1) {\n\t\t\t\t\t\t\t\t\tresetDay();\n\t\t\t\t\t\t\t\t\tif (Mi == MDate.length - 1) {\n\t\t\t\t\t\t\t\t\t\tresetMonth();\n\t\t\t\t\t\t\t\t\t\tcontinue goYear;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tcontinue goMonth;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (this.dayRule == 'assWeek') {\n\t\t\t\t\t\t\t// 如果指定了是第几周的星期几\n\t\t\t\t\t\t\t// 获取每月1号是属于星期几\n\t\t\t\t\t\t\tlet thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week');\n\t\t\t\t\t\t\tif (this.dayRuleSup[1] >= thisWeek) {\n\t\t\t\t\t\t\t\tDD = (this.dayRuleSup[0] - 1) * 7 + this.dayRuleSup[1] - thisWeek + 1;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tDD = this.dayRuleSup[0] * 7 + this.dayRuleSup[1] - thisWeek + 1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (this.dayRule == 'lastWeek') {\n\t\t\t\t\t\t\t// 如果指定了每月最后一个星期几\n\t\t\t\t\t\t\t// 校验并调整如果是2月30号这种日期传进来时需调整至正常月底\n\t\t\t\t\t\t\tif (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {\n\t\t\t\t\t\t\t\twhile (DD > 0 && this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {\n\t\t\t\t\t\t\t\t\tDD--;\n\t\t\t\t\t\t\t\t\tthisDD = DD < 10 ? '0' + DD : DD;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// 获取月末最后一天是星期几\n\t\t\t\t\t\t\tlet thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week');\n\t\t\t\t\t\t\t// 找到要求中最近的那个星期几\n\t\t\t\t\t\t\tif (this.dayRuleSup < thisWeek) {\n\t\t\t\t\t\t\t\tDD -= thisWeek - this.dayRuleSup;\n\t\t\t\t\t\t\t} else if (this.dayRuleSup > thisWeek) {\n\t\t\t\t\t\t\t\tDD -= 7 - (this.dayRuleSup - thisWeek)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// 判断时间值是否小于10置换成“05”这种格式\n\t\t\t\t\t\tDD = DD < 10 ? '0' + DD : DD;\n\n\t\t\t\t\t\t// 循环“时”数组\n\t\t\t\t\t\tgoHour: for (let hi = hIdx; hi < hDate.length; hi++) {\n\t\t\t\t\t\t\tlet hh = hDate[hi] < 10 ? '0' + hDate[hi] : hDate[hi]\n\n\t\t\t\t\t\t\t// 如果到达最大值时\n\t\t\t\t\t\t\tif (nMin > mDate[mDate.length - 1]) {\n\t\t\t\t\t\t\t\tresetMin();\n\t\t\t\t\t\t\t\tif (hi == hDate.length - 1) {\n\t\t\t\t\t\t\t\t\tresetHour();\n\t\t\t\t\t\t\t\t\tif (Di == DDate.length - 1) {\n\t\t\t\t\t\t\t\t\t\tresetDay();\n\t\t\t\t\t\t\t\t\t\tif (Mi == MDate.length - 1) {\n\t\t\t\t\t\t\t\t\t\t\tresetMonth();\n\t\t\t\t\t\t\t\t\t\t\tcontinue goYear;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tcontinue goMonth;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tcontinue goDay;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// 循环\"分\"数组\n\t\t\t\t\t\t\tgoMin: for (let mi = mIdx; mi < mDate.length; mi++) {\n\t\t\t\t\t\t\t\tlet mm = mDate[mi] < 10 ? '0' + mDate[mi] : mDate[mi];\n\n\t\t\t\t\t\t\t\t// 如果到达最大值时\n\t\t\t\t\t\t\t\tif (nSecond > sDate[sDate.length - 1]) {\n\t\t\t\t\t\t\t\t\tresetSecond();\n\t\t\t\t\t\t\t\t\tif (mi == mDate.length - 1) {\n\t\t\t\t\t\t\t\t\t\tresetMin();\n\t\t\t\t\t\t\t\t\t\tif (hi == hDate.length - 1) {\n\t\t\t\t\t\t\t\t\t\t\tresetHour();\n\t\t\t\t\t\t\t\t\t\t\tif (Di == DDate.length - 1) {\n\t\t\t\t\t\t\t\t\t\t\t\tresetDay();\n\t\t\t\t\t\t\t\t\t\t\t\tif (Mi == MDate.length - 1) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tresetMonth();\n\t\t\t\t\t\t\t\t\t\t\t\t\tcontinue goYear;\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\tcontinue goMonth;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tcontinue goDay;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tcontinue goHour;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// 循环\"秒\"数组\n\t\t\t\t\t\t\t\tgoSecond: for (let si = sIdx; si <= sDate.length - 1; si++) {\n\t\t\t\t\t\t\t\t\tlet ss = sDate[si] < 10 ? '0' + sDate[si] : sDate[si];\n\t\t\t\t\t\t\t\t\t// 添加当前时间（时间合法性在日期循环时已经判断）\n\t\t\t\t\t\t\t\t\tif (MM !== '00' && DD !== '00') {\n\t\t\t\t\t\t\t\t\t\tresultArr.push(YY + '-' + MM + '-' + DD + ' ' + hh + ':' + mm + ':' + ss)\n\t\t\t\t\t\t\t\t\t\tnums++;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t// 如果条数满了就退出循环\n\t\t\t\t\t\t\t\t\tif (nums == 5) break goYear;\n\t\t\t\t\t\t\t\t\t// 如果到达最大值时\n\t\t\t\t\t\t\t\t\tif (si == sDate.length - 1) {\n\t\t\t\t\t\t\t\t\t\tresetSecond();\n\t\t\t\t\t\t\t\t\t\tif (mi == mDate.length - 1) {\n\t\t\t\t\t\t\t\t\t\t\tresetMin();\n\t\t\t\t\t\t\t\t\t\t\tif (hi == hDate.length - 1) {\n\t\t\t\t\t\t\t\t\t\t\t\tresetHour();\n\t\t\t\t\t\t\t\t\t\t\t\tif (Di == DDate.length - 1) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tresetDay();\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (Mi == MDate.length - 1) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tresetMonth();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcontinue goYear;\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\tcontinue goMonth;\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\tcontinue goDay;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tcontinue goHour;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tcontinue goMin;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} //goSecond\n\t\t\t\t\t\t\t} //goMin\n\t\t\t\t\t\t}//goHour\n\t\t\t\t\t}//goDay\n\t\t\t\t}//goMonth\n\t\t\t}\n\t\t\t// 判断100年内的结果条数\n\t\t\tif (resultArr.length == 0) {\n\t\t\t\tthis.resultList = ['没有达到条件的结果！'];\n\t\t\t} else {\n\t\t\t\tthis.resultList = resultArr;\n\t\t\t\tif (resultArr.length !== 5) {\n\t\t\t\t\tthis.resultList.push('最近100年内只有上面' + resultArr.length + '条结果！')\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 计算完成-显示结果\n\t\t\tthis.isShow = true;\n\n\n\t\t},\n\t\t// 用于计算某位数字在数组中的索引\n\t\tgetIndex(arr, value) {\n\t\t\tif (value <= arr[0] || value > arr[arr.length - 1]) {\n\t\t\t\treturn 0;\n\t\t\t} else {\n\t\t\t\tfor (let i = 0; i < arr.length - 1; i++) {\n\t\t\t\t\tif (value > arr[i] && value <= arr[i + 1]) {\n\t\t\t\t\t\treturn i + 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t// 获取\"年\"数组\n\t\tgetYearArr(rule, year) {\n\t\t\tthis.dateArr[5] = this.getOrderArr(year, year + 100);\n\t\t\tif (rule !== undefined) {\n\t\t\t\tif (rule.indexOf('-') >= 0) {\n\t\t\t\t\tthis.dateArr[5] = this.getCycleArr(rule, year + 100, false)\n\t\t\t\t} else if (rule.indexOf('/') >= 0) {\n\t\t\t\t\tthis.dateArr[5] = this.getAverageArr(rule, year + 100)\n\t\t\t\t} else if (rule !== '*') {\n\t\t\t\t\tthis.dateArr[5] = this.getAssignArr(rule)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t// 获取\"月\"数组\n\t\tgetMonthArr(rule) {\n\t\t\tthis.dateArr[4] = this.getOrderArr(1, 12);\n\t\t\tif (rule.indexOf('-') >= 0) {\n\t\t\t\tthis.dateArr[4] = this.getCycleArr(rule, 12, false)\n\t\t\t} else if (rule.indexOf('/') >= 0) {\n\t\t\t\tthis.dateArr[4] = this.getAverageArr(rule, 12)\n\t\t\t} else if (rule !== '*') {\n\t\t\t\tthis.dateArr[4] = this.getAssignArr(rule)\n\t\t\t}\n\t\t},\n\t\t// 获取\"日\"数组-主要为日期规则\n\t\tgetWeekArr(rule) {\n\t\t\t// 只有当日期规则的两个值均为“”时则表达日期是有选项的\n\t\t\tif (this.dayRule == '' && this.dayRuleSup == '') {\n\t\t\t\tif (rule.indexOf('-') >= 0) {\n\t\t\t\t\tthis.dayRule = 'weekDay';\n\t\t\t\t\tthis.dayRuleSup = this.getCycleArr(rule, 7, false)\n\t\t\t\t} else if (rule.indexOf('#') >= 0) {\n\t\t\t\t\tthis.dayRule = 'assWeek';\n\t\t\t\t\tlet matchRule = rule.match(/[0-9]{1}/g);\n\t\t\t\t\tthis.dayRuleSup = [Number(matchRule[1]), Number(matchRule[0])];\n\t\t\t\t\tthis.dateArr[3] = [1];\n\t\t\t\t\tif (this.dayRuleSup[1] == 7) {\n\t\t\t\t\t\tthis.dayRuleSup[1] = 0;\n\t\t\t\t\t}\n\t\t\t\t} else if (rule.indexOf('L') >= 0) {\n\t\t\t\t\tthis.dayRule = 'lastWeek';\n\t\t\t\t\tthis.dayRuleSup = Number(rule.match(/[0-9]{1,2}/g)[0]);\n\t\t\t\t\tthis.dateArr[3] = [31];\n\t\t\t\t\tif (this.dayRuleSup == 7) {\n\t\t\t\t\t\tthis.dayRuleSup = 0;\n\t\t\t\t\t}\n\t\t\t\t} else if (rule !== '*' && rule !== '?') {\n\t\t\t\t\tthis.dayRule = 'weekDay';\n\t\t\t\t\tthis.dayRuleSup = this.getAssignArr(rule)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t// 获取\"日\"数组-少量为日期规则\n\t\tgetDayArr(rule) {\n\t\t\tthis.dateArr[3] = this.getOrderArr(1, 31);\n\t\t\tthis.dayRule = '';\n\t\t\tthis.dayRuleSup = '';\n\t\t\tif (rule.indexOf('-') >= 0) {\n\t\t\t\tthis.dateArr[3] = this.getCycleArr(rule, 31, false)\n\t\t\t\tthis.dayRuleSup = 'null';\n\t\t\t} else if (rule.indexOf('/') >= 0) {\n\t\t\t\tthis.dateArr[3] = this.getAverageArr(rule, 31)\n\t\t\t\tthis.dayRuleSup = 'null';\n\t\t\t} else if (rule.indexOf('W') >= 0) {\n\t\t\t\tthis.dayRule = 'workDay';\n\t\t\t\tthis.dayRuleSup = Number(rule.match(/[0-9]{1,2}/g)[0]);\n\t\t\t\tthis.dateArr[3] = [this.dayRuleSup];\n\t\t\t} else if (rule.indexOf('L') >= 0) {\n\t\t\t\tthis.dayRule = 'lastDay';\n\t\t\t\tthis.dayRuleSup = 'null';\n\t\t\t\tthis.dateArr[3] = [31];\n\t\t\t} else if (rule !== '*' && rule !== '?') {\n\t\t\t\tthis.dateArr[3] = this.getAssignArr(rule)\n\t\t\t\tthis.dayRuleSup = 'null';\n\t\t\t} else if (rule == '*') {\n\t\t\t\tthis.dayRuleSup = 'null';\n\t\t\t}\n\t\t},\n\t\t// 获取\"时\"数组\n\t\tgetHourArr(rule) {\n\t\t\tthis.dateArr[2] = this.getOrderArr(0, 23);\n\t\t\tif (rule.indexOf('-') >= 0) {\n\t\t\t\tthis.dateArr[2] = this.getCycleArr(rule, 24, true)\n\t\t\t} else if (rule.indexOf('/') >= 0) {\n\t\t\t\tthis.dateArr[2] = this.getAverageArr(rule, 23)\n\t\t\t} else if (rule !== '*') {\n\t\t\t\tthis.dateArr[2] = this.getAssignArr(rule)\n\t\t\t}\n\t\t},\n\t\t// 获取\"分\"数组\n\t\tgetMinArr(rule) {\n\t\t\tthis.dateArr[1] = this.getOrderArr(0, 59);\n\t\t\tif (rule.indexOf('-') >= 0) {\n\t\t\t\tthis.dateArr[1] = this.getCycleArr(rule, 60, true)\n\t\t\t} else if (rule.indexOf('/') >= 0) {\n\t\t\t\tthis.dateArr[1] = this.getAverageArr(rule, 59)\n\t\t\t} else if (rule !== '*') {\n\t\t\t\tthis.dateArr[1] = this.getAssignArr(rule)\n\t\t\t}\n\t\t},\n\t\t// 获取\"秒\"数组\n\t\tgetSecondArr(rule) {\n\t\t\tthis.dateArr[0] = this.getOrderArr(0, 59);\n\t\t\tif (rule.indexOf('-') >= 0) {\n\t\t\t\tthis.dateArr[0] = this.getCycleArr(rule, 60, true)\n\t\t\t} else if (rule.indexOf('/') >= 0) {\n\t\t\t\tthis.dateArr[0] = this.getAverageArr(rule, 59)\n\t\t\t} else if (rule !== '*') {\n\t\t\t\tthis.dateArr[0] = this.getAssignArr(rule)\n\t\t\t}\n\t\t},\n\t\t// 根据传进来的min-max返回一个顺序的数组\n\t\tgetOrderArr(min, max) {\n\t\t\tlet arr = [];\n\t\t\tfor (let i = min; i <= max; i++) {\n\t\t\t\tarr.push(i);\n\t\t\t}\n\t\t\treturn arr;\n\t\t},\n\t\t// 根据规则中指定的零散值返回一个数组\n\t\tgetAssignArr(rule) {\n\t\t\tlet arr = [];\n\t\t\tlet assiginArr = rule.split(',');\n\t\t\tfor (let i = 0; i < assiginArr.length; i++) {\n\t\t\t\tarr[i] = Number(assiginArr[i])\n\t\t\t}\n\t\t\tarr.sort(this.compare)\n\t\t\treturn arr;\n\t\t},\n\t\t// 根据一定算术规则计算返回一个数组\n\t\tgetAverageArr(rule, limit) {\n\t\t\tlet arr = [];\n\t\t\tlet agArr = rule.split('/');\n\t\t\tlet min = Number(agArr[0]);\n\t\t\tlet step = Number(agArr[1]);\n\t\t\twhile (min <= limit) {\n\t\t\t\tarr.push(min);\n\t\t\t\tmin += step;\n\t\t\t}\n\t\t\treturn arr;\n\t\t},\n\t\t// 根据规则返回一个具有周期性的数组\n\t\tgetCycleArr(rule, limit, status) {\n\t\t\t// status--表示是否从0开始（则从1开始）\n\t\t\tlet arr = [];\n\t\t\tlet cycleArr = rule.split('-');\n\t\t\tlet min = Number(cycleArr[0]);\n\t\t\tlet max = Number(cycleArr[1]);\n\t\t\tif (min > max) {\n\t\t\t\tmax += limit;\n\t\t\t}\n\t\t\tfor (let i = min; i <= max; i++) {\n\t\t\t\tlet add = 0;\n\t\t\t\tif (status == false && i % limit == 0) {\n\t\t\t\t\tadd = limit;\n\t\t\t\t}\n\t\t\t\tarr.push(Math.round(i % limit + add))\n\t\t\t}\n\t\t\tarr.sort(this.compare)\n\t\t\treturn arr;\n\t\t},\n\t\t// 比较数字大小（用于Array.sort）\n\t\tcompare(value1, value2) {\n\t\t\tif (value2 - value1 > 0) {\n\t\t\t\treturn -1;\n\t\t\t} else {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t},\n\t\t// 格式化日期格式如：2017-9-19 18:04:33\n\t\tformatDate(value, type) {\n\t\t\t// 计算日期相关值\n\t\t\tlet time = typeof value == 'number' ? new Date(value) : value;\n\t\t\tlet Y = time.getFullYear();\n\t\t\tlet M = time.getMonth() + 1;\n\t\t\tlet D = time.getDate();\n\t\t\tlet h = time.getHours();\n\t\t\tlet m = time.getMinutes();\n\t\t\tlet s = time.getSeconds();\n\t\t\tlet week = time.getDay();\n\t\t\t// 如果传递了type的话\n\t\t\tif (type == undefined) {\n\t\t\t\treturn Y + '-' + (M < 10 ? '0' + M : M) + '-' + (D < 10 ? '0' + D : D) + ' ' + (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);\n\t\t\t} else if (type == 'week') {\n\t\t\t\t// 在quartz中 1为星期日\n\t\t\t\treturn week + 1;\n\t\t\t}\n\t\t},\n\t\t// 检查日期是否存在\n\t\tcheckDate(value) {\n\t\t\tlet time = new Date(value);\n\t\t\tlet format = this.formatDate(time)\n\t\t\treturn value === format;\n\t\t}\n\t},\n\twatch: {\n\t\t'ex': 'expressionChange'\n\t},\n\tprops: ['ex'],\n\tmounted: function () {\n\t\t// 初始化 获取一次结果\n\t\tthis.expressionChange();\n\t}\n}\n\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/components/Crontab/second.vue",
    "content": "<template>\n\t<el-form size=\"small\">\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"1\">\n\t\t\t\t秒，允许的通配符[, - * /]\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"2\">\n\t\t\t\t周期从\n\t\t\t\t<el-input-number v-model='cycle01' :min=\"0\" :max=\"58\" /> -\n\t\t\t\t<el-input-number v-model='cycle02' :min=\"cycle01 ? cycle01 + 1 : 1\" :max=\"59\" /> 秒\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"3\">\n\t\t\t\t从\n\t\t\t\t<el-input-number v-model='average01' :min=\"0\" :max=\"58\" /> 秒开始，每\n\t\t\t\t<el-input-number v-model='average02' :min=\"1\" :max=\"59 - average01 || 0\" /> 秒执行一次\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"4\">\n\t\t\t\t指定\n\t\t\t\t<el-select clearable v-model=\"checkboxList\" placeholder=\"可多选\" multiple style=\"width:100%\">\n\t\t\t\t\t<el-option v-for=\"item in 60\" :key=\"item\" :value=\"item-1\">{{item-1}}</el-option>\n\t\t\t\t</el-select>\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\t</el-form>\n</template>\n\n<script>\nexport default {\n\tdata() {\n\t\treturn {\n\t\t\tradioValue: 1,\n\t\t\tcycle01: 1,\n\t\t\tcycle02: 2,\n\t\t\taverage01: 0,\n\t\t\taverage02: 1,\n\t\t\tcheckboxList: [],\n\t\t\tcheckNum: this.$options.propsData.check\n\t\t}\n\t},\n\tname: 'crontab-second',\n\tprops: ['check', 'radioParent'],\n\tmethods: {\n\t\t// 单选按钮值变化时\n\t\tradioChange() {\n\t\t\tswitch (this.radioValue) {\n\t\t\t\tcase 1:\n\t\t\t\t\tthis.$emit('update', 'second', '*', 'second');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tthis.$emit('update', 'second', this.cycleTotal);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tthis.$emit('update', 'second', this.averageTotal);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tthis.$emit('update', 'second', this.checkboxString);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\t\t// 周期两个值变化时\n\t\tcycleChange() {\n\t\t\tif (this.radioValue == '2') {\n\t\t\t\tthis.$emit('update', 'second', this.cycleTotal);\n\t\t\t}\n\t\t},\n\t\t// 平均两个值变化时\n\t\taverageChange() {\n\t\t\tif (this.radioValue == '3') {\n\t\t\t\tthis.$emit('update', 'second', this.averageTotal);\n\t\t\t}\n\t\t},\n\t\t// checkbox值变化时\n\t\tcheckboxChange() {\n\t\t\tif (this.radioValue == '4') {\n\t\t\t\tthis.$emit('update', 'second', this.checkboxString);\n\t\t\t}\n\t\t}\n\t},\n\twatch: {\n\t\t'radioValue': 'radioChange',\n\t\t'cycleTotal': 'cycleChange',\n\t\t'averageTotal': 'averageChange',\n\t\t'checkboxString': 'checkboxChange',\n\t\tradioParent() {\n\t\t\tthis.radioValue = this.radioParent\n\t\t}\n\t},\n\tcomputed: {\n\t\t// 计算两个周期值\n\t\tcycleTotal: function () {\n\t\t\tconst cycle01 = this.checkNum(this.cycle01, 0, 58)\n\t\t\tconst cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 1, 59)\n\t\t\treturn cycle01 + '-' + cycle02;\n\t\t},\n\t\t// 计算平均用到的值\n\t\taverageTotal: function () {\n\t\t\tconst average01 = this.checkNum(this.average01, 0, 58)\n\t\t\tconst average02 = this.checkNum(this.average02, 1, 59 - average01 || 0)\n\t\t\treturn average01 + '/' + average02;\n\t\t},\n\t\t// 计算勾选的checkbox值合集\n\t\tcheckboxString: function () {\n\t\t\tlet str = this.checkboxList.join();\n\t\t\treturn str == '' ? '*' : str;\n\t\t}\n\t}\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/components/Crontab/week.vue",
    "content": "<template>\n\t<el-form size='small'>\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"1\">\n\t\t\t\t周，允许的通配符[, - * ? / L #]\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"2\">\n\t\t\t\t不指定\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"3\">\n\t\t\t\t周期从星期\n\t\t\t\t<el-select clearable v-model=\"cycle01\">\n\t\t\t\t\t<el-option\n\t\t\t\t\t\tv-for=\"(item,index) of weekList\"\n\t\t\t\t\t\t:key=\"index\"\n\t\t\t\t\t\t:label=\"item.value\"\n\t\t\t\t\t\t:value=\"item.key\"\n\t\t\t\t\t\t:disabled=\"item.key === 1\"\n\t\t\t\t\t>{{item.value}}</el-option>\n\t\t\t\t</el-select>\n\t\t\t\t-\n\t\t\t\t<el-select clearable v-model=\"cycle02\">\n\t\t\t\t\t<el-option\n\t\t\t\t\t\tv-for=\"(item,index) of weekList\"\n\t\t\t\t\t\t:key=\"index\"\n\t\t\t\t\t\t:label=\"item.value\"\n\t\t\t\t\t\t:value=\"item.key\"\n\t\t\t\t\t\t:disabled=\"item.key < cycle01 && item.key !== 1\"\n\t\t\t\t\t>{{item.value}}</el-option>\n\t\t\t\t</el-select>\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"4\">\n\t\t\t\t第\n\t\t\t\t<el-input-number v-model='average01' :min=\"1\" :max=\"4\" /> 周的星期\n\t\t\t\t<el-select clearable v-model=\"average02\">\n\t\t\t\t\t<el-option v-for=\"(item,index) of weekList\" :key=\"index\" :label=\"item.value\" :value=\"item.key\">{{item.value}}</el-option>\n\t\t\t\t</el-select>\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"5\">\n\t\t\t\t本月最后一个星期\n\t\t\t\t<el-select clearable v-model=\"weekday\">\n\t\t\t\t\t<el-option v-for=\"(item,index) of weekList\" :key=\"index\" :label=\"item.value\" :value=\"item.key\">{{item.value}}</el-option>\n\t\t\t\t</el-select>\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio v-model='radioValue' :label=\"6\">\n\t\t\t\t指定\n\t\t\t\t<el-select clearable v-model=\"checkboxList\" placeholder=\"可多选\" multiple style=\"width:100%\">\n\t\t\t\t\t<el-option v-for=\"(item,index) of weekList\" :key=\"index\" :label=\"item.value\" :value=\"String(item.key)\">{{item.value}}</el-option>\n\t\t\t\t</el-select>\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t</el-form>\n</template>\n\n<script>\nexport default {\n\tdata() {\n\t\treturn {\n\t\t\tradioValue: 2,\n\t\t\tweekday: 2,\n\t\t\tcycle01: 2,\n\t\t\tcycle02: 3,\n\t\t\taverage01: 1,\n\t\t\taverage02: 2,\n\t\t\tcheckboxList: [],\n\t\t\tweekList: [\n\t\t\t\t{\n\t\t\t\t\tkey: 2,\n\t\t\t\t\tvalue: '星期一'\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tkey: 3,\n\t\t\t\t\tvalue: '星期二'\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tkey: 4,\n\t\t\t\t\tvalue: '星期三'\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tkey: 5,\n\t\t\t\t\tvalue: '星期四'\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tkey: 6,\n\t\t\t\t\tvalue: '星期五'\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tkey: 7,\n\t\t\t\t\tvalue: '星期六'\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tkey: 1,\n\t\t\t\t\tvalue: '星期日'\n\t\t\t\t}\n\t\t\t],\n\t\t\tcheckNum: this.$options.propsData.check\n\t\t}\n\t},\n\tname: 'crontab-week',\n\tprops: ['check', 'cron'],\n\tmethods: {\n\t\t// 单选按钮值变化时\n\t\tradioChange() {\n\t\t\tif (this.radioValue !== 2 && this.cron.day !== '?') {\n\t\t\t\tthis.$emit('update', 'day', '?', 'week');\n\t\t\t}\n\t\t\tswitch (this.radioValue) {\n\t\t\t\tcase 1:\n\t\t\t\t\tthis.$emit('update', 'week', '*');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tthis.$emit('update', 'week', '?');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tthis.$emit('update', 'week', this.cycleTotal);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tthis.$emit('update', 'week', this.averageTotal);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 5:\n\t\t\t\t\tthis.$emit('update', 'week', this.weekdayCheck + 'L');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 6:\n\t\t\t\t\tthis.$emit('update', 'week', this.checkboxString);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\n\t\t// 周期两个值变化时\n\t\tcycleChange() {\n\t\t\tif (this.radioValue == '3') {\n\t\t\t\tthis.$emit('update', 'week', this.cycleTotal);\n\t\t\t}\n\t\t},\n\t\t// 平均两个值变化时\n\t\taverageChange() {\n\t\t\tif (this.radioValue == '4') {\n\t\t\t\tthis.$emit('update', 'week', this.averageTotal);\n\t\t\t}\n\t\t},\n\t\t// 最近工作日值变化时\n\t\tweekdayChange() {\n\t\t\tif (this.radioValue == '5') {\n\t\t\t\tthis.$emit('update', 'week', this.weekday + 'L');\n\t\t\t}\n\t\t},\n\t\t// checkbox值变化时\n\t\tcheckboxChange() {\n\t\t\tif (this.radioValue == '6') {\n\t\t\t\tthis.$emit('update', 'week', this.checkboxString);\n\t\t\t}\n\t\t},\n\t},\n\twatch: {\n\t\t'radioValue': 'radioChange',\n\t\t'cycleTotal': 'cycleChange',\n\t\t'averageTotal': 'averageChange',\n\t\t'weekdayCheck': 'weekdayChange',\n\t\t'checkboxString': 'checkboxChange',\n\t},\n\tcomputed: {\n\t\t// 计算两个周期值\n\t\tcycleTotal: function () {\n\t\t\tthis.cycle01 = this.checkNum(this.cycle01, 1, 7)\n\t\t\tthis.cycle02 = this.checkNum(this.cycle02, 1, 7)\n\t\t\treturn this.cycle01 + '-' + this.cycle02;\n\t\t},\n\t\t// 计算平均用到的值\n\t\taverageTotal: function () {\n\t\t\tthis.average01 = this.checkNum(this.average01, 1, 4)\n\t\t\tthis.average02 = this.checkNum(this.average02, 1, 7)\n\t\t\treturn this.average02 + '#' + this.average01;\n\t\t},\n\t\t// 最近的工作日（格式）\n\t\tweekdayCheck: function () {\n\t\t\tthis.weekday = this.checkNum(this.weekday, 1, 7)\n\t\t\treturn this.weekday;\n\t\t},\n\t\t// 计算勾选的checkbox值合集\n\t\tcheckboxString: function () {\n\t\t\tlet str = this.checkboxList.join();\n\t\t\treturn str == '' ? '*' : str;\n\t\t}\n\t}\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/components/Crontab/year.vue",
    "content": "<template>\n\t<el-form size=\"small\">\n\t\t<el-form-item>\n\t\t\t<el-radio :label=\"1\" v-model='radioValue'>\n\t\t\t\t不填，允许的通配符[, - * /]\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio :label=\"2\" v-model='radioValue'>\n\t\t\t\t每年\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio :label=\"3\" v-model='radioValue'>\n\t\t\t\t周期从\n\t\t\t\t<el-input-number v-model='cycle01' :min='fullYear' :max=\"2098\" /> -\n\t\t\t\t<el-input-number v-model='cycle02' :min=\"cycle01 ? cycle01 + 1 : fullYear + 1\" :max=\"2099\" />\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio :label=\"4\" v-model='radioValue'>\n\t\t\t\t从\n\t\t\t\t<el-input-number v-model='average01' :min='fullYear' :max=\"2098\"/> 年开始，每\n\t\t\t\t<el-input-number v-model='average02' :min=\"1\" :max=\"2099 - average01 || fullYear\" /> 年执行一次\n\t\t\t</el-radio>\n\n\t\t</el-form-item>\n\n\t\t<el-form-item>\n\t\t\t<el-radio :label=\"5\" v-model='radioValue'>\n\t\t\t\t指定\n\t\t\t\t<el-select clearable v-model=\"checkboxList\" placeholder=\"可多选\" multiple>\n\t\t\t\t\t<el-option v-for=\"item in 9\" :key=\"item\" :value=\"item - 1 + fullYear\" :label=\"item -1 + fullYear\" />\n\t\t\t\t</el-select>\n\t\t\t</el-radio>\n\t\t</el-form-item>\n\t</el-form>\n</template>\n\n<script>\nexport default {\n\tdata() {\n\t\treturn {\n\t\t\tfullYear: 0,\n\t\t\tradioValue: 1,\n\t\t\tcycle01: 0,\n\t\t\tcycle02: 0,\n\t\t\taverage01: 0,\n\t\t\taverage02: 1,\n\t\t\tcheckboxList: [],\n\t\t\tcheckNum: this.$options.propsData.check\n\t\t}\n\t},\n\tname: 'crontab-year',\n\tprops: ['check', 'month', 'cron'],\n\tmethods: {\n\t\t// 单选按钮值变化时\n\t\tradioChange() {\n\t\t\tswitch (this.radioValue) {\n\t\t\t\tcase 1:\n\t\t\t\t\tthis.$emit('update', 'year', '');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tthis.$emit('update', 'year', '*');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tthis.$emit('update', 'year', this.cycleTotal);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tthis.$emit('update', 'year', this.averageTotal);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 5:\n\t\t\t\t\tthis.$emit('update', 'year', this.checkboxString);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\t\t// 周期两个值变化时\n\t\tcycleChange() {\n\t\t\tif (this.radioValue == '3') {\n\t\t\t\tthis.$emit('update', 'year', this.cycleTotal);\n\t\t\t}\n\t\t},\n\t\t// 平均两个值变化时\n\t\taverageChange() {\n\t\t\tif (this.radioValue == '4') {\n\t\t\t\tthis.$emit('update', 'year', this.averageTotal);\n\t\t\t}\n\t\t},\n\t\t// checkbox值变化时\n\t\tcheckboxChange() {\n\t\t\tif (this.radioValue == '5') {\n\t\t\t\tthis.$emit('update', 'year', this.checkboxString);\n\t\t\t}\n\t\t}\n\t},\n\twatch: {\n\t\t'radioValue': 'radioChange',\n\t\t'cycleTotal': 'cycleChange',\n\t\t'averageTotal': 'averageChange',\n\t\t'checkboxString': 'checkboxChange'\n\t},\n\tcomputed: {\n\t\t// 计算两个周期值\n\t\tcycleTotal: function () {\n\t\t\tconst cycle01 = this.checkNum(this.cycle01, this.fullYear, 2098)\n\t\t\tconst cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : this.fullYear + 1, 2099)\n\t\t\treturn cycle01 + '-' + cycle02;\n\t\t},\n\t\t// 计算平均用到的值\n\t\taverageTotal: function () {\n\t\t\tconst average01 = this.checkNum(this.average01, this.fullYear, 2098)\n\t\t\tconst average02 = this.checkNum(this.average02, 1, 2099 - average01 || this.fullYear)\n\t\t\treturn average01 + '/' + average02;\n\t\t},\n\t\t// 计算勾选的checkbox值合集\n\t\tcheckboxString: function () {\n\t\t\tlet str = this.checkboxList.join();\n\t\t\treturn str;\n\t\t}\n\t},\n\tmounted: function () {\n\t\t// 仅获取当前年份\n\t\tthis.fullYear = Number(new Date().getFullYear());\n\t\tthis.cycle01 = this.fullYear\n\t\tthis.average01 = this.fullYear\n\t}\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/components/DictData/index.js",
    "content": "import Vue from 'vue'\nimport store from '@/store'\nimport DataDict from '@/utils/dict'\nimport { getDicts as getDicts } from '@/api/system/dict/data'\n\nfunction searchDictByKey(dict, key) {\n  if (key == null && key == \"\") {\n    return null\n  }\n  try {\n    for (let i = 0; i < dict.length; i++) {\n      if (dict[i].key == key) {\n        return dict[i].value\n      }\n    }\n  } catch (e) {\n    return null\n  }\n}\n\nfunction install() {\n  Vue.use(DataDict, {\n    metas: {\n      '*': {\n        labelField: 'dictLabel',\n        valueField: 'dictValue',\n        request(dictMeta) {\n          const storeDict = searchDictByKey(store.getters.dict, dictMeta.type)\n          if (storeDict) {\n            return new Promise(resolve => { resolve(storeDict) })\n          } else {\n            return new Promise((resolve, reject) => {\n              getDicts(dictMeta.type).then(res => {\n                store.dispatch('dict/setDict', { key: dictMeta.type, value: res.data })\n                resolve(res.data)\n              }).catch(error => {\n                reject(error)\n              })\n            })\n          }\n        },\n      },\n    },\n  })\n}\n\nexport default {\n  install,\n}"
  },
  {
    "path": "ruoyi-ui/src/components/DictTag/index.vue",
    "content": "<template>\n  <div>\n    <template v-for=\"(item, index) in options\">\n      <template v-if=\"values.includes(item.value)\">\n        <span\n          v-if=\"item.raw.listClass == 'default' || item.raw.listClass == ''\"\n          :key=\"item.value\"\n          :index=\"index\"\n          :class=\"item.raw.cssClass\"\n          >{{ item.label + ' ' }}</span\n        >\n        <el-tag\n          v-else\n          :disable-transitions=\"true\"\n          :key=\"item.value\"\n          :index=\"index\"\n          :type=\"item.raw.listClass == 'primary' ? '' : item.raw.listClass\"\n          :class=\"item.raw.cssClass\"\n        >\n          {{ item.label + ' ' }}\n        </el-tag>\n      </template>\n    </template>\n    <template v-if=\"unmatch && showValue\">\n      {{ unmatchArray | handleArray }}\n    </template>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: \"DictTag\",\n  props: {\n    options: {\n      type: Array,\n      default: null,\n    },\n    value: [Number, String, Array],\n    // 当未找到匹配的数据时，显示value\n    showValue: {\n      type: Boolean,\n      default: true,\n    }\n  },\n  data() {\n    return {\n      unmatchArray: [], // 记录未匹配的项\n    }\n  },\n  computed: {\n    values() {\n      if (this.value !== null && typeof this.value !== 'undefined') {\n        return Array.isArray(this.value) ? this.value : [String(this.value)];\n      } else {\n        return [];\n      }\n    },\n    unmatch(){\n      this.unmatchArray = [];\n      if (this.value !== null && typeof this.value !== 'undefined') {\n        // 传入值为非数组\n        if(!Array.isArray(this.value)){\n          if (this.options !== null && typeof this.options !== 'undefined' && this.options.length > 0) {\n            // 进行'some'函数的操作\n            if(this.options.some(v=> v.value == this.value )) return false;\n            this.unmatchArray.push(this.value);\n            return true;\n          } else {\n            // 选项数组为空或null时的处理逻辑\n            return false\n          }\n\n        }\n        // 传入值为Array\n        this.value.forEach(item => {\n          if (this.options !== null && typeof this.options !== 'undefined' && this.options.length > 0) {\n            // 进行'some'函数的操作\n            if (!this.options.some(v=> v.value == item )) this.unmatchArray.push(item)\n          } else {\n            // 选项数组为空或null时的处理逻辑\n            return false\n          }\n\n        });\n        return true;\n      }\n      // 没有value不显示\n      return false;\n    },\n\n  },\n  filters: {\n    handleArray(array) {\n      if(array.length===0) return '';\n      return array.reduce((pre, cur) => pre + ' ' + cur)\n    },\n  }\n};\n</script>\n<style scoped>\n.el-tag + .el-tag {\n  margin-left: 10px;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/Editor/index.vue",
    "content": "<template>\n  <div>\n    <el-upload\n      :action=\"uploadUrl\"\n      :before-upload=\"handleBeforeUpload\"\n      :on-success=\"handleUploadSuccess\"\n      :on-error=\"handleUploadError\"\n      name=\"file\"\n      :show-file-list=\"false\"\n      :headers=\"headers\"\n      style=\"display: none\"\n      ref=\"upload\"\n      v-if=\"this.type == 'url'\"\n    >\n    </el-upload>\n    <div class=\"editor\" ref=\"editor\" :style=\"styles\"></div>\n  </div>\n</template>\n\n<script>\nimport Quill from \"quill\";\nimport \"quill/dist/quill.core.css\";\nimport \"quill/dist/quill.snow.css\";\nimport \"quill/dist/quill.bubble.css\";\nimport { getToken } from \"@/utils/auth\";\n\nexport default {\n  name: \"Editor\",\n  props: {\n    /* 编辑器的内容 */\n    value: {\n      type: String,\n      default: \"\",\n    },\n    /* 高度 */\n    height: {\n      type: Number,\n      default: null,\n    },\n    /* 最小高度 */\n    minHeight: {\n      type: Number,\n      default: null,\n    },\n    /* 只读 */\n    readOnly: {\n      type: Boolean,\n      default: false,\n    },\n    // 上传文件大小限制(MB)\n    fileSize: {\n      type: Number,\n      default: 5,\n    },\n    /* 类型（base64格式、url格式） */\n    type: {\n      type: String,\n      default: \"url\",\n    }\n  },\n  data() {\n    return {\n      uploadUrl: process.env.VUE_APP_BASE_API + \"/system/oss/upload\", // 上传的图片服务器地址\n      headers: {\n        Authorization: \"Bearer \" + getToken()\n      },\n      Quill: null,\n      currentValue: \"\",\n      options: {\n        theme: \"snow\",\n        bounds: document.body,\n        debug: \"warn\",\n        modules: {\n          // 工具栏配置\n          toolbar: [\n            [\"bold\", \"italic\", \"underline\", \"strike\"],       // 加粗 斜体 下划线 删除线\n            [\"blockquote\", \"code-block\"],                    // 引用  代码块\n            [{ list: \"ordered\" }, { list: \"bullet\" }],       // 有序、无序列表\n            [{ indent: \"-1\" }, { indent: \"+1\" }],            // 缩进\n            [{ size: [\"small\", false, \"large\", \"huge\"] }],   // 字体大小\n            [{ header: [1, 2, 3, 4, 5, 6, false] }],         // 标题\n            [{ color: [] }, { background: [] }],             // 字体颜色、字体背景颜色\n            [{ align: [] }],                                 // 对齐方式\n            [\"clean\"],                                       // 清除文本格式\n            [\"link\", \"image\", \"video\"]                       // 链接、图片、视频\n          ],\n        },\n        placeholder: \"请输入内容\",\n        readOnly: this.readOnly,\n      },\n    };\n  },\n  computed: {\n    styles() {\n      let style = {};\n      if (this.minHeight) {\n        style.minHeight = `${this.minHeight}px`;\n      }\n      if (this.height) {\n        style.height = `${this.height}px`;\n      }\n      return style;\n    },\n  },\n  watch: {\n    value: {\n      handler(val) {\n        if (val !== this.currentValue) {\n          this.currentValue = val === null ? \"\" : val;\n          if (this.Quill) {\n            this.Quill.pasteHTML(this.currentValue);\n          }\n        }\n      },\n      immediate: true,\n    },\n  },\n  mounted() {\n    this.init();\n  },\n  beforeDestroy() {\n    this.Quill = null;\n  },\n  methods: {\n    init() {\n      const editor = this.$refs.editor;\n      this.Quill = new Quill(editor, this.options);\n      // 如果设置了上传地址则自定义图片上传事件\n      if (this.type == 'url') {\n        let toolbar = this.Quill.getModule(\"toolbar\");\n        toolbar.addHandler(\"image\", (value) => {\n          this.uploadType = \"image\";\n          if (value) {\n            this.$refs.upload.$children[0].$refs.input.click();\n          } else {\n            this.quill.format(\"image\", false);\n          }\n        });\n      }\n      this.Quill.pasteHTML(this.currentValue);\n      this.Quill.on(\"text-change\", (delta, oldDelta, source) => {\n        const html = this.$refs.editor.children[0].innerHTML;\n        const text = this.Quill.getText();\n        const quill = this.Quill;\n        this.currentValue = html;\n        this.$emit(\"input\", html);\n        this.$emit(\"on-change\", { html, text, quill });\n      });\n      this.Quill.on(\"text-change\", (delta, oldDelta, source) => {\n        this.$emit(\"on-text-change\", delta, oldDelta, source);\n      });\n      this.Quill.on(\"selection-change\", (range, oldRange, source) => {\n        this.$emit(\"on-selection-change\", range, oldRange, source);\n      });\n      this.Quill.on(\"editor-change\", (eventName, ...args) => {\n        this.$emit(\"on-editor-change\", eventName, ...args);\n      });\n    },\n    // 上传前校检格式和大小\n    handleBeforeUpload(file) {\n      // 校检文件大小\n      if (this.fileSize) {\n        const isLt = file.size / 1024 / 1024 < this.fileSize;\n        if (!isLt) {\n          this.$message.error(`上传文件大小不能超过 ${this.fileSize} MB!`);\n          return false;\n        }\n      }\n      return true;\n    },\n    handleUploadSuccess(res, file) {\n      // 获取富文本组件实例\n      let quill = this.Quill;\n      // 如果上传成功\n      if (res.code == 200) {\n        // 获取光标所在位置\n        let length = quill.getSelection().index;\n        // 插入图片  res.url为服务器返回的图片地址\n        quill.insertEmbed(length, \"image\", res.data.url);\n        // 调整光标到最后\n        quill.setSelection(length + 1);\n      } else {\n        this.$message.error(res.msg);\n      }\n    },\n    handleUploadError() {\n      this.$message.error(\"图片插入失败\");\n    },\n  },\n};\n</script>\n\n<style>\n.editor, .ql-toolbar {\n  white-space: pre-wrap !important;\n  line-height: normal !important;\n}\n.quill-img {\n  display: none;\n}\n.ql-snow .ql-tooltip[data-mode=\"link\"]::before {\n  content: \"请输入链接地址:\";\n}\n.ql-snow .ql-tooltip.ql-editing a.ql-action::after {\n  border-right: 0px;\n  content: \"保存\";\n  padding-right: 0px;\n}\n\n.ql-snow .ql-tooltip[data-mode=\"video\"]::before {\n  content: \"请输入视频地址:\";\n}\n\n.ql-snow .ql-picker.ql-size .ql-picker-label::before,\n.ql-snow .ql-picker.ql-size .ql-picker-item::before {\n  content: \"14px\";\n}\n.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=\"small\"]::before,\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=\"small\"]::before {\n  content: \"10px\";\n}\n.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=\"large\"]::before,\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=\"large\"]::before {\n  content: \"18px\";\n}\n.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=\"huge\"]::before,\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=\"huge\"]::before {\n  content: \"32px\";\n}\n\n.ql-snow .ql-picker.ql-header .ql-picker-label::before,\n.ql-snow .ql-picker.ql-header .ql-picker-item::before {\n  content: \"文本\";\n}\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value=\"1\"]::before,\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"1\"]::before {\n  content: \"标题1\";\n}\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value=\"2\"]::before,\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"2\"]::before {\n  content: \"标题2\";\n}\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value=\"3\"]::before,\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"3\"]::before {\n  content: \"标题3\";\n}\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value=\"4\"]::before,\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"4\"]::before {\n  content: \"标题4\";\n}\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value=\"5\"]::before,\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"5\"]::before {\n  content: \"标题5\";\n}\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value=\"6\"]::before,\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"6\"]::before {\n  content: \"标题6\";\n}\n\n.ql-snow .ql-picker.ql-font .ql-picker-label::before,\n.ql-snow .ql-picker.ql-font .ql-picker-item::before {\n  content: \"标准字体\";\n}\n.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=\"serif\"]::before,\n.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=\"serif\"]::before {\n  content: \"衬线字体\";\n}\n.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=\"monospace\"]::before,\n.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=\"monospace\"]::before {\n  content: \"等宽字体\";\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/FileUpload/index.vue",
    "content": "<template>\n  <div class=\"upload-file\">\n    <el-upload\n      multiple\n      :action=\"uploadFileUrl\"\n      :before-upload=\"handleBeforeUpload\"\n      :file-list=\"fileList\"\n      :limit=\"limit\"\n      :on-error=\"handleUploadError\"\n      :on-exceed=\"handleExceed\"\n      :on-success=\"handleUploadSuccess\"\n      :show-file-list=\"false\"\n      :headers=\"headers\"\n      class=\"upload-file-uploader\"\n      ref=\"fileUpload\"\n    >\n      <!-- 上传按钮 -->\n      <el-button size=\"mini\" type=\"primary\">选取文件</el-button>\n      <!-- 上传提示 -->\n      <div class=\"el-upload__tip\" slot=\"tip\" v-if=\"showTip\">\n        请上传\n        <template v-if=\"fileSize\"> 大小不超过 <b style=\"color: #f56c6c\">{{ fileSize }}MB</b> </template>\n        <template v-if=\"fileType\"> 格式为 <b style=\"color: #f56c6c\">{{ fileType.join(\"/\") }}</b> </template>\n        的文件\n      </div>\n    </el-upload>\n\n    <!-- 文件列表 -->\n    <transition-group class=\"upload-file-list el-upload-list el-upload-list--text\" name=\"el-fade-in-linear\" tag=\"ul\">\n      <li :key=\"file.url\" class=\"el-upload-list__item ele-upload-list__item-content\" v-for=\"(file, index) in fileList\">\n        <el-link :href=\"`${file.url}`\" :underline=\"false\" target=\"_blank\">\n          <span class=\"el-icon-document\"> {{ getFileName(file.name) }} </span>\n        </el-link>\n        <div class=\"ele-upload-list__item-content-action\">\n          <el-link :underline=\"false\" @click=\"handleDelete(index)\" type=\"danger\">删除</el-link>\n        </div>\n      </li>\n    </transition-group>\n  </div>\n</template>\n\n<script>\nimport { getToken } from \"@/utils/auth\";\nimport { listByIds, delOss } from \"@/api/system/oss\";\n\nexport default {\n  name: \"FileUpload\",\n  props: {\n    // 值\n    value: [String, Object, Array],\n    // 数量限制\n    limit: {\n      type: Number,\n      default: 5,\n    },\n    // 大小限制(MB)\n    fileSize: {\n      type: Number,\n      default: 50,\n    },\n    // 文件类型, 例如['png', 'jpg', 'jpeg']\n    fileType: {\n      type: Array,\n      default: () => [\"doc\", \"xls\", \"ppt\", \"txt\", \"pdf\",\"wav\",\"mp3\"],\n    },\n    // 是否显示提示\n    isShowTip: {\n      type: Boolean,\n      default: true\n    }\n  },\n  data() {\n    return {\n      number: 0,\n      uploadList: [],\n      baseUrl: process.env.VUE_APP_BASE_API,\n      uploadFileUrl: process.env.VUE_APP_BASE_API + \"/system/oss/upload\", // 上传文件服务器地址\n      headers: {\n        Authorization: \"Bearer \" + getToken(),\n      },\n      fileList: [],\n    };\n  },\n  watch: {\n    value: {\n      async handler(val) {\n        if (val) {\n          let temp = 1;\n          // 首先将值转为数组\n          let list;\n          if (Array.isArray(val)) {\n            list = val;\n          } else {\n            await listByIds(val).then(res => {\n              list = res.data.map(oss => {\n                oss = { name: oss.originalName, url: oss.url, ossId: oss.ossId };\n                return oss;\n              });\n            })\n          }\n          // 然后将数组转为对象数组\n          this.fileList = list.map(item => {\n            item = { name: item.name, url: item.url, ossId: item.ossId };\n            item.uid = item.uid || new Date().getTime() + temp++;\n            return item;\n          });\n        } else {\n          this.fileList = [];\n          return [];\n        }\n      },\n      deep: true,\n      immediate: true\n    }\n  },\n  computed: {\n    // 是否显示提示\n    showTip() {\n      return this.isShowTip && (this.fileType || this.fileSize);\n    },\n  },\n  methods: {\n    // 上传前校检格式和大小\n    handleBeforeUpload(file) {\n      // 校检文件类型\n      if (this.fileType) {\n        const fileName = file.name.split('.');\n        const fileExt = fileName[fileName.length - 1];\n        const isTypeOk = this.fileType.indexOf(fileExt) >= 0;\n        if (!isTypeOk) {\n          this.$modal.msgError(`文件格式不正确, 请上传${this.fileType.join(\"/\")}格式文件!`);\n          return false;\n        }\n      }\n      // 校检文件大小\n      if (this.fileSize) {\n        const isLt = file.size / 1024 / 1024 < this.fileSize;\n        if (!isLt) {\n          this.$modal.msgError(`上传文件大小不能超过 ${this.fileSize} MB!`);\n          return false;\n        }\n      }\n      this.$modal.loading(\"正在上传文件，请稍候...\");\n      this.number++;\n      return true;\n    },\n    // 文件个数超出\n    handleExceed() {\n      this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);\n    },\n    // 上传失败\n    handleUploadError(err) {\n      this.$modal.msgError(\"上传文件失败，请重试\");\n      this.$modal.closeLoading();\n    },\n    // 上传成功回调\n    handleUploadSuccess(res, file) {\n      if (res.code === 200) {\n        this.uploadList.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });\n        this.uploadedSuccessfully();\n      } else {\n        this.number--;\n        this.$modal.closeLoading();\n        this.$modal.msgError(res.msg);\n        this.$refs.fileUpload.handleRemove(file);\n        this.uploadedSuccessfully();\n      }\n    },\n    // 删除文件\n    handleDelete(index) {\n      let ossId = this.fileList[index].ossId;\n      delOss(ossId);\n      this.fileList.splice(index, 1);\n      this.$emit(\"input\", this.listToString(this.fileList));\n    },\n    // 上传结束处理\n    uploadedSuccessfully() {\n      if (this.number > 0 && this.uploadList.length === this.number) {\n        this.fileList = this.fileList.concat(this.uploadList);\n        this.uploadList = [];\n        this.number = 0;\n        this.$emit(\"input\", this.listToString(this.fileList));\n        this.$modal.closeLoading();\n      }\n    },\n    // 获取文件名称\n    getFileName(name) {\n      // 如果是url那么取最后的名字 如果不是直接返回\n      if (name.lastIndexOf(\"/\") > -1) {\n        return name.slice(name.lastIndexOf(\"/\") + 1);\n      } else {\n        return name;\n      }\n    },\n    // 对象转成指定字符串分隔\n    listToString(list, separator) {\n      let strs = \"\";\n      separator = separator || \",\";\n      for (let i in list) {\n        strs += list[i].ossId + separator;\n      }\n      return strs != \"\" ? strs.substr(0, strs.length - 1) : \"\";\n    },\n  },\n};\n</script>\n\n<style scoped lang=\"scss\">\n.upload-file-uploader {\n  margin-bottom: 5px;\n}\n.upload-file-list .el-upload-list__item {\n  border: 1px solid #e4e7ed;\n  line-height: 2;\n  margin-bottom: 10px;\n  position: relative;\n}\n.upload-file-list .ele-upload-list__item-content {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  color: inherit;\n}\n.ele-upload-list__item-content-action .el-link {\n  margin-right: 10px;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/Hamburger/index.vue",
    "content": "<template>\n  <div style=\"padding: 0 15px;\" @click=\"toggleClick\">\n    <svg\n      :class=\"{'is-active':isActive}\"\n      class=\"hamburger\"\n      viewBox=\"0 0 1024 1024\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"64\"\n      height=\"64\"\n    >\n      <path d=\"M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z\" />\n    </svg>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'Hamburger',\n  props: {\n    isActive: {\n      type: Boolean,\n      default: false\n    }\n  },\n  methods: {\n    toggleClick() {\n      this.$emit('toggleClick')\n    }\n  }\n}\n</script>\n\n<style scoped>\n.hamburger {\n  display: inline-block;\n  vertical-align: middle;\n  width: 20px;\n  height: 20px;\n}\n\n.hamburger.is-active {\n  transform: rotate(180deg);\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/HeaderSearch/index.vue",
    "content": "<template>\n  <div :class=\"{'show':show}\" class=\"header-search\">\n    <svg-icon class-name=\"search-icon\" icon-class=\"search\" @click.stop=\"click\" />\n    <el-select\n      ref=\"headerSearchSelect\"\n      v-model=\"search\"\n      :remote-method=\"querySearch\"\n      filterable\n      default-first-option\n      remote\n      placeholder=\"Search\"\n      class=\"header-search-select\"\n      @change=\"change\"\n    >\n      <el-option v-for=\"option in options\" :key=\"option.item.path\" :value=\"option.item\" :label=\"option.item.title.join(' > ')\" />\n    </el-select>\n  </div>\n</template>\n\n<script>\n// fuse is a lightweight fuzzy-search module\n// make search results more in line with expectations\nimport Fuse from 'fuse.js/dist/fuse.min.js'\nimport path from 'path'\n\nexport default {\n  name: 'HeaderSearch',\n  data() {\n    return {\n      search: '',\n      options: [],\n      searchPool: [],\n      show: false,\n      fuse: undefined\n    }\n  },\n  computed: {\n    routes() {\n      return this.$store.getters.permission_routes\n    }\n  },\n  watch: {\n    routes() {\n      this.searchPool = this.generateRoutes(this.routes)\n    },\n    searchPool(list) {\n      this.initFuse(list)\n    },\n    show(value) {\n      if (value) {\n        document.body.addEventListener('click', this.close)\n      } else {\n        document.body.removeEventListener('click', this.close)\n      }\n    }\n  },\n  mounted() {\n    this.searchPool = this.generateRoutes(this.routes)\n  },\n  methods: {\n    click() {\n      this.show = !this.show\n      if (this.show) {\n        this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus()\n      }\n    },\n    close() {\n      this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur()\n      this.options = []\n      this.show = false\n    },\n    change(val) {\n      const path = val.path;\n      if(this.ishttp(val.path)) {\n        // http(s):// 路径新窗口打开\n        const pindex = path.indexOf(\"http\");\n        window.open(path.substr(pindex, path.length), \"_blank\");\n      } else {\n        this.$router.push(val.path)\n      }\n      this.search = ''\n      this.options = []\n      this.$nextTick(() => {\n        this.show = false\n      })\n    },\n    initFuse(list) {\n      this.fuse = new Fuse(list, {\n        shouldSort: true,\n        threshold: 0.4,\n        location: 0,\n        distance: 100,\n        minMatchCharLength: 1,\n        keys: [{\n          name: 'title',\n          weight: 0.7\n        }, {\n          name: 'path',\n          weight: 0.3\n        }]\n      })\n    },\n    // Filter out the routes that can be displayed in the sidebar\n    // And generate the internationalized title\n    generateRoutes(routes, basePath = '/', prefixTitle = []) {\n      let res = []\n\n      for (const router of routes) {\n        // skip hidden router\n        if (router.hidden) { continue }\n\n        const data = {\n          path: !this.ishttp(router.path) ? path.resolve(basePath, router.path) : router.path,\n          title: [...prefixTitle]\n        }\n\n        if (router.meta && router.meta.title) {\n          data.title = [...data.title, router.meta.title]\n\n          if (router.redirect !== 'noRedirect') {\n            // only push the routes with title\n            // special case: need to exclude parent router without redirect\n            res.push(data)\n          }\n        }\n\n        // recursive child routes\n        if (router.children) {\n          const tempRoutes = this.generateRoutes(router.children, data.path, data.title)\n          if (tempRoutes.length >= 1) {\n            res = [...res, ...tempRoutes]\n          }\n        }\n      }\n      return res\n    },\n    querySearch(query) {\n      if (query !== '') {\n        this.options = this.fuse.search(query)\n      } else {\n        this.options = []\n      }\n    },\n    ishttp(url) {\n      return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.header-search {\n  font-size: 0 !important;\n\n  .search-icon {\n    cursor: pointer;\n    font-size: 18px;\n    vertical-align: middle;\n  }\n\n  .header-search-select {\n    font-size: 18px;\n    transition: width 0.2s;\n    width: 0;\n    overflow: hidden;\n    background: transparent;\n    border-radius: 0;\n    display: inline-block;\n    vertical-align: middle;\n\n    ::v-deep .el-input__inner {\n      border-radius: 0;\n      border: 0;\n      padding-left: 0;\n      padding-right: 0;\n      box-shadow: none !important;\n      border-bottom: 1px solid #d9d9d9;\n      vertical-align: middle;\n    }\n  }\n\n  &.show {\n    .header-search-select {\n      width: 210px;\n      margin-left: 10px;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/IconSelect/index.vue",
    "content": "<!-- @author zhengjie -->\n<template>\n  <div class=\"icon-body\">\n    <el-input v-model=\"name\" class=\"icon-search\" clearable placeholder=\"请输入图标名称\" @clear=\"filterIcons\" @input=\"filterIcons\">\n      <i slot=\"suffix\" class=\"el-icon-search el-input__icon\" />\n    </el-input>\n    <div class=\"icon-list\">\n      <div class=\"list-container\">\n        <div v-for=\"(item, index) in iconList\" class=\"icon-item-wrapper\" :key=\"index\" @click=\"selectedIcon(item)\">\n          <div :class=\"['icon-item', { active: activeIcon === item }]\">\n            <svg-icon :icon-class=\"item\" class-name=\"icon\" style=\"height: 25px;width: 16px;\"/>\n            <span>{{ item }}</span>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport icons from './requireIcons'\nexport default {\n  name: 'IconSelect',\n  props: {\n    activeIcon: {\n      type: String\n    }\n  },\n  data() {\n    return {\n      name: '',\n      iconList: icons\n    }\n  },\n  methods: {\n    filterIcons() {\n      this.iconList = icons\n      if (this.name) {\n        this.iconList = this.iconList.filter(item => item.includes(this.name))\n      }\n    },\n    selectedIcon(name) {\n      this.$emit('selected', name)\n      document.body.click()\n    },\n    reset() {\n      this.name = ''\n      this.iconList = icons\n    }\n  }\n}\n</script>\n\n<style rel=\"stylesheet/scss\" lang=\"scss\" scoped>\n  .icon-body {\n    width: 100%;\n    padding: 10px;\n    .icon-search {\n      position: relative;\n      margin-bottom: 5px;\n    }\n    .icon-list {\n      height: 200px;\n      overflow: auto;\n      .list-container {\n        display: flex;\n        flex-wrap: wrap;\n        .icon-item-wrapper {\n          width: calc(100% / 3);\n          height: 25px;\n          line-height: 25px;\n          cursor: pointer;\n          display: flex;\n          .icon-item {\n            display: flex;\n            max-width: 100%;\n            height: 100%;\n            padding: 0 5px;\n            &:hover {\n              background: #ececec;\n              border-radius: 5px;\n            }\n            .icon {\n              flex-shrink: 0;\n            }\n            span {\n              display: inline-block;\n              vertical-align: -0.15em;\n              fill: currentColor;\n              padding-left: 2px;\n              overflow: hidden;\n              text-overflow: ellipsis;\n              white-space: nowrap;\n            }\n          }\n          .icon-item.active {\n            background: #ececec;\n            border-radius: 5px;\n          }\n        }\n      }\n    }\n  }\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/IconSelect/requireIcons.js",
    "content": "\nconst req = require.context('../../assets/icons/svg', false, /\\.svg$/)\nconst requireAll = requireContext => requireContext.keys()\n\nconst re = /\\.\\/(.*)\\.svg/\n\nconst icons = requireAll(req).map(i => {\n  return i.match(re)[1]\n})\n\nexport default icons\n"
  },
  {
    "path": "ruoyi-ui/src/components/ImagePreview/index.vue",
    "content": "<template>\n  <el-image\n    :src=\"`${realSrc}`\"\n    fit=\"cover\"\n    :style=\"`width:${realWidth};height:${realHeight};`\"\n    :preview-src-list=\"realSrcList\"\n  >\n    <div slot=\"error\" class=\"image-slot\">\n      <i class=\"el-icon-picture-outline\"></i>\n    </div>\n  </el-image>\n</template>\n\n<script>\n\nexport default {\n  name: \"ImagePreview\",\n  props: {\n    src: {\n      type: String,\n      default: \"\"\n    },\n    width: {\n      type: [Number, String],\n      default: \"\"\n    },\n    height: {\n      type: [Number, String],\n      default: \"\"\n    }\n  },\n  computed: {\n    realSrc() {\n      if (!this.src) {\n        return;\n      }\n      let real_src = this.src.split(\",\")[0];\n      return real_src;\n    },\n    realSrcList() {\n      if (!this.src) {\n        return;\n      }\n      let real_src_list = this.src.split(\",\");\n      let srcList = [];\n      real_src_list.forEach(item => {\n        return srcList.push(item);\n      });\n      return srcList;\n    },\n    realWidth() {\n      return typeof this.width == \"string\" ? this.width : `${this.width}px`;\n    },\n    realHeight() {\n      return typeof this.height == \"string\" ? this.height : `${this.height}px`;\n    }\n  },\n};\n</script>\n\n<style lang=\"scss\" scoped>\n.el-image {\n  border-radius: 5px;\n  background-color: #ebeef5;\n  box-shadow: 0 0 5px 1px #ccc;\n  ::v-deep .el-image__inner {\n    transition: all 0.3s;\n    cursor: pointer;\n    &:hover {\n      transform: scale(1.2);\n    }\n  }\n  ::v-deep .image-slot {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    width: 100%;\n    height: 100%;\n    color: #909399;\n    font-size: 30px;\n  }\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/ImageUpload/index.vue",
    "content": "<template>\n  <div class=\"component-upload-image\">\n    <el-upload\n      multiple\n      :action=\"uploadImgUrl\"\n      list-type=\"picture-card\"\n      :on-success=\"handleUploadSuccess\"\n      :before-upload=\"handleBeforeUpload\"\n      :limit=\"limit\"\n      :on-error=\"handleUploadError\"\n      :on-exceed=\"handleExceed\"\n      ref=\"imageUpload\"\n      :on-remove=\"handleDelete\"\n      :show-file-list=\"true\"\n      :headers=\"headers\"\n      :file-list=\"fileList\"\n      :on-preview=\"handlePictureCardPreview\"\n      :class=\"{hide: this.fileList.length >= this.limit}\"\n    >\n      <i class=\"el-icon-plus\"></i>\n    </el-upload>\n\n    <!-- 上传提示 -->\n    <div class=\"el-upload__tip\" slot=\"tip\" v-if=\"showTip\">\n      请上传\n      <template v-if=\"fileSize\"> 大小不超过 <b style=\"color: #f56c6c\">{{ fileSize }}MB</b> </template>\n      <template v-if=\"fileType\"> 格式为 <b style=\"color: #f56c6c\">{{ fileType.join(\"/\") }}</b> </template>\n      的文件\n    </div>\n\n    <el-dialog\n      :visible.sync=\"dialogVisible\"\n      title=\"预览\"\n      width=\"800\"\n      append-to-body\n    >\n      <img\n        :src=\"dialogImageUrl\"\n        style=\"display: block; max-width: 100%; margin: 0 auto\"\n      />\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { getToken } from \"@/utils/auth\";\nimport { listByIds, delOss } from \"@/api/system/oss\";\n\nexport default {\n  props: {\n    value: [String, Object, Array],\n    // 图片数量限制\n    limit: {\n      type: Number,\n      default: 5,\n    },\n    // 大小限制(MB)\n    fileSize: {\n       type: Number,\n      default: 5,\n    },\n    // 文件类型, 例如['png', 'jpg', 'jpeg']\n    fileType: {\n      type: Array,\n      default: () => [\"png\", \"jpg\", \"jpeg\"],\n    },\n    // 是否显示提示\n    isShowTip: {\n      type: Boolean,\n      default: true\n    }\n  },\n  data() {\n    return {\n      number: 0,\n      uploadList: [],\n      dialogImageUrl: \"\",\n      dialogVisible: false,\n      hideUpload: false,\n      baseUrl: process.env.VUE_APP_BASE_API,\n      uploadImgUrl: process.env.VUE_APP_BASE_API + \"/system/oss/upload\", // 上传的图片服务器地址\n      headers: {\n        Authorization: \"Bearer \" + getToken(),\n      },\n      fileList: []\n    };\n  },\n  watch: {\n    value: {\n      async handler(val) {\n        if (val) {\n          // 首先将值转为数组\n          let list;\n          if (Array.isArray(val)) {\n            list = val;\n          } else {\n            if(val.includes('http')){\n              this.fileList = [{ name: val, url: val, ossId: val }];\n              return\n            }\n            await listByIds(val).then(res => {\n              list = res.data;\n            })\n          }\n          // 然后将数组转为对象数组\n          this.fileList = list.map(item => {\n            // 此处name使用ossId 防止删除出现重名\n            item = { name: item.ossId, url: item.url, ossId: item.ossId };\n            return item;\n          });\n        } else {\n          this.fileList = [];\n          return [];\n        }\n      },\n      deep: true,\n      immediate: true\n    }\n  },\n  computed: {\n    // 是否显示提示\n    showTip() {\n      return this.isShowTip && (this.fileType || this.fileSize);\n    },\n  },\n  methods: {\n    // 上传前loading加载\n    handleBeforeUpload(file) {\n      let isImg = false;\n      if (this.fileType.length) {\n        let fileExtension = \"\";\n        if (file.name.lastIndexOf(\".\") > -1) {\n          fileExtension = file.name.slice(file.name.lastIndexOf(\".\") + 1);\n        }\n        isImg = this.fileType.some((type) => {\n          if (file.type.indexOf(type) > -1) return true;\n          if (fileExtension && fileExtension.indexOf(type) > -1) return true;\n          return false;\n        });\n      } else {\n        isImg = file.type.indexOf(\"image\") > -1;\n      }\n\n      if (!isImg) {\n        this.$modal.msgError(`文件格式不正确, 请上传${this.fileType.join(\"/\")}图片格式文件!`);\n        return false;\n      }\n      if (this.fileSize) {\n        const isLt = file.size / 1024 / 1024 < this.fileSize;\n        if (!isLt) {\n          this.$modal.msgError(`上传头像图片大小不能超过 ${this.fileSize} MB!`);\n          return false;\n        }\n      }\n      this.$modal.loading(\"正在上传图片，请稍候...\");\n      this.number++;\n    },\n    // 文件个数超出\n    handleExceed() {\n      this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);\n    },\n    // 上传成功回调\n    handleUploadSuccess(res, file) {\n      if (res.code === 200) {\n        this.uploadList.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });\n        this.uploadedSuccessfully();\n      } else {\n        this.number--;\n        this.$modal.closeLoading();\n        this.$modal.msgError(res.msg);\n        this.$refs.imageUpload.handleRemove(file);\n        this.uploadedSuccessfully();\n      }\n    },\n    // 删除图片\n    handleDelete(file) {\n      const findex = this.fileList.map(f => f.name).indexOf(file.name);\n      if(findex > -1) {\n        let ossId = this.fileList[findex].ossId;\n        delOss(ossId);\n        this.fileList.splice(findex, 1);\n        this.$emit(\"input\", this.listToString(this.fileList));\n      }\n    },\n    // 上传失败\n    handleUploadError(res) {\n      this.$modal.msgError(\"上传图片失败，请重试\");\n      this.$modal.closeLoading();\n    },\n    // 上传结束处理\n    uploadedSuccessfully() {\n      if (this.number > 0 && this.uploadList.length === this.number) {\n        this.fileList = this.fileList.concat(this.uploadList);\n        this.uploadList = [];\n        this.number = 0;\n        this.$emit(\"input\", this.listToString(this.fileList));\n        this.$modal.closeLoading();\n      }\n    },\n    // 预览\n    handlePictureCardPreview(file) {\n      this.dialogImageUrl = file.url;\n      this.dialogVisible = true;\n    },\n    // 对象转成指定字符串分隔\n    listToString(list, separator) {\n      let strs = \"\";\n      separator = separator || \",\";\n      for (let i in list) {\n        if (list[i].ossId) {\n          strs += list[i].ossId + separator;\n        }\n      }\n      return strs != \"\" ? strs.substr(0, strs.length - 1) : \"\";\n    }\n  }\n};\n</script>\n<style scoped lang=\"scss\">\n// .el-upload--picture-card 控制加号部分\n::v-deep.hide .el-upload--picture-card {\n    display: none;\n}\n// 去掉动画效果\n::v-deep .el-list-enter-active,\n::v-deep .el-list-leave-active {\n    transition: all 0s;\n}\n\n::v-deep .el-list-enter, .el-list-leave-active {\n  opacity: 0;\n  transform: translateY(0);\n}\n</style>\n\n"
  },
  {
    "path": "ruoyi-ui/src/components/Pagination/index.vue",
    "content": "<template>\n  <div :class=\"{'hidden':hidden}\" class=\"pagination-container\">\n    <el-pagination\n      :background=\"background\"\n      :current-page.sync=\"currentPage\"\n      :page-size.sync=\"pageSize\"\n      :layout=\"layout\"\n      :page-sizes=\"pageSizes\"\n      :pager-count=\"pagerCount\"\n      :total=\"total\"\n      v-bind=\"$attrs\"\n      @size-change=\"handleSizeChange\"\n      @current-change=\"handleCurrentChange\"\n    />\n  </div>\n</template>\n\n<script>\nimport { scrollTo } from '@/utils/scroll-to'\n\nexport default {\n  name: 'Pagination',\n  props: {\n    total: {\n      required: true,\n      type: Number\n    },\n    page: {\n      type: Number,\n      default: 1\n    },\n    limit: {\n      type: Number,\n      default: 20\n    },\n    pageSizes: {\n      type: Array,\n      default() {\n        return [10, 20, 30, 50]\n      }\n    },\n    // 移动端页码按钮的数量端默认值5\n    pagerCount: {\n      type: Number,\n      default: document.body.clientWidth < 992 ? 5 : 7\n    },\n    layout: {\n      type: String,\n      default: 'total, sizes, prev, pager, next, jumper'\n    },\n    background: {\n      type: Boolean,\n      default: true\n    },\n    autoScroll: {\n      type: Boolean,\n      default: true\n    },\n    hidden: {\n      type: Boolean,\n      default: false\n    }\n  },\n  data() {\n    return {\n    };\n  },\n  computed: {\n    currentPage: {\n      get() {\n        return this.page\n      },\n      set(val) {\n        this.$emit('update:page', val)\n      }\n    },\n    pageSize: {\n      get() {\n        return this.limit\n      },\n      set(val) {\n        this.$emit('update:limit', val)\n      }\n    }\n  },\n  methods: {\n    handleSizeChange(val) {\n      if (this.currentPage * val > this.total) {\n        this.currentPage = 1\n      }\n      this.$emit('pagination', { page: this.currentPage, limit: val })\n      if (this.autoScroll) {\n        scrollTo(0, 800)\n      }\n    },\n    handleCurrentChange(val) {\n      this.$emit('pagination', { page: val, limit: this.pageSize })\n      if (this.autoScroll) {\n        scrollTo(0, 800)\n      }\n    }\n  }\n}\n</script>\n\n<style scoped>\n.pagination-container {\n  background: #fff;\n  padding: 32px 16px;\n}\n.pagination-container.hidden {\n  display: none;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/PanThumb/index.vue",
    "content": "<template>\n  <div :style=\"{zIndex:zIndex,height:height,width:width}\" class=\"pan-item\">\n    <div class=\"pan-info\">\n      <div class=\"pan-info-roles-container\">\n        <slot />\n      </div>\n    </div>\n    <!-- eslint-disable-next-line -->\n    <div :style=\"{backgroundImage: `url(${image})`}\" class=\"pan-thumb\"></div>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'PanThumb',\n  props: {\n    image: {\n      type: String,\n      required: true\n    },\n    zIndex: {\n      type: Number,\n      default: 1\n    },\n    width: {\n      type: String,\n      default: '150px'\n    },\n    height: {\n      type: String,\n      default: '150px'\n    }\n  }\n}\n</script>\n\n<style scoped>\n.pan-item {\n  width: 200px;\n  height: 200px;\n  border-radius: 50%;\n  display: inline-block;\n  position: relative;\n  cursor: default;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n}\n\n.pan-info-roles-container {\n  padding: 20px;\n  text-align: center;\n}\n\n.pan-thumb {\n  width: 100%;\n  height: 100%;\n  background-position: center center;\n  background-size: cover;\n  border-radius: 50%;\n  overflow: hidden;\n  position: absolute;\n  transform-origin: 95% 40%;\n  transition: all 0.3s ease-in-out;\n}\n\n/* .pan-thumb:after {\n  content: '';\n  width: 8px;\n  height: 8px;\n  position: absolute;\n  border-radius: 50%;\n  top: 40%;\n  left: 95%;\n  margin: -4px 0 0 -4px;\n  background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%);\n  box-shadow: 0 0 1px rgba(255, 255, 255, 0.9);\n} */\n\n.pan-info {\n  position: absolute;\n  width: inherit;\n  height: inherit;\n  border-radius: 50%;\n  overflow: hidden;\n  box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05);\n}\n\n.pan-info h3 {\n  color: #fff;\n  text-transform: uppercase;\n  position: relative;\n  letter-spacing: 2px;\n  font-size: 18px;\n  margin: 0 60px;\n  padding: 22px 0 0 0;\n  height: 85px;\n  font-family: 'Open Sans', Arial, sans-serif;\n  text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3);\n}\n\n.pan-info p {\n  color: #fff;\n  padding: 10px 5px;\n  font-style: italic;\n  margin: 0 30px;\n  font-size: 12px;\n  border-top: 1px solid rgba(255, 255, 255, 0.5);\n}\n\n.pan-info p a {\n  display: block;\n  color: #333;\n  width: 80px;\n  height: 80px;\n  background: rgba(255, 255, 255, 0.3);\n  border-radius: 50%;\n  color: #fff;\n  font-style: normal;\n  font-weight: 700;\n  text-transform: uppercase;\n  font-size: 9px;\n  letter-spacing: 1px;\n  padding-top: 24px;\n  margin: 7px auto 0;\n  font-family: 'Open Sans', Arial, sans-serif;\n  opacity: 0;\n  transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s;\n  transform: translateX(60px) rotate(90deg);\n}\n\n.pan-info p a:hover {\n  background: rgba(255, 255, 255, 0.5);\n}\n\n.pan-item:hover .pan-thumb {\n  transform: rotate(-110deg);\n}\n\n.pan-item:hover .pan-info p a {\n  opacity: 1;\n  transform: translateX(0px) rotate(0deg);\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/ParentView/index.vue",
    "content": "<template >\n  <router-view />\n</template>\n"
  },
  {
    "path": "ruoyi-ui/src/components/RightPanel/index.vue",
    "content": "<template>\n  <div ref=\"rightPanel\" class=\"rightPanel-container\">\n    <div class=\"rightPanel-background\" />\n    <div class=\"rightPanel\">\n      <div class=\"rightPanel-items\">\n        <slot />\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'RightPanel',\n  props: {\n    clickNotClose: {\n      default: false,\n      type: Boolean\n    }\n  },\n  computed: {\n    show: {\n      get() {\n        return this.$store.state.settings.showSettings\n      },\n      set(val) {\n        this.$store.dispatch('settings/changeSetting', {\n          key: 'showSettings',\n          value: val\n        })\n      }\n    }\n  },\n  watch: {\n    show(value) {\n      if (value && !this.clickNotClose) {\n        this.addEventClick()\n      }\n    }\n  },\n  mounted() {\n    this.addEventClick()\n  },\n  beforeDestroy() {\n    const elx = this.$refs.rightPanel\n    elx.remove()\n  },\n  methods: {\n    addEventClick() {\n      window.addEventListener('click', this.closeSidebar)\n    },\n    closeSidebar(evt) {\n      const parent = evt.target.closest('.el-drawer__body')\n      if (!parent) {\n        this.show = false\n        window.removeEventListener('click', this.closeSidebar)\n      }\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.rightPanel-background {\n  position: fixed;\n  top: 0;\n  left: 0;\n  opacity: 0;\n  transition: opacity .3s cubic-bezier(.7, .3, .1, 1);\n  background: rgba(0, 0, 0, .2);\n  z-index: -1;\n}\n\n.rightPanel {\n  width: 100%;\n  max-width: 260px;\n  height: 100vh;\n  position: fixed;\n  top: 0;\n  right: 0;\n  box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05);\n  transition: all .25s cubic-bezier(.7, .3, .1, 1);\n  transform: translate(100%);\n  background: #fff;\n  z-index: 40000;\n}\n\n.handle-button {\n  width: 48px;\n  height: 48px;\n  position: absolute;\n  left: -48px;\n  text-align: center;\n  font-size: 24px;\n  border-radius: 6px 0 0 6px !important;\n  z-index: 0;\n  pointer-events: auto;\n  cursor: pointer;\n  color: #fff;\n  line-height: 48px;\n  i {\n    font-size: 24px;\n    line-height: 48px;\n  }\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/RightToolbar/index.vue",
    "content": "<template>\n  <div class=\"top-right-btn\" :style=\"style\">\n    <el-row>\n      <el-tooltip class=\"item\" effect=\"dark\" :content=\"showSearch ? '隐藏搜索' : '显示搜索'\" placement=\"top\" v-if=\"search\">\n        <el-button size=\"mini\" circle icon=\"el-icon-search\" @click=\"toggleSearch()\" />\n      </el-tooltip>\n      <el-tooltip class=\"item\" effect=\"dark\" content=\"刷新\" placement=\"top\">\n        <el-button size=\"mini\" circle icon=\"el-icon-refresh\" @click=\"refresh()\" />\n      </el-tooltip>\n      <el-tooltip class=\"item\" effect=\"dark\" content=\"显隐列\" placement=\"top\" v-if=\"columns\">\n        <el-button size=\"mini\" circle icon=\"el-icon-menu\" @click=\"showColumn()\" />\n      </el-tooltip>\n    </el-row>\n    <el-dialog :title=\"title\" :visible.sync=\"open\" append-to-body>\n      <el-transfer\n        :titles=\"['显示', '隐藏']\"\n        v-model=\"value\"\n        :data=\"columns\"\n        @change=\"dataChange\"\n      ></el-transfer>\n    </el-dialog>\n  </div>\n</template>\n<script>\nexport default {\n  name: \"RightToolbar\",\n  data() {\n    return {\n      // 显隐数据\n      value: [],\n      // 弹出层标题\n      title: \"显示/隐藏\",\n      // 是否显示弹出层\n      open: false,\n    };\n  },\n  props: {\n    showSearch: {\n      type: Boolean,\n      default: true,\n    },\n    columns: {\n      type: Array,\n    },\n    search: {\n      type: Boolean,\n      default: true,\n    },\n    gutter: {\n      type: Number,\n      default: 10,\n    },\n  },\n  computed: {\n    style() {\n      const ret = {};\n      if (this.gutter) {\n        ret.marginRight = `${this.gutter / 2}px`;\n      }\n      return ret;\n    }\n  },\n  created() {\n    // 显隐列初始默认隐藏列\n    for (let item in this.columns) {\n      if (this.columns[item].visible === false) {\n        this.value.push(parseInt(item));\n      }\n    }\n  },\n  methods: {\n    // 搜索\n    toggleSearch() {\n      this.$emit(\"update:showSearch\", !this.showSearch);\n    },\n    // 刷新\n    refresh() {\n      this.$emit(\"queryTable\");\n    },\n    // 右侧列表元素变化\n    dataChange(data) {\n      for (let item in this.columns) {\n        const key = this.columns[item].key;\n        this.columns[item].visible = !data.includes(key);\n      }\n    },\n    // 打开显隐列dialog\n    showColumn() {\n      this.open = true;\n    },\n  },\n};\n</script>\n<style lang=\"scss\" scoped>\n::v-deep .el-transfer__button {\n  border-radius: 50%;\n  padding: 12px;\n  display: block;\n  margin-left: 0px;\n}\n::v-deep .el-transfer__button:first-child {\n  margin-bottom: 10px;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/RuoYi/Doc/index.vue",
    "content": "<!--<template>-->\n<!--  <div>-->\n<!--    <svg-icon icon-class=\"question\" @click=\"goto\" />-->\n<!--  </div>-->\n<!--</template>-->\n\n<!--<script>-->\n<!--export default {-->\n<!--  name: 'RuoYiDoc',-->\n<!--  data() {-->\n<!--    return {-->\n<!--      url: 'https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages'-->\n<!--    }-->\n<!--  },-->\n<!--  methods: {-->\n<!--    goto() {-->\n<!--      window.open(this.url)-->\n<!--    }-->\n<!--  }-->\n<!--}-->\n<!--</script>-->\n"
  },
  {
    "path": "ruoyi-ui/src/components/RuoYi/Git/index.vue",
    "content": "<!--<template>-->\n<!--  <div>-->\n<!--    <svg-icon icon-class=\"github\" @click=\"goto\" />-->\n<!--  </div>-->\n<!--</template>-->\n\n<!--<script>-->\n<!--export default {-->\n<!--  name: 'RuoYiGit',-->\n<!--  data() {-->\n<!--    return {-->\n<!--      url: 'https://gitee.com/dromara/RuoYi-Vue-Plus'-->\n<!--    }-->\n<!--  },-->\n<!--  methods: {-->\n<!--    goto() {-->\n<!--      window.open(this.url)-->\n<!--    }-->\n<!--  }-->\n<!--}-->\n<!--</script>-->\n"
  },
  {
    "path": "ruoyi-ui/src/components/Screenfull/index.vue",
    "content": "<template>\n  <div>\n    <svg-icon :icon-class=\"isFullscreen?'exit-fullscreen':'fullscreen'\" @click=\"click\" />\n  </div>\n</template>\n\n<script>\nimport screenfull from 'screenfull'\n\nexport default {\n  name: 'Screenfull',\n  data() {\n    return {\n      isFullscreen: false\n    }\n  },\n  mounted() {\n    this.init()\n  },\n  beforeDestroy() {\n    this.destroy()\n  },\n  methods: {\n    click() {\n      if (!screenfull.isEnabled) {\n        this.$message({ message: '你的浏览器不支持全屏', type: 'warning' })\n        return false\n      }\n      screenfull.toggle()\n    },\n    change() {\n      this.isFullscreen = screenfull.isFullscreen\n    },\n    init() {\n      if (screenfull.isEnabled) {\n        screenfull.on('change', this.change)\n      }\n    },\n    destroy() {\n      if (screenfull.isEnabled) {\n        screenfull.off('change', this.change)\n      }\n    }\n  }\n}\n</script>\n\n<style scoped>\n.screenfull-svg {\n  display: inline-block;\n  cursor: pointer;\n  fill: #5a5e66;;\n  width: 20px;\n  height: 20px;\n  vertical-align: 10px;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/SizeSelect/index.vue",
    "content": "<template>\n  <el-dropdown trigger=\"click\" @command=\"handleSetSize\">\n    <div>\n      <svg-icon class-name=\"size-icon\" icon-class=\"size\" />\n    </div>\n    <el-dropdown-menu slot=\"dropdown\">\n      <el-dropdown-item v-for=\"item of sizeOptions\" :key=\"item.value\" :disabled=\"size===item.value\" :command=\"item.value\">\n        {{ item.label }}\n      </el-dropdown-item>\n    </el-dropdown-menu>\n  </el-dropdown>\n</template>\n\n<script>\nexport default {\n  data() {\n    return {\n      sizeOptions: [\n        { label: 'Default', value: 'default' },\n        { label: 'Medium', value: 'medium' },\n        { label: 'Small', value: 'small' },\n        { label: 'Mini', value: 'mini' }\n      ]\n    }\n  },\n  computed: {\n    size() {\n      return this.$store.getters.size\n    }\n  },\n  methods: {\n    handleSetSize(size) {\n      this.$ELEMENT.size = size\n      this.$store.dispatch('app/setSize', size)\n      this.refreshView()\n      this.$message({\n        message: 'Switch Size Success',\n        type: 'success'\n      })\n    },\n    refreshView() {\n      // In order to make the cached page re-rendered\n      this.$store.dispatch('tagsView/delAllCachedViews', this.$route)\n\n      const { fullPath } = this.$route\n\n      this.$nextTick(() => {\n        this.$router.replace({\n          path: '/redirect' + fullPath\n        })\n      })\n    }\n  }\n\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/components/SvgIcon/index.vue",
    "content": "<template>\n  <div v-if=\"isExternal\" :style=\"styleExternalIcon\" class=\"svg-external-icon svg-icon\" v-on=\"$listeners\" />\n  <svg v-else :class=\"svgClass\" aria-hidden=\"true\" v-on=\"$listeners\">\n    <use :xlink:href=\"iconName\" />\n  </svg>\n</template>\n\n<script>\nimport { isExternal } from '@/utils/validate'\n\nexport default {\n  name: 'SvgIcon',\n  props: {\n    iconClass: {\n      type: String,\n      required: true\n    },\n    className: {\n      type: String,\n      default: ''\n    }\n  },\n  computed: {\n    isExternal() {\n      return isExternal(this.iconClass)\n    },\n    iconName() {\n      return `#icon-${this.iconClass}`\n    },\n    svgClass() {\n      if (this.className) {\n        return 'svg-icon ' + this.className\n      } else {\n        return 'svg-icon'\n      }\n    },\n    styleExternalIcon() {\n      return {\n        mask: `url(${this.iconClass}) no-repeat 50% 50%`,\n        '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`\n      }\n    }\n  }\n}\n</script>\n\n<style scoped>\n.svg-icon {\n  width: 1em;\n  height: 1em;\n  vertical-align: -0.15em;\n  fill: currentColor;\n  overflow: hidden;\n}\n\n.svg-external-icon {\n  background-color: currentColor;\n  mask-size: cover!important;\n  display: inline-block;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/ThemePicker/index.vue",
    "content": "<template>\n  <el-color-picker\n    v-model=\"theme\"\n    :predefine=\"['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]\"\n    class=\"theme-picker\"\n    popper-class=\"theme-picker-dropdown\"\n  />\n</template>\n\n<script>\nconst version = require('element-ui/package.json').version // element-ui version from node_modules\nconst ORIGINAL_THEME = '#409EFF' // default color\n\nexport default {\n  data() {\n    return {\n      chalk: '', // content of theme-chalk css\n      theme: ''\n    }\n  },\n  computed: {\n    defaultTheme() {\n      return this.$store.state.settings.theme\n    }\n  },\n  watch: {\n    defaultTheme: {\n      handler: function(val, oldVal) {\n        this.theme = val\n      },\n      immediate: true\n    },\n    async theme(val) {\n      await this.setTheme(val)\n    }\n  },\n  created() {\n    if(this.defaultTheme !== ORIGINAL_THEME) {\n      this.setTheme(this.defaultTheme)\n    }\n  },\n\n  methods: {\n    async setTheme(val) {\n      const oldVal = this.chalk ? this.theme : ORIGINAL_THEME\n      if (typeof val !== 'string') return\n      const themeCluster = this.getThemeCluster(val.replace('#', ''))\n      const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))\n\n      const getHandler = (variable, id) => {\n        return () => {\n          const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))\n          const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)\n\n          let styleTag = document.getElementById(id)\n          if (!styleTag) {\n            styleTag = document.createElement('style')\n            styleTag.setAttribute('id', id)\n            document.head.appendChild(styleTag)\n          }\n          styleTag.innerText = newStyle\n        }\n      }\n\n      if (!this.chalk) {\n        const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`\n        await this.getCSSString(url, 'chalk')\n      }\n\n      const chalkHandler = getHandler('chalk', 'chalk-style')\n\n      chalkHandler()\n\n      const styles = [].slice.call(document.querySelectorAll('style'))\n        .filter(style => {\n          const text = style.innerText\n          return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)\n        })\n      styles.forEach(style => {\n        const { innerText } = style\n        if (typeof innerText !== 'string') return\n        style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)\n      })\n\n      this.$emit('change', val)\n    },\n\n    updateStyle(style, oldCluster, newCluster) {\n      let newStyle = style\n      oldCluster.forEach((color, index) => {\n        newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])\n      })\n      return newStyle\n    },\n\n    getCSSString(url, variable) {\n      return new Promise(resolve => {\n        const xhr = new XMLHttpRequest()\n        xhr.onreadystatechange = () => {\n          if (xhr.readyState === 4 && xhr.status === 200) {\n            this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')\n            resolve()\n          }\n        }\n        xhr.open('GET', url)\n        xhr.send()\n      })\n    },\n\n    getThemeCluster(theme) {\n      const tintColor = (color, tint) => {\n        let red = parseInt(color.slice(0, 2), 16)\n        let green = parseInt(color.slice(2, 4), 16)\n        let blue = parseInt(color.slice(4, 6), 16)\n\n        if (tint === 0) { // when primary color is in its rgb space\n          return [red, green, blue].join(',')\n        } else {\n          red += Math.round(tint * (255 - red))\n          green += Math.round(tint * (255 - green))\n          blue += Math.round(tint * (255 - blue))\n\n          red = red.toString(16)\n          green = green.toString(16)\n          blue = blue.toString(16)\n\n          return `#${red}${green}${blue}`\n        }\n      }\n\n      const shadeColor = (color, shade) => {\n        let red = parseInt(color.slice(0, 2), 16)\n        let green = parseInt(color.slice(2, 4), 16)\n        let blue = parseInt(color.slice(4, 6), 16)\n\n        red = Math.round((1 - shade) * red)\n        green = Math.round((1 - shade) * green)\n        blue = Math.round((1 - shade) * blue)\n\n        red = red.toString(16)\n        green = green.toString(16)\n        blue = blue.toString(16)\n\n        return `#${red}${green}${blue}`\n      }\n\n      const clusters = [theme]\n      for (let i = 0; i <= 9; i++) {\n        clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))\n      }\n      clusters.push(shadeColor(theme, 0.1))\n      return clusters\n    }\n  }\n}\n</script>\n\n<style>\n.theme-message,\n.theme-picker-dropdown {\n  z-index: 99999 !important;\n}\n\n.theme-picker .el-color-picker__trigger {\n  height: 26px !important;\n  width: 26px !important;\n  padding: 2px;\n}\n\n.theme-picker-dropdown .el-color-dropdown__link-btn {\n  display: none;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/TopNav/index.vue",
    "content": "<template>\n  <el-menu\n    :default-active=\"activeMenu\"\n    mode=\"horizontal\"\n    @select=\"handleSelect\"\n  >\n    <template v-for=\"(item, index) in topMenus\">\n      <el-menu-item :style=\"{'--theme': theme}\" :index=\"item.path\" :key=\"index\" v-if=\"index < visibleNumber\"\n        ><svg-icon :icon-class=\"item.meta.icon\" />\n        {{ item.meta.title }}</el-menu-item\n      >\n    </template>\n\n    <!-- 顶部菜单超出数量折叠 -->\n    <el-submenu :style=\"{'--theme': theme}\" index=\"more\" v-if=\"topMenus.length > visibleNumber\">\n      <template slot=\"title\">更多菜单</template>\n      <template v-for=\"(item, index) in topMenus\">\n        <el-menu-item\n          :index=\"item.path\"\n          :key=\"index\"\n          v-if=\"index >= visibleNumber\"\n          ><svg-icon :icon-class=\"item.meta.icon\" />\n          {{ item.meta.title }}</el-menu-item\n        >\n      </template>\n    </el-submenu>\n  </el-menu>\n</template>\n\n<script>\nimport { constantRoutes } from \"@/router\";\n\n// 隐藏侧边栏路由\nconst hideList = ['/index', '/user/profile'];\n\nexport default {\n  data() {\n    return {\n      // 顶部栏初始数\n      visibleNumber: 5,\n      // 当前激活菜单的 index\n      currentIndex: undefined\n    };\n  },\n  computed: {\n    theme() {\n      return this.$store.state.settings.theme;\n    },\n    // 顶部显示菜单\n    topMenus() {\n      let topMenus = [];\n      this.routers.map((menu) => {\n        if (menu.hidden !== true) {\n          // 兼容顶部栏一级菜单内部跳转\n          if (menu.path === \"/\") {\n              topMenus.push(menu.children[0]);\n          } else {\n              topMenus.push(menu);\n          }\n        }\n      });\n      return topMenus;\n    },\n    // 所有的路由信息\n    routers() {\n      return this.$store.state.permission.topbarRouters;\n    },\n    // 设置子路由\n    childrenMenus() {\n      var childrenMenus = [];\n      this.routers.map((router) => {\n        for (var item in router.children) {\n          if (router.children[item].parentPath === undefined) {\n            if(router.path === \"/\") {\n              router.children[item].path = \"/\" + router.children[item].path;\n            } else {\n              if(!this.ishttp(router.children[item].path)) {\n                router.children[item].path = router.path + \"/\" + router.children[item].path;\n              }\n            }\n            router.children[item].parentPath = router.path;\n          }\n          childrenMenus.push(router.children[item]);\n        }\n      });\n      return constantRoutes.concat(childrenMenus);\n    },\n    // 默认激活的菜单\n    activeMenu() {\n      const path = this.$route.path;\n      let activePath = path;\n      if (path !== undefined && path.lastIndexOf(\"/\") > 0 && hideList.indexOf(path) === -1) {\n        const tmpPath = path.substring(1, path.length);\n        activePath = \"/\" + tmpPath.substring(0, tmpPath.indexOf(\"/\"));\n        if (!this.$route.meta.link) {\n          this.$store.dispatch('app/toggleSideBarHide', false);\n        }\n      } else if(!this.$route.children) {\n        activePath = path;\n        this.$store.dispatch('app/toggleSideBarHide', true);\n      }\n      this.activeRoutes(activePath);\n      return activePath;\n    },\n  },\n  beforeMount() {\n    window.addEventListener('resize', this.setVisibleNumber)\n  },\n  beforeDestroy() {\n    window.removeEventListener('resize', this.setVisibleNumber)\n  },\n  mounted() {\n    this.setVisibleNumber();\n  },\n  methods: {\n    // 根据宽度计算设置显示栏数\n    setVisibleNumber() {\n      const width = document.body.getBoundingClientRect().width / 3;\n      this.visibleNumber = parseInt(width / 85);\n    },\n    // 菜单选择事件\n    handleSelect(key, keyPath) {\n      this.currentIndex = key;\n      const route = this.routers.find(item => item.path === key);\n      if (this.ishttp(key)) {\n        // http(s):// 路径新窗口打开\n        window.open(key, \"_blank\");\n      } else if (!route || !route.children) {\n        // 没有子路由路径内部打开\n        const routeMenu = this.childrenMenus.find(item => item.path === key);\n        if (routeMenu && routeMenu.query) {\n          let query = JSON.parse(routeMenu.query);\n          this.$router.push({ path: key, query: query });\n        } else {\n          this.$router.push({ path: key });\n        }\n        this.$store.dispatch('app/toggleSideBarHide', true);\n      } else {\n        // 显示左侧联动菜单\n        this.activeRoutes(key);\n        this.$store.dispatch('app/toggleSideBarHide', false);\n      }\n    },\n    // 当前激活的路由\n    activeRoutes(key) {\n      var routes = [];\n      if (this.childrenMenus && this.childrenMenus.length > 0) {\n        this.childrenMenus.map((item) => {\n          if (key == item.parentPath || (key == \"index\" && \"\" == item.path)) {\n            routes.push(item);\n          }\n        });\n      }\n      if(routes.length > 0) {\n        this.$store.commit(\"SET_SIDEBAR_ROUTERS\", routes);\n      } else {\n        this.$store.dispatch('app/toggleSideBarHide', true);\n      }\n    },\n    ishttp(url) {\n      return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1\n    }\n  },\n};\n</script>\n\n<style lang=\"scss\">\n.topmenu-container.el-menu--horizontal > .el-menu-item {\n  float: left;\n  height: 50px !important;\n  line-height: 50px !important;\n  color: #999093 !important;\n  padding: 0 5px !important;\n  margin: 0 10px !important;\n}\n\n.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-submenu.is-active .el-submenu__title {\n  border-bottom: 2px solid #{'var(--theme)'} !important;\n  color: #303133;\n}\n\n/* submenu item */\n.topmenu-container.el-menu--horizontal > .el-submenu .el-submenu__title {\n  float: left;\n  height: 50px !important;\n  line-height: 50px !important;\n  color: #999093 !important;\n  padding: 0 5px !important;\n  margin: 0 10px !important;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/components/iFrame/index.vue",
    "content": "<template>\n  <div v-loading=\"loading\" :style=\"'height:' + height\">\n    <iframe\n      :src=\"src\"\n      frameborder=\"no\"\n      style=\"width: 100%; height: 100%\"\n      scrolling=\"auto\"\n    />\n  </div>\n</template>\n<script>\nexport default {\n  props: {\n    src: {\n      type: String,\n      required: true\n    },\n  },\n  data() {\n    return {\n      height: document.documentElement.clientHeight - 94.5 + \"px;\",\n      loading: true,\n      url: this.src\n    };\n  },\n  mounted: function () {\n    setTimeout(() => {\n      this.loading = false;\n    }, 300);\n    const that = this;\n    window.onresize = function temp() {\n      that.height = document.documentElement.clientHeight - 94.5 + \"px;\";\n    };\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/directive/dialog/drag.js",
    "content": "/**\n* v-dialogDrag 弹窗拖拽\n* Copyright (c) 2019 ruoyi\n*/\n\nexport default {\n  bind(el, binding, vnode, oldVnode) {\n    const value = binding.value\n    if (value == false) return\n    // 获取拖拽内容头部\n    const dialogHeaderEl = el.querySelector('.el-dialog__header');\n    const dragDom = el.querySelector('.el-dialog');\n    dialogHeaderEl.style.cursor = 'move';\n    // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);\n    const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null);\n    dragDom.style.position = 'absolute';\n    dragDom.style.marginTop = 0;\n    let width = dragDom.style.width;\n    if (width.includes('%')) {\n      width = +document.body.clientWidth * (+width.replace(/\\%/g, '') / 100);\n    } else {\n      width = +width.replace(/\\px/g, '');\n    }\n    dragDom.style.left = `${(document.body.clientWidth - width) / 2}px`;\n    // 鼠标按下事件\n    dialogHeaderEl.onmousedown = (e) => {\n      // 鼠标按下，计算当前元素距离可视区的距离 (鼠标点击位置距离可视窗口的距离)\n      const disX = e.clientX - dialogHeaderEl.offsetLeft;\n      const disY = e.clientY - dialogHeaderEl.offsetTop;\n\n      // 获取到的值带px 正则匹配替换\n      let styL, styT;\n\n      // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px\n      if (sty.left.includes('%')) {\n        styL = +document.body.clientWidth * (+sty.left.replace(/\\%/g, '') / 100);\n        styT = +document.body.clientHeight * (+sty.top.replace(/\\%/g, '') / 100);\n      } else {\n        styL = +sty.left.replace(/\\px/g, '');\n        styT = +sty.top.replace(/\\px/g, '');\n      };\n\n      // 鼠标拖拽事件\n      document.onmousemove = function (e) {\n        // 通过事件委托，计算移动的距离 （开始拖拽至结束拖拽的距离）\n        const l = e.clientX - disX;\n        const t = e.clientY - disY;\n\n        let finallyL = l + styL\n        let finallyT = t + styT\n\n        // 移动当前元素\n        dragDom.style.left = `${finallyL}px`;\n        dragDom.style.top = `${finallyT}px`;\n\n      };\n\n      document.onmouseup = function (e) {\n        document.onmousemove = null;\n        document.onmouseup = null;\n      };\n    }\n  }\n};"
  },
  {
    "path": "ruoyi-ui/src/directive/dialog/dragHeight.js",
    "content": "/**\n* v-dialogDragWidth 可拖动弹窗高度（右下角）\n* Copyright (c) 2019 ruoyi\n*/\n\nexport default {\n    bind(el) {\n        const dragDom = el.querySelector('.el-dialog');\n        const lineEl = document.createElement('div');\n        lineEl.style = 'width: 6px; background: inherit; height: 10px; position: absolute; right: 0; bottom: 0; margin: auto; z-index: 1; cursor: nwse-resize;';\n        lineEl.addEventListener('mousedown',\n            function(e) {\n                // 鼠标按下，计算当前元素距离可视区的距离\n                const disX = e.clientX - el.offsetLeft;\n                const disY = e.clientY - el.offsetTop;\n                // 当前宽度 高度\n                const curWidth = dragDom.offsetWidth;\n                const curHeight = dragDom.offsetHeight;\n                document.onmousemove = function(e) {\n                    e.preventDefault(); // 移动时禁用默认事件\n                    // 通过事件委托，计算移动的距离\n                    const xl = e.clientX - disX;\n                    const yl = e.clientY - disY\n                    dragDom.style.width = `${curWidth + xl}px`;\n                    dragDom.style.height = `${curHeight + yl}px`;\n                };\n                document.onmouseup = function(e) {\n                    document.onmousemove = null;\n                    document.onmouseup = null;\n                };\n            }, false);\n        dragDom.appendChild(lineEl);\n    }\n}"
  },
  {
    "path": "ruoyi-ui/src/directive/dialog/dragWidth.js",
    "content": "/**\n* v-dialogDragWidth 可拖动弹窗宽度（右侧边）\n* Copyright (c) 2019 ruoyi\n*/\n\nexport default {\n    bind(el) {\n        const dragDom = el.querySelector('.el-dialog');\n        const lineEl = document.createElement('div');\n        lineEl.style = 'width: 5px; background: inherit; height: 80%; position: absolute; right: 0; top: 0; bottom: 0; margin: auto; z-index: 1; cursor: w-resize;';\n        lineEl.addEventListener('mousedown',\n            function (e) {\n                // 鼠标按下，计算当前元素距离可视区的距离\n                const disX = e.clientX - el.offsetLeft;\n                // 当前宽度\n                const curWidth = dragDom.offsetWidth;\n                document.onmousemove = function (e) {\n                    e.preventDefault(); // 移动时禁用默认事件\n                    // 通过事件委托，计算移动的距离\n                    const l = e.clientX - disX;\n                    dragDom.style.width = `${curWidth + l}px`;\n                };\n                document.onmouseup = function (e) {\n                    document.onmousemove = null;\n                    document.onmouseup = null;\n                };\n            }, false);\n        dragDom.appendChild(lineEl);\n    }\n}"
  },
  {
    "path": "ruoyi-ui/src/directive/index.js",
    "content": "import hasRole from './permission/hasRole'\nimport hasPermi from './permission/hasPermi'\nimport dialogDrag from './dialog/drag'\nimport dialogDragWidth from './dialog/dragWidth'\nimport dialogDragHeight from './dialog/dragHeight'\nimport clipboard from './module/clipboard'\n\nconst install = function(Vue) {\n  Vue.directive('hasRole', hasRole)\n  Vue.directive('hasPermi', hasPermi)\n  Vue.directive('clipboard', clipboard)\n  Vue.directive('dialogDrag', dialogDrag)\n  Vue.directive('dialogDragWidth', dialogDragWidth)\n  Vue.directive('dialogDragHeight', dialogDragHeight)\n}\n\nif (window.Vue) {\n  window['hasRole'] = hasRole\n  window['hasPermi'] = hasPermi\n  Vue.use(install); // eslint-disable-line\n}\n\nexport default install\n"
  },
  {
    "path": "ruoyi-ui/src/directive/module/clipboard.js",
    "content": "/**\n* v-clipboard 文字复制剪贴\n* Copyright (c) 2021 ruoyi\n*/\n\nimport Clipboard from 'clipboard'\nexport default {\n  bind(el, binding, vnode) {\n    switch (binding.arg) {\n      case 'success':\n        el._vClipBoard_success = binding.value;\n        break;\n      case 'error':\n        el._vClipBoard_error = binding.value;\n        break;\n      default: {\n        const clipboard = new Clipboard(el, {\n          text: () => binding.value,\n          action: () => binding.arg === 'cut' ? 'cut' : 'copy'\n        });\n        clipboard.on('success', e => {\n          const callback = el._vClipBoard_success;\n          callback && callback(e);\n        });\n        clipboard.on('error', e => {\n          const callback = el._vClipBoard_error;\n          callback && callback(e);\n        });\n        el._vClipBoard = clipboard;\n      }\n    }\n  },\n  update(el, binding) {\n    if (binding.arg === 'success') {\n      el._vClipBoard_success = binding.value;\n    } else if (binding.arg === 'error') {\n      el._vClipBoard_error = binding.value;\n    } else {\n      el._vClipBoard.text = function () { return binding.value; };\n      el._vClipBoard.action = () => binding.arg === 'cut' ? 'cut' : 'copy';\n    }\n  },\n  unbind(el, binding) {\n    if (!el._vClipboard) return\n    if (binding.arg === 'success') {\n      delete el._vClipBoard_success;\n    } else if (binding.arg === 'error') {\n      delete el._vClipBoard_error;\n    } else {\n      el._vClipBoard.destroy();\n      delete el._vClipBoard;\n    }\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/src/directive/permission/hasPermi.js",
    "content": " /**\n * v-hasPermi 操作权限处理\n * Copyright (c) 2019 ruoyi\n */\n\nimport store from '@/store'\n\nexport default {\n  inserted(el, binding, vnode) {\n    const { value } = binding\n    const all_permission = \"*:*:*\";\n    const permissions = store.getters && store.getters.permissions\n\n    if (value && value instanceof Array && value.length > 0) {\n      const permissionFlag = value\n\n      const hasPermissions = permissions.some(permission => {\n        return all_permission === permission || permissionFlag.includes(permission)\n      })\n\n      if (!hasPermissions) {\n        el.parentNode && el.parentNode.removeChild(el)\n      }\n    } else {\n      throw new Error(`请设置操作权限标签值`)\n    }\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/src/directive/permission/hasRole.js",
    "content": " /**\n * v-hasRole 角色权限处理\n * Copyright (c) 2019 ruoyi\n */\n\nimport store from '@/store'\n\nexport default {\n  inserted(el, binding, vnode) {\n    const { value } = binding\n    const super_admin = \"admin\";\n    const roles = store.getters && store.getters.roles\n\n    if (value && value instanceof Array && value.length > 0) {\n      const roleFlag = value\n\n      const hasRole = roles.some(role => {\n        return super_admin === role || roleFlag.includes(role)\n      })\n\n      if (!hasRole) {\n        el.parentNode && el.parentNode.removeChild(el)\n      }\n    } else {\n      throw new Error(`请设置角色权限标签值\"`)\n    }\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/src/layout/components/AppMain.vue",
    "content": "<template>\n  <section class=\"app-main\">\n    <transition name=\"fade-transform\" mode=\"out-in\">\n      <keep-alive :include=\"cachedViews\">\n        <router-view v-if=\"!$route.meta.link\" :key=\"key\" />\n      </keep-alive>\n    </transition>\n    <iframe-toggle />\n  </section>\n</template>\n\n<script>\nimport iframeToggle from \"./IframeToggle/index\"\n\nexport default {\n  name: 'AppMain',\n  components: { iframeToggle },\n  computed: {\n    cachedViews() {\n      return this.$store.state.tagsView.cachedViews\n    },\n    key() {\n      return this.$route.path\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.app-main {\n  /* 50= navbar  50  */\n  min-height: calc(100vh - 50px);\n  width: 100%;\n  position: relative;\n  overflow: hidden;\n}\n\n.fixed-header + .app-main {\n  padding-top: 50px;\n}\n\n.hasTagsView {\n  .app-main {\n    /* 84 = navbar + tags-view = 50 + 34 */\n    min-height: calc(100vh - 84px);\n  }\n\n  .fixed-header + .app-main {\n    padding-top: 84px;\n  }\n}\n</style>\n\n<style lang=\"scss\">\n// fix css style bug in open el-dialog\n.el-popup-parent--hidden {\n  .fixed-header {\n    padding-right: 6px;\n  }\n}\n\n::-webkit-scrollbar {\n  width: 6px;\n  height: 6px;\n}\n\n::-webkit-scrollbar-track {\n  background-color: #f1f1f1;\n}\n\n::-webkit-scrollbar-thumb {\n  background-color: #c0c0c0;\n  border-radius: 3px;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/layout/components/IframeToggle/index.vue",
    "content": "<template>\n  <transition-group name=\"fade-transform\" mode=\"out-in\">\n    <inner-link\n      v-for=\"(item, index) in iframeViews\"\n      :key=\"item.path\"\n      :iframeId=\"'iframe' + index\"\n      v-show=\"$route.path === item.path\"\n      :src=\"item.meta.link\"\n    ></inner-link>\n  </transition-group>\n</template>\n\n<script>\nimport InnerLink from \"../InnerLink/index\"\n\nexport default {\n  components: { InnerLink },\n  computed: {\n    iframeViews() {\n      return this.$store.state.tagsView.iframeViews\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/layout/components/InnerLink/index.vue",
    "content": "<template>\n  <div :style=\"'height:' + height\" v-loading=\"loading\" element-loading-text=\"正在加载页面，请稍候！\">\n    <iframe\n      :id=\"iframeId\"\n      style=\"width: 100%; height: 100%\"\n      :src=\"src\"\n      frameborder=\"no\"\n    ></iframe>\n  </div>\n</template>\n\n<script>\nexport default {\n  props: {\n    src: {\n      type: String,\n      default: \"/\"\n    },\n    iframeId: {\n      type: String\n    }\n  },\n  data() {\n    return {\n      loading: false,\n      height: document.documentElement.clientHeight - 94.5 + \"px;\"\n    };\n  },\n  mounted() {\n    var _this = this;\n    const iframeId = (\"#\" + this.iframeId).replace(/\\//g, \"\\\\/\");\n    const iframe = document.querySelector(iframeId);\n    // iframe页面loading控制\n    if (iframe.attachEvent) {\n      this.loading = true;\n      iframe.attachEvent(\"onload\", function () {\n        _this.loading = false;\n      });\n    } else {\n      this.loading = true;\n      iframe.onload = function () {\n        _this.loading = false;\n      };\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/layout/components/Navbar.vue",
    "content": "<template>\n  <div class=\"navbar\">\n    <hamburger id=\"hamburger-container\" :is-active=\"sidebar.opened\" class=\"hamburger-container\" @toggleClick=\"toggleSideBar\" />\n\n    <breadcrumb id=\"breadcrumb-container\" class=\"breadcrumb-container\" v-if=\"!topNav\"/>\n    <top-nav id=\"topmenu-container\" class=\"topmenu-container\" v-if=\"topNav\"/>\n\n    <div class=\"right-menu\">\n      <template v-if=\"device!=='mobile'\">\n        <search id=\"header-search\" class=\"right-menu-item\" />\n\n<!--        <el-tooltip content=\"源码地址\" effect=\"dark\" placement=\"bottom\">-->\n<!--          <ruo-yi-git id=\"ruoyi-git\" class=\"right-menu-item hover-effect\" />-->\n<!--        </el-tooltip>-->\n\n<!--        <el-tooltip content=\"文档地址\" effect=\"dark\" placement=\"bottom\">-->\n<!--          <ruo-yi-doc id=\"ruoyi-doc\" class=\"right-menu-item hover-effect\" />-->\n<!--        </el-tooltip>-->\n\n        <screenfull id=\"screenfull\" class=\"right-menu-item hover-effect\" />\n\n        <el-tooltip content=\"布局大小\" effect=\"dark\" placement=\"bottom\">\n          <size-select id=\"size-select\" class=\"right-menu-item hover-effect\" />\n        </el-tooltip>\n\n      </template>\n\n      <el-dropdown class=\"avatar-container right-menu-item hover-effect\" trigger=\"click\">\n        <div class=\"avatar-wrapper\">\n          <img :src=\"avatar\" class=\"user-avatar\">\n          <i class=\"el-icon-caret-bottom\" />\n        </div>\n        <el-dropdown-menu slot=\"dropdown\">\n          <router-link to=\"/user/profile\">\n            <el-dropdown-item>个人中心</el-dropdown-item>\n          </router-link>\n          <el-dropdown-item @click.native=\"setting = true\">\n            <span>布局设置</span>\n          </el-dropdown-item>\n          <el-dropdown-item divided @click.native=\"logout\">\n            <span>退出登录</span>\n          </el-dropdown-item>\n        </el-dropdown-menu>\n      </el-dropdown>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { mapGetters } from 'vuex'\nimport Breadcrumb from '@/components/Breadcrumb'\nimport TopNav from '@/components/TopNav'\nimport Hamburger from '@/components/Hamburger'\nimport Screenfull from '@/components/Screenfull'\nimport SizeSelect from '@/components/SizeSelect'\nimport Search from '@/components/HeaderSearch'\n// import RuoYiGit from '@/components/RuoYi/Git'\n// import RuoYiDoc from '@/components/RuoYi/Doc'\n\nexport default {\n  components: {\n    Breadcrumb,\n    TopNav,\n    Hamburger,\n    Screenfull,\n    SizeSelect,\n    Search,\n    // RuoYiGit,\n    // RuoYiDoc\n  },\n  computed: {\n    ...mapGetters([\n      'sidebar',\n      'avatar',\n      'device'\n    ]),\n    setting: {\n      get() {\n        return this.$store.state.settings.showSettings\n      },\n      set(val) {\n        this.$store.dispatch('settings/changeSetting', {\n          key: 'showSettings',\n          value: val\n        })\n      }\n    },\n    topNav: {\n      get() {\n        return this.$store.state.settings.topNav\n      }\n    }\n  },\n  methods: {\n    toggleSideBar() {\n      this.$store.dispatch('app/toggleSideBar')\n    },\n    async logout() {\n      this.$confirm('确定注销并退出系统吗？', '提示', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning'\n      }).then(() => {\n        this.$store.dispatch('LogOut').then(() => {\n          location.href = process.env.VUE_APP_CONTEXT_PATH + \"index\";\n        })\n      }).catch(() => {});\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.navbar {\n  height: 50px;\n  overflow: hidden;\n  position: relative;\n  background: #fff;\n  box-shadow: 0 1px 4px rgba(0,21,41,.08);\n\n  .hamburger-container {\n    line-height: 46px;\n    height: 100%;\n    float: left;\n    cursor: pointer;\n    transition: background .3s;\n    -webkit-tap-highlight-color:transparent;\n\n    &:hover {\n      background: rgba(0, 0, 0, .025)\n    }\n  }\n\n  .breadcrumb-container {\n    float: left;\n  }\n\n  .topmenu-container {\n    position: absolute;\n    left: 50px;\n  }\n\n  .errLog-container {\n    display: inline-block;\n    vertical-align: top;\n  }\n\n  .right-menu {\n    float: right;\n    height: 100%;\n    line-height: 50px;\n\n    &:focus {\n      outline: none;\n    }\n\n    .right-menu-item {\n      display: inline-block;\n      padding: 0 8px;\n      height: 100%;\n      font-size: 18px;\n      color: #5a5e66;\n      vertical-align: text-bottom;\n\n      &.hover-effect {\n        cursor: pointer;\n        transition: background .3s;\n\n        &:hover {\n          background: rgba(0, 0, 0, .025)\n        }\n      }\n    }\n\n    .avatar-container {\n      margin-right: 30px;\n\n      .avatar-wrapper {\n        margin-top: 5px;\n        position: relative;\n\n        .user-avatar {\n          cursor: pointer;\n          width: 40px;\n          height: 40px;\n          border-radius: 10px;\n        }\n\n        .el-icon-caret-bottom {\n          cursor: pointer;\n          position: absolute;\n          right: -20px;\n          top: 25px;\n          font-size: 12px;\n        }\n      }\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/layout/components/Settings/index.vue",
    "content": "<template>\n  <el-drawer size=\"280px\" :visible=\"visible\" :with-header=\"false\" :append-to-body=\"true\" :show-close=\"false\">\n    <div class=\"drawer-container\">\n      <div>\n        <div class=\"setting-drawer-content\">\n          <div class=\"setting-drawer-title\">\n            <h3 class=\"drawer-title\">主题风格设置</h3>\n          </div>\n          <div class=\"setting-drawer-block-checbox\">\n            <div class=\"setting-drawer-block-checbox-item\" @click=\"handleTheme('theme-dark')\">\n              <img src=\"@/assets/images/dark.svg\" alt=\"dark\">\n              <div v-if=\"sideTheme === 'theme-dark'\" class=\"setting-drawer-block-checbox-selectIcon\" style=\"display: block;\">\n                <i aria-label=\"图标: check\" class=\"anticon anticon-check\">\n                  <svg viewBox=\"64 64 896 896\" data-icon=\"check\" width=\"1em\" height=\"1em\" :fill=\"theme\" aria-hidden=\"true\" focusable=\"false\" class=\"\">\n                    <path d=\"M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z\"/>\n                  </svg>\n                </i>\n              </div>\n            </div>\n            <div class=\"setting-drawer-block-checbox-item\" @click=\"handleTheme('theme-light')\">\n              <img src=\"@/assets/images/light.svg\" alt=\"light\">\n              <div v-if=\"sideTheme === 'theme-light'\" class=\"setting-drawer-block-checbox-selectIcon\" style=\"display: block;\">\n                <i aria-label=\"图标: check\" class=\"anticon anticon-check\">\n                  <svg viewBox=\"64 64 896 896\" data-icon=\"check\" width=\"1em\" height=\"1em\" :fill=\"theme\" aria-hidden=\"true\" focusable=\"false\" class=\"\">\n                    <path d=\"M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z\"/>\n                  </svg>\n                </i>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"drawer-item\">\n            <span>主题颜色</span>\n            <theme-picker style=\"float: right;height: 26px;margin: -3px 8px 0 0;\" @change=\"themeChange\" />\n          </div>\n        </div>\n\n        <el-divider/>\n\n        <h3 class=\"drawer-title\">系统布局配置</h3>\n\n        <div class=\"drawer-item\">\n          <span>开启 TopNav</span>\n          <el-switch v-model=\"topNav\" class=\"drawer-switch\" />\n        </div>\n\n        <div class=\"drawer-item\">\n          <span>开启 Tags-Views</span>\n          <el-switch v-model=\"tagsView\" class=\"drawer-switch\" />\n        </div>\n\n        <div class=\"drawer-item\">\n          <span>固定 Header</span>\n          <el-switch v-model=\"fixedHeader\" class=\"drawer-switch\" />\n        </div>\n\n        <div class=\"drawer-item\">\n          <span>显示 Logo</span>\n          <el-switch v-model=\"sidebarLogo\" class=\"drawer-switch\" />\n        </div>\n\n        <div class=\"drawer-item\">\n          <span>动态标题</span>\n          <el-switch v-model=\"dynamicTitle\" class=\"drawer-switch\" />\n        </div>\n\n        <el-divider/>\n\n        <el-button size=\"small\" type=\"primary\" plain icon=\"el-icon-document-add\" @click=\"saveSetting\">保存配置</el-button>\n        <el-button size=\"small\" plain icon=\"el-icon-refresh\" @click=\"resetSetting\">重置配置</el-button>\n      </div>\n    </div>\n  </el-drawer>\n</template>\n\n<script>\nimport ThemePicker from '@/components/ThemePicker'\n\nexport default {\n  components: { ThemePicker },\n  data() {\n    return {\n      theme: this.$store.state.settings.theme,\n      sideTheme: this.$store.state.settings.sideTheme\n    };\n  },\n  computed: {\n    visible: {\n      get() {\n        return this.$store.state.settings.showSettings\n      }\n    },\n    fixedHeader: {\n      get() {\n        return this.$store.state.settings.fixedHeader\n      },\n      set(val) {\n        this.$store.dispatch('settings/changeSetting', {\n          key: 'fixedHeader',\n          value: val\n        })\n      }\n    },\n    topNav: {\n      get() {\n        return this.$store.state.settings.topNav\n      },\n      set(val) {\n        this.$store.dispatch('settings/changeSetting', {\n          key: 'topNav',\n          value: val\n        })\n        if (!val) {\n          this.$store.dispatch('app/toggleSideBarHide', false);\n          this.$store.commit(\"SET_SIDEBAR_ROUTERS\", this.$store.state.permission.defaultRoutes);\n        }\n      }\n    },\n    tagsView: {\n      get() {\n        return this.$store.state.settings.tagsView\n      },\n      set(val) {\n        this.$store.dispatch('settings/changeSetting', {\n          key: 'tagsView',\n          value: val\n        })\n      }\n    },\n    sidebarLogo: {\n      get() {\n        return this.$store.state.settings.sidebarLogo\n      },\n      set(val) {\n        this.$store.dispatch('settings/changeSetting', {\n          key: 'sidebarLogo',\n          value: val\n        })\n      }\n    },\n    dynamicTitle: {\n      get() {\n        return this.$store.state.settings.dynamicTitle\n      },\n      set(val) {\n        this.$store.dispatch('settings/changeSetting', {\n          key: 'dynamicTitle',\n          value: val\n        })\n      }\n    },\n  },\n  methods: {\n    themeChange(val) {\n      this.$store.dispatch('settings/changeSetting', {\n        key: 'theme',\n        value: val\n      })\n      this.theme = val;\n    },\n    handleTheme(val) {\n      this.$store.dispatch('settings/changeSetting', {\n        key: 'sideTheme',\n        value: val\n      })\n      this.sideTheme = val;\n    },\n    saveSetting() {\n      this.$modal.loading(\"正在保存到本地，请稍候...\");\n      this.$cache.local.set(\n        \"layout-setting\",\n        `{\n            \"topNav\":${this.topNav},\n            \"tagsView\":${this.tagsView},\n            \"fixedHeader\":${this.fixedHeader},\n            \"sidebarLogo\":${this.sidebarLogo},\n            \"dynamicTitle\":${this.dynamicTitle},\n            \"sideTheme\":\"${this.sideTheme}\",\n            \"theme\":\"${this.theme}\"\n          }`\n      );\n      setTimeout(this.$modal.closeLoading(), 1000)\n    },\n    resetSetting() {\n      this.$modal.loading(\"正在清除设置缓存并刷新，请稍候...\");\n      this.$cache.local.remove(\"layout-setting\")\n      setTimeout(\"window.location.reload()\", 1000)\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n  .setting-drawer-content {\n    .setting-drawer-title {\n      margin-bottom: 12px;\n      color: rgba(0, 0, 0, .85);\n      font-size: 14px;\n      line-height: 22px;\n      font-weight: bold;\n    }\n\n    .setting-drawer-block-checbox {\n      display: flex;\n      justify-content: flex-start;\n      align-items: center;\n      margin-top: 10px;\n      margin-bottom: 20px;\n\n      .setting-drawer-block-checbox-item {\n        position: relative;\n        margin-right: 16px;\n        border-radius: 2px;\n        cursor: pointer;\n\n        img {\n          width: 48px;\n          height: 48px;\n        }\n\n        .setting-drawer-block-checbox-selectIcon {\n          position: absolute;\n          top: 0;\n          right: 0;\n          width: 100%;\n          height: 100%;\n          padding-top: 15px;\n          padding-left: 24px;\n          color: #1890ff;\n          font-weight: 700;\n          font-size: 14px;\n        }\n      }\n    }\n  }\n\n  .drawer-container {\n    padding: 20px;\n    font-size: 14px;\n    line-height: 1.5;\n    word-wrap: break-word;\n\n    .drawer-title {\n      margin-bottom: 12px;\n      color: rgba(0, 0, 0, .85);\n      font-size: 14px;\n      line-height: 22px;\n    }\n\n    .drawer-item {\n      color: rgba(0, 0, 0, .65);\n      font-size: 14px;\n      padding: 12px 0;\n    }\n\n    .drawer-switch {\n      float: right\n    }\n  }\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/layout/components/Sidebar/FixiOSBug.js",
    "content": "export default {\n  computed: {\n    device() {\n      return this.$store.state.app.device\n    }\n  },\n  mounted() {\n    // In order to fix the click on menu on the ios device will trigger the mouseleave bug\n    this.fixBugIniOS()\n  },\n  methods: {\n    fixBugIniOS() {\n      const $subMenu = this.$refs.subMenu\n      if ($subMenu) {\n        const handleMouseleave = $subMenu.handleMouseleave\n        $subMenu.handleMouseleave = (e) => {\n          if (this.device === 'mobile') {\n            return\n          }\n          handleMouseleave(e)\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/src/layout/components/Sidebar/Item.vue",
    "content": "<script>\nexport default {\n  name: 'MenuItem',\n  functional: true,\n  props: {\n    icon: {\n      type: String,\n      default: ''\n    },\n    title: {\n      type: String,\n      default: ''\n    }\n  },\n  render(h, context) {\n    const { icon, title } = context.props\n    const vnodes = []\n\n    if (icon) {\n      vnodes.push(<svg-icon icon-class={icon}/>)\n    }\n\n    if (title) {\n      if (title.length > 5) {\n        vnodes.push(<span slot='title' title={(title)}>{(title)}</span>)\n      } else {\n        vnodes.push(<span slot='title'>{(title)}</span>)\n      }\n    }\n    return vnodes\n  }\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/layout/components/Sidebar/Link.vue",
    "content": "<template>\n  <component :is=\"type\" v-bind=\"linkProps(to)\">\n    <slot />\n  </component>\n</template>\n\n<script>\nimport { isExternal } from '@/utils/validate'\n\nexport default {\n  props: {\n    to: {\n      type: [String, Object],\n      required: true\n    }\n  },\n  computed: {\n    isExternal() {\n      return isExternal(this.to)\n    },\n    type() {\n      if (this.isExternal) {\n        return 'a'\n      }\n      return 'router-link'\n    }\n  },\n  methods: {\n    linkProps(to) {\n      if (this.isExternal) {\n        return {\n          href: to,\n          target: '_blank',\n          rel: 'noopener'\n        }\n      }\n      return {\n        to: to\n      }\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/layout/components/Sidebar/Logo.vue",
    "content": "<template>\n  <div class=\"sidebar-logo-container\" :class=\"{'collapse':collapse}\" :style=\"{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }\">\n    <transition name=\"sidebarLogoFade\">\n      <router-link v-if=\"collapse\" key=\"collapse\" class=\"sidebar-logo-link\" to=\"/\">\n        <img v-if=\"logo\" :src=\"logo\" class=\"sidebar-logo\" />\n        <h1 v-else class=\"sidebar-title\" :style=\"{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }\">{{ title }} </h1>\n      </router-link>\n      <router-link v-else key=\"expand\" class=\"sidebar-logo-link\" to=\"/\">\n        <img v-if=\"logo\" :src=\"logo\" class=\"sidebar-logo\" />\n        <h1 class=\"sidebar-title\" :style=\"{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }\">{{ title }} </h1>\n      </router-link>\n    </transition>\n  </div>\n</template>\n\n<script>\nimport logoImg from '@/assets/logo/1.png'\nimport variables from '@/assets/styles/variables.scss'\n\nexport default {\n  name: 'SidebarLogo',\n  props: {\n    collapse: {\n      type: Boolean,\n      required: true\n    }\n  },\n  computed: {\n    variables() {\n      return variables;\n    },\n    sideTheme() {\n      return this.$store.state.settings.sideTheme\n    }\n  },\n  data() {\n    return {\n      title: '派之城后台管理',\n      logo: logoImg\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.sidebarLogoFade-enter-active {\n  transition: opacity 1.5s;\n}\n\n.sidebarLogoFade-enter,\n.sidebarLogoFade-leave-to {\n  opacity: 0;\n}\n\n.sidebar-logo-container {\n  position: relative;\n  width: 100%;\n  height: 50px;\n  line-height: 50px;\n  background: #2b2f3a;\n  text-align: center;\n  overflow: hidden;\n\n  & .sidebar-logo-link {\n    height: 100%;\n    width: 100%;\n\n    & .sidebar-logo {\n      width: 32px;\n      height: 32px;\n      vertical-align: middle;\n      margin-right: 12px;\n    }\n\n    & .sidebar-title {\n      display: inline-block;\n      margin: 0;\n      color: #fff;\n      font-weight: 600;\n      line-height: 50px;\n      font-size: 14px;\n      font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;\n      vertical-align: middle;\n    }\n  }\n\n  &.collapse {\n    .sidebar-logo {\n      margin-right: 0px;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/layout/components/Sidebar/SidebarItem.vue",
    "content": "<template>\n  <div v-if=\"!item.hidden\">\n    <template v-if=\"hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow\">\n      <app-link v-if=\"onlyOneChild.meta\" :to=\"resolvePath(onlyOneChild.path, onlyOneChild.query)\">\n        <el-menu-item :index=\"resolvePath(onlyOneChild.path)\" :class=\"{'submenu-title-noDropdown':!isNest}\">\n          <item :icon=\"onlyOneChild.meta.icon||(item.meta&&item.meta.icon)\" :title=\"onlyOneChild.meta.title\" />\n        </el-menu-item>\n      </app-link>\n    </template>\n\n    <el-submenu v-else ref=\"subMenu\" :index=\"resolvePath(item.path)\" popper-append-to-body>\n      <template slot=\"title\">\n        <item v-if=\"item.meta\" :icon=\"item.meta && item.meta.icon\" :title=\"item.meta.title\" />\n      </template>\n      <sidebar-item\n        v-for=\"child in item.children\"\n        :key=\"child.path\"\n        :is-nest=\"true\"\n        :item=\"child\"\n        :base-path=\"resolvePath(child.path)\"\n        class=\"nest-menu\"\n      />\n    </el-submenu>\n  </div>\n</template>\n\n<script>\nimport path from 'path'\nimport { isExternal } from '@/utils/validate'\nimport Item from './Item'\nimport AppLink from './Link'\nimport FixiOSBug from './FixiOSBug'\n\nexport default {\n  name: 'SidebarItem',\n  components: { Item, AppLink },\n  mixins: [FixiOSBug],\n  props: {\n    // route object\n    item: {\n      type: Object,\n      required: true\n    },\n    isNest: {\n      type: Boolean,\n      default: false\n    },\n    basePath: {\n      type: String,\n      default: ''\n    }\n  },\n  data() {\n    this.onlyOneChild = null\n    return {}\n  },\n  methods: {\n    hasOneShowingChild(children = [], parent) {\n      if (!children) {\n        children = [];\n      }\n      const showingChildren = children.filter(item => {\n        if (item.hidden) {\n          return false\n        } else {\n          // Temp set(will be used if only has one showing child)\n          this.onlyOneChild = item\n          return true\n        }\n      })\n\n      // When there is only one child router, the child router is displayed by default\n      if (showingChildren.length === 1) {\n        return true\n      }\n\n      // Show parent if there are no child router to display\n      if (showingChildren.length === 0) {\n        this.onlyOneChild = { ... parent, path: '', noShowingChildren: true }\n        return true\n      }\n\n      return false\n    },\n    resolvePath(routePath, routeQuery) {\n      if (isExternal(routePath)) {\n        return routePath\n      }\n      if (isExternal(this.basePath)) {\n        return this.basePath\n      }\n      if (routeQuery) {\n        let query = JSON.parse(routeQuery);\n        return { path: path.resolve(this.basePath, routePath), query: query }\n      }\n      return path.resolve(this.basePath, routePath)\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/layout/components/Sidebar/index.vue",
    "content": "<template>\n    <div :class=\"{'has-logo':showLogo}\" :style=\"{ backgroundColor: settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }\">\n        <logo v-if=\"showLogo\" :collapse=\"isCollapse\" />\n        <el-scrollbar :class=\"settings.sideTheme\" wrap-class=\"scrollbar-wrapper\">\n            <el-menu\n                :default-active=\"activeMenu\"\n                :collapse=\"isCollapse\"\n                :background-color=\"settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground\"\n                :text-color=\"settings.sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor\"\n                :unique-opened=\"true\"\n                :active-text-color=\"settings.theme\"\n                :collapse-transition=\"false\"\n                mode=\"vertical\"\n            >\n                <sidebar-item\n                    v-for=\"(route, index) in sidebarRouters\"\n                    :key=\"route.path  + index\"\n                    :item=\"route\"\n                    :base-path=\"route.path\"\n                />\n            </el-menu>\n        </el-scrollbar>\n    </div>\n</template>\n\n<script>\nimport { mapGetters, mapState } from \"vuex\";\nimport Logo from \"./Logo\";\nimport SidebarItem from \"./SidebarItem\";\nimport variables from \"@/assets/styles/variables.scss\";\n\nexport default {\n    components: { SidebarItem, Logo },\n    computed: {\n        ...mapState([\"settings\"]),\n        ...mapGetters([\"sidebarRouters\", \"sidebar\"]),\n        activeMenu() {\n            const route = this.$route;\n            const { meta, path } = route;\n            // if set path, the sidebar will highlight the path you set\n            if (meta.activeMenu) {\n                return meta.activeMenu;\n            }\n            return path;\n        },\n        showLogo() {\n            return this.$store.state.settings.sidebarLogo;\n        },\n        variables() {\n            return variables;\n        },\n        isCollapse() {\n            return !this.sidebar.opened;\n        }\n    }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue",
    "content": "<template>\n  <el-scrollbar ref=\"scrollContainer\" :vertical=\"false\" class=\"scroll-container\" @wheel.native.prevent=\"handleScroll\">\n    <slot />\n  </el-scrollbar>\n</template>\n\n<script>\nconst tagAndTagSpacing = 4 // tagAndTagSpacing\n\nexport default {\n  name: 'ScrollPane',\n  data() {\n    return {\n      left: 0\n    }\n  },\n  computed: {\n    scrollWrapper() {\n      return this.$refs.scrollContainer.$refs.wrap\n    }\n  },\n  mounted() {\n    this.scrollWrapper.addEventListener('scroll', this.emitScroll, true)\n  },\n  beforeDestroy() {\n    this.scrollWrapper.removeEventListener('scroll', this.emitScroll)\n  },\n  methods: {\n    handleScroll(e) {\n      const eventDelta = e.wheelDelta || -e.deltaY * 40\n      const $scrollWrapper = this.scrollWrapper\n      $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4\n    },\n    emitScroll() {\n      this.$emit('scroll')\n    },\n    moveToTarget(currentTag) {\n      const $container = this.$refs.scrollContainer.$el\n      const $containerWidth = $container.offsetWidth\n      const $scrollWrapper = this.scrollWrapper\n      const tagList = this.$parent.$refs.tag\n\n      let firstTag = null\n      let lastTag = null\n\n      // find first tag and last tag\n      if (tagList.length > 0) {\n        firstTag = tagList[0]\n        lastTag = tagList[tagList.length - 1]\n      }\n\n      if (firstTag === currentTag) {\n        $scrollWrapper.scrollLeft = 0\n      } else if (lastTag === currentTag) {\n        $scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth\n      } else {\n        // find preTag and nextTag\n        const currentIndex = tagList.findIndex(item => item === currentTag)\n        const prevTag = tagList[currentIndex - 1]\n        const nextTag = tagList[currentIndex + 1]\n\n        // the tag's offsetLeft after of nextTag\n        const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing\n\n        // the tag's offsetLeft before of prevTag\n        const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing\n\n        if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {\n          $scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth\n        } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {\n          $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft\n        }\n      }\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.scroll-container {\n  white-space: nowrap;\n  position: relative;\n  overflow: hidden;\n  width: 100%;\n  ::v-deep {\n    .el-scrollbar__bar {\n      bottom: 0px;\n    }\n    .el-scrollbar__wrap {\n      height: 39px;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/layout/components/TagsView/index.vue",
    "content": "<template>\n  <div id=\"tags-view-container\" class=\"tags-view-container\">\n    <scroll-pane ref=\"scrollPane\" class=\"tags-view-wrapper\" @scroll=\"handleScroll\">\n      <router-link\n        v-for=\"tag in visitedViews\"\n        ref=\"tag\"\n        :key=\"tag.path\"\n        :class=\"isActive(tag)?'active':''\"\n        :to=\"{ path: tag.path, query: tag.query, fullPath: tag.fullPath }\"\n        tag=\"span\"\n        class=\"tags-view-item\"\n        :style=\"activeStyle(tag)\"\n        @click.middle.native=\"!isAffix(tag)?closeSelectedTag(tag):''\"\n        @contextmenu.prevent.native=\"openMenu(tag,$event)\"\n      >\n        {{ tag.title }}\n        <span v-if=\"!isAffix(tag)\" class=\"el-icon-close\" @click.prevent.stop=\"closeSelectedTag(tag)\" />\n      </router-link>\n    </scroll-pane>\n    <ul v-show=\"visible\" :style=\"{left:left+'px',top:top+'px'}\" class=\"contextmenu\">\n      <li @click=\"refreshSelectedTag(selectedTag)\"><i class=\"el-icon-refresh-right\"></i> 刷新页面</li>\n      <li v-if=\"!isAffix(selectedTag)\" @click=\"closeSelectedTag(selectedTag)\"><i class=\"el-icon-close\"></i> 关闭当前</li>\n      <li @click=\"closeOthersTags\"><i class=\"el-icon-circle-close\"></i> 关闭其他</li>\n      <li v-if=\"!isFirstView()\" @click=\"closeLeftTags\"><i class=\"el-icon-back\"></i> 关闭左侧</li>\n      <li v-if=\"!isLastView()\" @click=\"closeRightTags\"><i class=\"el-icon-right\"></i> 关闭右侧</li>\n      <li @click=\"closeAllTags(selectedTag)\"><i class=\"el-icon-circle-close\"></i> 全部关闭</li>\n    </ul>\n  </div>\n</template>\n\n<script>\nimport ScrollPane from './ScrollPane'\nimport path from 'path'\n\nexport default {\n  components: { ScrollPane },\n  data() {\n    return {\n      visible: false,\n      top: 0,\n      left: 0,\n      selectedTag: {},\n      affixTags: []\n    }\n  },\n  computed: {\n    visitedViews() {\n      return this.$store.state.tagsView.visitedViews\n    },\n    routes() {\n      return this.$store.state.permission.routes\n    },\n    theme() {\n      return this.$store.state.settings.theme;\n    }\n  },\n  watch: {\n    $route() {\n      this.addTags()\n      this.moveToCurrentTag()\n    },\n    visible(value) {\n      if (value) {\n        document.body.addEventListener('click', this.closeMenu)\n      } else {\n        document.body.removeEventListener('click', this.closeMenu)\n      }\n    }\n  },\n  mounted() {\n    this.initTags()\n    this.addTags()\n  },\n  methods: {\n    isActive(route) {\n      return route.path === this.$route.path\n    },\n    activeStyle(tag) {\n      if (!this.isActive(tag)) return {};\n      return {\n        \"background-color\": this.theme,\n        \"border-color\": this.theme\n      };\n    },\n    isAffix(tag) {\n      return tag.meta && tag.meta.affix\n    },\n    isFirstView() {\n      try {\n        return this.selectedTag.fullPath === '/index' || this.selectedTag.fullPath === this.visitedViews[1].fullPath\n      } catch (err) {\n        return false\n      }\n    },\n    isLastView() {\n      try {\n        return this.selectedTag.fullPath === this.visitedViews[this.visitedViews.length - 1].fullPath\n      } catch (err) {\n        return false\n      }\n    },\n    filterAffixTags(routes, basePath = '/') {\n      let tags = []\n      routes.forEach(route => {\n        if (route.meta && route.meta.affix) {\n          const tagPath = path.resolve(basePath, route.path)\n          tags.push({\n            fullPath: tagPath,\n            path: tagPath,\n            name: route.name,\n            meta: { ...route.meta }\n          })\n        }\n        if (route.children) {\n          const tempTags = this.filterAffixTags(route.children, route.path)\n          if (tempTags.length >= 1) {\n            tags = [...tags, ...tempTags]\n          }\n        }\n      })\n      return tags\n    },\n    initTags() {\n      const affixTags = this.affixTags = this.filterAffixTags(this.routes)\n      for (const tag of affixTags) {\n        // Must have tag name\n        if (tag.name) {\n          this.$store.dispatch('tagsView/addVisitedView', tag)\n        }\n      }\n    },\n    addTags() {\n      const { name } = this.$route\n      if (name) {\n        this.$store.dispatch('tagsView/addView', this.$route)\n        if (this.$route.meta.link) {\n          this.$store.dispatch('tagsView/addIframeView', this.$route)\n        }\n      }\n      return false\n    },\n    moveToCurrentTag() {\n      const tags = this.$refs.tag\n      this.$nextTick(() => {\n        for (const tag of tags) {\n          if (tag.to.path === this.$route.path) {\n            this.$refs.scrollPane.moveToTarget(tag)\n            // when query is different then update\n            if (tag.to.fullPath !== this.$route.fullPath) {\n              this.$store.dispatch('tagsView/updateVisitedView', this.$route)\n            }\n            break\n          }\n        }\n      })\n    },\n    refreshSelectedTag(view) {\n      this.$tab.refreshPage(view);\n      if (this.$route.meta.link) {\n        this.$store.dispatch('tagsView/delIframeView', this.$route)\n      }\n    },\n    closeSelectedTag(view) {\n      this.$tab.closePage(view).then(({ visitedViews }) => {\n        if (this.isActive(view)) {\n          this.toLastView(visitedViews, view)\n        }\n      })\n    },\n    closeRightTags() {\n      this.$tab.closeRightPage(this.selectedTag).then(visitedViews => {\n        if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {\n          this.toLastView(visitedViews)\n        }\n      })\n    },\n    closeLeftTags() {\n      this.$tab.closeLeftPage(this.selectedTag).then(visitedViews => {\n        if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {\n          this.toLastView(visitedViews)\n        }\n      })\n    },\n    closeOthersTags() {\n      this.$router.push(this.selectedTag.fullPath).catch(()=>{});\n      this.$tab.closeOtherPage(this.selectedTag).then(() => {\n        this.moveToCurrentTag()\n      })\n    },\n    closeAllTags(view) {\n      this.$tab.closeAllPage().then(({ visitedViews }) => {\n        if (this.affixTags.some(tag => tag.path === this.$route.path)) {\n          return\n        }\n        this.toLastView(visitedViews, view)\n      })\n    },\n    toLastView(visitedViews, view) {\n      const latestView = visitedViews.slice(-1)[0]\n      if (latestView) {\n        this.$router.push(latestView.fullPath)\n      } else {\n        // now the default is to redirect to the home page if there is no tags-view,\n        // you can adjust it according to your needs.\n        if (view.name === 'Dashboard') {\n          // to reload home page\n          this.$router.replace({ path: '/redirect' + view.fullPath })\n        } else {\n          this.$router.push('/')\n        }\n      }\n    },\n    openMenu(tag, e) {\n      const menuMinWidth = 105\n      const offsetLeft = this.$el.getBoundingClientRect().left // container margin left\n      const offsetWidth = this.$el.offsetWidth // container width\n      const maxLeft = offsetWidth - menuMinWidth // left boundary\n      const left = e.clientX - offsetLeft + 15 // 15: margin right\n\n      if (left > maxLeft) {\n        this.left = maxLeft\n      } else {\n        this.left = left\n      }\n\n      this.top = e.clientY\n      this.visible = true\n      this.selectedTag = tag\n    },\n    closeMenu() {\n      this.visible = false\n    },\n    handleScroll() {\n      this.closeMenu()\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.tags-view-container {\n  height: 34px;\n  width: 100%;\n  background: #fff;\n  border-bottom: 1px solid #d8dce5;\n  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);\n  .tags-view-wrapper {\n    .tags-view-item {\n      display: inline-block;\n      position: relative;\n      cursor: pointer;\n      height: 26px;\n      line-height: 26px;\n      border: 1px solid #d8dce5;\n      color: #495060;\n      background: #fff;\n      padding: 0 8px;\n      font-size: 12px;\n      margin-left: 5px;\n      margin-top: 4px;\n      &:first-of-type {\n        margin-left: 15px;\n      }\n      &:last-of-type {\n        margin-right: 15px;\n      }\n      &.active {\n        background-color: #42b983;\n        color: #fff;\n        border-color: #42b983;\n        &::before {\n          content: '';\n          background: #fff;\n          display: inline-block;\n          width: 8px;\n          height: 8px;\n          border-radius: 50%;\n          position: relative;\n          margin-right: 2px;\n        }\n      }\n    }\n  }\n  .contextmenu {\n    margin: 0;\n    background: #fff;\n    z-index: 3000;\n    position: absolute;\n    list-style-type: none;\n    padding: 5px 0;\n    border-radius: 4px;\n    font-size: 12px;\n    font-weight: 400;\n    color: #333;\n    box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);\n    li {\n      margin: 0;\n      padding: 7px 16px;\n      cursor: pointer;\n      &:hover {\n        background: #eee;\n      }\n    }\n  }\n}\n</style>\n\n<style lang=\"scss\">\n//reset element css of el-icon-close\n.tags-view-wrapper {\n  .tags-view-item {\n    .el-icon-close {\n      width: 16px;\n      height: 16px;\n      vertical-align: 2px;\n      border-radius: 50%;\n      text-align: center;\n      transition: all .3s cubic-bezier(.645, .045, .355, 1);\n      transform-origin: 100% 50%;\n      &:before {\n        transform: scale(.6);\n        display: inline-block;\n        vertical-align: -3px;\n      }\n      &:hover {\n        background-color: #b4bccc;\n        color: #fff;\n      }\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/layout/components/index.js",
    "content": "export { default as AppMain } from './AppMain'\nexport { default as Navbar } from './Navbar'\nexport { default as Settings } from './Settings'\nexport { default as Sidebar } from './Sidebar/index.vue'\nexport { default as TagsView } from './TagsView/index.vue'\n"
  },
  {
    "path": "ruoyi-ui/src/layout/index.vue",
    "content": "<template>\n  <div :class=\"classObj\" class=\"app-wrapper\" :style=\"{'--current-color': theme}\">\n    <div v-if=\"device==='mobile'&&sidebar.opened\" class=\"drawer-bg\" @click=\"handleClickOutside\"/>\n    <sidebar v-if=\"!sidebar.hide\" class=\"sidebar-container\"/>\n    <div :class=\"{hasTagsView:needTagsView,sidebarHide:sidebar.hide}\" class=\"main-container\">\n      <div :class=\"{'fixed-header':fixedHeader}\">\n        <navbar/>\n        <tags-view v-if=\"needTagsView\"/>\n      </div>\n      <app-main/>\n      <right-panel>\n        <settings/>\n      </right-panel>\n    </div>\n  </div>\n</template>\n\n<script>\nimport RightPanel from '@/components/RightPanel'\nimport { AppMain, Navbar, Settings, Sidebar, TagsView } from './components'\nimport ResizeMixin from './mixin/ResizeHandler'\nimport { mapState } from 'vuex'\nimport variables from '@/assets/styles/variables.scss'\n\nexport default {\n  name: 'Layout',\n  components: {\n    AppMain,\n    Navbar,\n    RightPanel,\n    Settings,\n    Sidebar,\n    TagsView\n  },\n  mixins: [ResizeMixin],\n  computed: {\n    ...mapState({\n      theme: state => state.settings.theme,\n      sideTheme: state => state.settings.sideTheme,\n      sidebar: state => state.app.sidebar,\n      device: state => state.app.device,\n      needTagsView: state => state.settings.tagsView,\n      fixedHeader: state => state.settings.fixedHeader\n    }),\n    classObj() {\n      return {\n        hideSidebar: !this.sidebar.opened,\n        openSidebar: this.sidebar.opened,\n        withoutAnimation: this.sidebar.withoutAnimation,\n        mobile: this.device === 'mobile'\n      }\n    },\n    variables() {\n      return variables;\n    }\n  },\n  methods: {\n    handleClickOutside() {\n      this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n  @import \"~@/assets/styles/mixin.scss\";\n  @import \"~@/assets/styles/variables.scss\";\n\n  .app-wrapper {\n    @include clearfix;\n    position: relative;\n    height: 100%;\n    width: 100%;\n\n    &.mobile.openSidebar {\n      position: fixed;\n      top: 0;\n    }\n  }\n\n  .drawer-bg {\n    background: #000;\n    opacity: 0.3;\n    width: 100%;\n    top: 0;\n    height: 100%;\n    position: absolute;\n    z-index: 999;\n  }\n\n  .fixed-header {\n    position: fixed;\n    top: 0;\n    right: 0;\n    z-index: 9;\n    width: calc(100% - #{$base-sidebar-width});\n    transition: width 0.28s;\n  }\n\n  .hideSidebar .fixed-header {\n    width: calc(100% - 54px);\n  }\n\n  .sidebarHide .fixed-header {\n    width: 100%;\n  }\n\n  .mobile .fixed-header {\n    width: 100%;\n  }\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/layout/mixin/ResizeHandler.js",
    "content": "import store from '@/store'\n\nconst { body } = document\nconst WIDTH = 992 // refer to Bootstrap's responsive design\n\nexport default {\n  watch: {\n    $route(route) {\n      if (this.device === 'mobile' && this.sidebar.opened) {\n        store.dispatch('app/closeSideBar', { withoutAnimation: false })\n      }\n    }\n  },\n  beforeMount() {\n    window.addEventListener('resize', this.$_resizeHandler)\n  },\n  beforeDestroy() {\n    window.removeEventListener('resize', this.$_resizeHandler)\n  },\n  mounted() {\n    const isMobile = this.$_isMobile()\n    if (isMobile) {\n      store.dispatch('app/toggleDevice', 'mobile')\n      store.dispatch('app/closeSideBar', { withoutAnimation: true })\n    }\n  },\n  methods: {\n    // use $_ for mixins properties\n    // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential\n    $_isMobile() {\n      const rect = body.getBoundingClientRect()\n      return rect.width - 1 < WIDTH\n    },\n    $_resizeHandler() {\n      if (!document.hidden) {\n        const isMobile = this.$_isMobile()\n        store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')\n\n        if (isMobile) {\n          store.dispatch('app/closeSideBar', { withoutAnimation: true })\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/src/main.js",
    "content": "import Vue from 'vue'\n\nimport Cookies from 'js-cookie'\n\nimport Element from 'element-ui'\nimport './assets/styles/element-variables.scss'\n\nimport '@/assets/styles/index.scss' // global css\nimport '@/assets/styles/ruoyi.scss' // ruoyi css\nimport App from './App'\nimport store from './store'\nimport router from './router'\nimport directive from './directive' // directive\nimport plugins from './plugins' // plugins\nimport { download } from '@/utils/request'\n\nimport './assets/icons' // icon\nimport './permission' // permission control\nimport { getDicts } from \"@/api/system/dict/data\";\nimport { getConfigKey, updateConfigByKey } from \"@/api/system/config\";\nimport { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from \"@/utils/ruoyi\";\n// 分页组件\nimport Pagination from \"@/components/Pagination\";\n// 自定义表格工具组件\nimport RightToolbar from \"@/components/RightToolbar\"\n// 富文本组件\nimport Editor from \"@/components/Editor\"\n// 文件上传组件\nimport FileUpload from \"@/components/FileUpload\"\n// 图片上传组件\nimport ImageUpload from \"@/components/ImageUpload\"\n// 图片预览组件\nimport ImagePreview from \"@/components/ImagePreview\"\n// 字典标签组件\nimport DictTag from '@/components/DictTag'\n// 头部标签组件\nimport VueMeta from 'vue-meta'\n// 字典数据组件\nimport DictData from '@/components/DictData'\n\n// 高德地图组件\nimport VueAMap from 'vue-amap';\n// 全局方法挂载\nVue.prototype.getDicts = getDicts\nVue.prototype.getConfigKey = getConfigKey\nVue.prototype.updateConfigByKey = updateConfigByKey\nVue.prototype.parseTime = parseTime\nVue.prototype.resetForm = resetForm\nVue.prototype.addDateRange = addDateRange\nVue.prototype.selectDictLabel = selectDictLabel\nVue.prototype.selectDictLabels = selectDictLabels\nVue.prototype.download = download\nVue.prototype.handleTree = handleTree\n\n// 全局组件挂载\nVue.component('DictTag', DictTag)\nVue.component('Pagination', Pagination)\nVue.component('RightToolbar', RightToolbar)\nVue.component('Editor', Editor)\nVue.component('FileUpload', FileUpload)\nVue.component('ImageUpload', ImageUpload)\nVue.component('ImagePreview', ImagePreview)\n\nVue.use(directive)\nVue.use(plugins)\nVue.use(VueMeta)\n\nVue.use(VueAMap); // 高德地图\nDictData.install()\n\n/**\n * If you don't want to use mock-server\n * you want to use MockJs for mock api\n * you can execute: mockXHR()\n *\n * Currently MockJs will be used in the production environment,\n * please remove it before going online! ! !\n */\n\n// 修改 el-dialog 默认点击遮照为不关闭\nElement.Dialog.props.closeOnClickModal.default = false\n\nVue.use(Element, {\n  size: Cookies.get('size') || 'medium' // set element-ui default size\n})\n\nVueAMap.initAMapApiLoader({\n  key: '112049d76fe83e408d4ecceafb2ad4e3',\n  plugin: ['AMap.Autocomplete', 'AMap.PlaceSearch', 'AMap.Scale', 'AMap.OverView', 'AMap.ToolBar', 'AMap.MapType', 'AMap.PolyEditor', 'AMap.CircleEditor'],\n  // 默认高德 sdk 版本为 1.4.4\n  v: '1.4.4',\n  uiVersion: '1.0.11' // 版本号\n});\n\n//配置安全密钥\nwindow._AMapSecurityConfig = {\n  securityJsCode: 'df0902aa53f34270d790ce32e8545f1e' //*  安全密钥\n}\n\nVue.config.productionTip = false\n\nnew Vue({\n  el: '#app',\n  router,\n  store,\n  render: h => h(App)\n})\n"
  },
  {
    "path": "ruoyi-ui/src/permission.js",
    "content": "import router from './router'\nimport store from './store'\nimport { Message } from 'element-ui'\nimport NProgress from 'nprogress'\nimport 'nprogress/nprogress.css'\nimport { getToken } from '@/utils/auth'\nimport { isRelogin } from '@/utils/request'\n\nNProgress.configure({ showSpinner: false })\n\nconst whiteList = ['/login', '/register']\n\nrouter.beforeEach((to, from, next) => {\n  NProgress.start()\n  if (getToken()) {\n    to.meta.title && store.dispatch('settings/setTitle', to.meta.title)\n    /* has token*/\n    if (to.path === '/login') {\n      next({ path: '/' })\n      NProgress.done()\n    } else {\n      if (store.getters.roles.length === 0) {\n        isRelogin.show = true\n        // 判断当前用户是否已拉取完user_info信息\n        store.dispatch('GetInfo').then(() => {\n          isRelogin.show = false\n          store.dispatch('GenerateRoutes').then(accessRoutes => {\n            // 根据roles权限生成可访问的路由表\n            router.addRoutes(accessRoutes) // 动态添加可访问路由表\n            next({ ...to, replace: true }) // hack方法 确保addRoutes已完成\n          })\n        }).catch(err => {\n            store.dispatch('LogOut').then(() => {\n              Message.error(err)\n              next({ path: '/' })\n            })\n          })\n      } else {\n        next()\n      }\n    }\n  } else {\n    // 没有token\n    if (whiteList.indexOf(to.path) !== -1) {\n      // 在免登录白名单，直接进入\n      next()\n    } else {\n      next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页\n      NProgress.done()\n    }\n  }\n})\n\nrouter.afterEach(() => {\n  NProgress.done()\n})\n"
  },
  {
    "path": "ruoyi-ui/src/plugins/auth.js",
    "content": "import store from '@/store'\n\nfunction authPermission(permission) {\n  const all_permission = \"*:*:*\";\n  const permissions = store.getters && store.getters.permissions\n  if (permission && permission.length > 0) {\n    return permissions.some(v => {\n      return all_permission === v || v === permission\n    })\n  } else {\n    return false\n  }\n}\n\nfunction authRole(role) {\n  const super_admin = \"admin\";\n  const roles = store.getters && store.getters.roles\n  if (role && role.length > 0) {\n    return roles.some(v => {\n      return super_admin === v || v === role\n    })\n  } else {\n    return false\n  }\n}\n\nexport default {\n  // 验证用户是否具备某权限\n  hasPermi(permission) {\n    return authPermission(permission);\n  },\n  // 验证用户是否含有指定权限，只需包含其中一个\n  hasPermiOr(permissions) {\n    return permissions.some(item => {\n      return authPermission(item)\n    })\n  },\n  // 验证用户是否含有指定权限，必须全部拥有\n  hasPermiAnd(permissions) {\n    return permissions.every(item => {\n      return authPermission(item)\n    })\n  },\n  // 验证用户是否具备某角色\n  hasRole(role) {\n    return authRole(role);\n  },\n  // 验证用户是否含有指定角色，只需包含其中一个\n  hasRoleOr(roles) {\n    return roles.some(item => {\n      return authRole(item)\n    })\n  },\n  // 验证用户是否含有指定角色，必须全部拥有\n  hasRoleAnd(roles) {\n    return roles.every(item => {\n      return authRole(item)\n    })\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/src/plugins/cache.js",
    "content": "const sessionCache = {\n  set (key, value) {\n    if (!sessionStorage) {\n      return\n    }\n    if (key != null && value != null) {\n      sessionStorage.setItem(key, value)\n    }\n  },\n  get (key) {\n    if (!sessionStorage) {\n      return null\n    }\n    if (key == null) {\n      return null\n    }\n    return sessionStorage.getItem(key)\n  },\n  setJSON (key, jsonValue) {\n    if (jsonValue != null) {\n      this.set(key, JSON.stringify(jsonValue))\n    }\n  },\n  getJSON (key) {\n    const value = this.get(key)\n    if (value != null) {\n      return JSON.parse(value)\n    }\n  },\n  remove (key) {\n    sessionStorage.removeItem(key);\n  }\n}\nconst localCache = {\n  set (key, value) {\n    if (!localStorage) {\n      return\n    }\n    if (key != null && value != null) {\n      localStorage.setItem(key, value)\n    }\n  },\n  get (key) {\n    if (!localStorage) {\n      return null\n    }\n    if (key == null) {\n      return null\n    }\n    return localStorage.getItem(key)\n  },\n  setJSON (key, jsonValue) {\n    if (jsonValue != null) {\n      this.set(key, JSON.stringify(jsonValue))\n    }\n  },\n  getJSON (key) {\n    const value = this.get(key)\n    if (value != null) {\n      return JSON.parse(value)\n    }\n  },\n  remove (key) {\n    localStorage.removeItem(key);\n  }\n}\n\nexport default {\n  /**\n   * 会话级缓存\n   */\n  session: sessionCache,\n  /**\n   * 本地缓存\n   */\n  local: localCache\n}\n"
  },
  {
    "path": "ruoyi-ui/src/plugins/download.js",
    "content": "import axios from 'axios'\nimport {Loading, Message} from 'element-ui'\nimport { saveAs } from 'file-saver'\nimport { getToken } from '@/utils/auth'\nimport errorCode from '@/utils/errorCode'\nimport { blobValidate } from \"@/utils/ruoyi\";\n\nconst baseURL = process.env.VUE_APP_BASE_API\nlet downloadLoadingInstance;\n\nexport default {\n  oss(ossId) {\n    var url = baseURL + '/system/oss/download/' + ossId\n    downloadLoadingInstance = Loading.service({ text: \"正在下载数据，请稍候\", spinner: \"el-icon-loading\", background: \"rgba(0, 0, 0, 0.7)\", })\n    axios({\n      method: 'get',\n      url: url,\n      responseType: 'blob',\n      headers: { 'Authorization': 'Bearer ' + getToken() }\n    }).then((res) => {\n      const isBlob = blobValidate(res.data);\n      if (isBlob) {\n        const blob = new Blob([res.data], { type: 'application/octet-stream' })\n        this.saveAs(blob, decodeURIComponent(res.headers['download-filename']))\n      } else {\n        this.printErrMsg(res.data);\n      }\n      downloadLoadingInstance.close();\n    }).catch((r) => {\n      console.error(r)\n      Message.error('下载文件出现错误，请联系管理员！')\n      downloadLoadingInstance.close();\n    })\n  },\n  zip(url, name) {\n    var url = baseURL + url\n    axios({\n      method: 'get',\n      url: url,\n      responseType: 'blob',\n      headers: {\n        'Authorization': 'Bearer ' + getToken(),\n        'datasource': localStorage.getItem(\"dataName\")\n      }\n    }).then((res) => {\n      const isBlob = blobValidate(res.data);\n      if (isBlob) {\n        const blob = new Blob([res.data], { type: 'application/zip' })\n        this.saveAs(blob, name)\n      } else {\n        this.printErrMsg(res.data);\n      }\n    })\n  },\n  saveAs(text, name, opts) {\n    saveAs(text, name, opts);\n  },\n  async printErrMsg(data) {\n    const resText = await data.text();\n    const rspObj = JSON.parse(resText);\n    const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']\n    Message.error(errMsg);\n  }\n}\n\n"
  },
  {
    "path": "ruoyi-ui/src/plugins/index.js",
    "content": "import tab from './tab'\nimport auth from './auth'\nimport cache from './cache'\nimport modal from './modal'\nimport download from './download'\n\nexport default {\n  install(Vue) {\n    // 页签操作\n    Vue.prototype.$tab = tab\n    // 认证对象\n    Vue.prototype.$auth = auth\n    // 缓存对象\n    Vue.prototype.$cache = cache\n    // 模态框对象\n    Vue.prototype.$modal = modal\n    // 下载文件\n    Vue.prototype.$download = download\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/src/plugins/modal.js",
    "content": "import { Message, MessageBox, Notification, Loading } from 'element-ui'\n\nlet loadingInstance;\n\nexport default {\n  // 消息提示\n  msg(content) {\n    Message.info(content)\n  },\n  // 错误消息\n  msgError(content) {\n    Message.error(content)\n  },\n  // 成功消息\n  msgSuccess(content) {\n    Message.success(content)\n  },\n  // 警告消息\n  msgWarning(content) {\n    Message.warning(content)\n  },\n  // 弹出提示\n  alert(content) {\n    MessageBox.alert(content, \"系统提示\")\n  },\n  // 错误提示\n  alertError(content) {\n    MessageBox.alert(content, \"系统提示\", { type: 'error' })\n  },\n  // 成功提示\n  alertSuccess(content) {\n    MessageBox.alert(content, \"系统提示\", { type: 'success' })\n  },\n  // 警告提示\n  alertWarning(content) {\n    MessageBox.alert(content, \"系统提示\", { type: 'warning' })\n  },\n  // 通知提示\n  notify(content) {\n    Notification.info(content)\n  },\n  // 错误通知\n  notifyError(content) {\n    Notification.error(content);\n  },\n  // 成功通知\n  notifySuccess(content) {\n    Notification.success(content)\n  },\n  // 警告通知\n  notifyWarning(content) {\n    Notification.warning(content)\n  },\n  // 确认窗体\n  confirm(content) {\n    return MessageBox.confirm(content, \"系统提示\", {\n      confirmButtonText: '确定',\n      cancelButtonText: '取消',\n      type: \"warning\",\n    })\n  },\n  // 提交内容\n  prompt(content) {\n    return MessageBox.prompt(content, \"系统提示\", {\n      confirmButtonText: '确定',\n      cancelButtonText: '取消',\n      type: \"warning\",\n    })\n  },\n  // 打开遮罩层\n  loading(content) {\n    loadingInstance = Loading.service({\n      lock: true,\n      text: content,\n      spinner: \"el-icon-loading\",\n      background: \"rgba(0, 0, 0, 0.7)\",\n    })\n  },\n  // 关闭遮罩层\n  closeLoading() {\n    loadingInstance.close();\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/src/plugins/tab.js",
    "content": "import store from '@/store'\nimport router from '@/router';\n\nexport default {\n  // 刷新当前tab页签\n  refreshPage(obj) {\n    const { path, query, matched } = router.currentRoute;\n    if (obj === undefined) {\n      matched.forEach((m) => {\n        if (m.components && m.components.default && m.components.default.name) {\n          if (!['Layout', 'ParentView'].includes(m.components.default.name)) {\n            obj = { name: m.components.default.name, path: path, query: query };\n          }\n        }\n      });\n    }\n    return store.dispatch('tagsView/delCachedView', obj).then(() => {\n      const { path, query } = obj\n      router.replace({\n        path: '/redirect' + path,\n        query: query\n      })\n    })\n  },\n  // 关闭当前tab页签，打开新页签\n  closeOpenPage(obj) {\n    store.dispatch(\"tagsView/delView\", router.currentRoute);\n    if (obj !== undefined) {\n      return router.push(obj);\n    }\n  },\n  // 关闭指定tab页签\n  closePage(obj) {\n    if (obj === undefined) {\n      return store.dispatch('tagsView/delView', router.currentRoute).then(({ visitedViews }) => {\n        const latestView = visitedViews.slice(-1)[0]\n        if (latestView) {\n          return router.push(latestView.fullPath)\n        }\n        return router.push('/');\n      });\n    }\n    return store.dispatch('tagsView/delView', obj);\n  },\n  // 关闭所有tab页签\n  closeAllPage() {\n    return store.dispatch('tagsView/delAllViews');\n  },\n  // 关闭左侧tab页签\n  closeLeftPage(obj) {\n    return store.dispatch('tagsView/delLeftTags', obj || router.currentRoute);\n  },\n  // 关闭右侧tab页签\n  closeRightPage(obj) {\n    return store.dispatch('tagsView/delRightTags', obj || router.currentRoute);\n  },\n  // 关闭其他tab页签\n  closeOtherPage(obj) {\n    return store.dispatch('tagsView/delOthersViews', obj || router.currentRoute);\n  },\n  // 添加tab页签\n  openPage(title, url, params) {\n    var obj = { path: url, meta: { title: title } }\n    store.dispatch('tagsView/addView', obj);\n    return router.push({ path: url, query: params });\n  },\n  // 修改tab页签\n  updatePage(obj) {\n    return store.dispatch('tagsView/updateVisitedView', obj);\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/src/router/index.js",
    "content": "import Vue from 'vue'\nimport Router from 'vue-router'\n\nVue.use(Router)\n\n/* Layout */\nimport Layout from '@/layout'\n\n/**\n * Note: 路由配置项\n *\n * hidden: true                     // 当设置 true 的时候该路由不会再侧边栏出现 如401，login等页面，或者如一些编辑页面/edit/1\n * alwaysShow: true                 // 当你一个路由下面的 children 声明的路由大于1个时，自动会变成嵌套的模式--如组件页面\n *                                  // 只有一个时，会将那个子路由当做根路由显示在侧边栏--如引导页面\n *                                  // 若你想不管路由下面的 children 声明的个数都显示你的根路由\n *                                  // 你可以设置 alwaysShow: true，这样它就会忽略之前定义的规则，一直显示根路由\n * redirect: noRedirect             // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击\n * name:'router-name'               // 设定路由的名字，一定要填写不然使用<keep-alive>时会出现各种问题\n * query: '{\"id\": 1, \"name\": \"ry\"}' // 访问路由的默认传递参数\n * roles: ['admin', 'common']       // 访问路由的角色权限\n * permissions: ['a:a:a', 'b:b:b']  // 访问路由的菜单权限\n * meta : {\n    noCache: true                   // 如果设置为true，则不会被 <keep-alive> 缓存(默认 false)\n    title: 'title'                  // 设置该路由在侧边栏和面包屑中展示的名字\n    icon: 'svg-name'                // 设置该路由的图标，对应路径src/assets/icons/svg\n    breadcrumb: false               // 如果设置为false，则不会在breadcrumb面包屑中显示\n    activeMenu: '/system/user'      // 当路由设置了该属性，则会高亮相对应的侧边栏。\n  }\n */\n\n// 公共路由\nexport const constantRoutes = [\n  {\n    path: '/redirect',\n    component: Layout,\n    hidden: true,\n    children: [\n      {\n        path: '/redirect/:path(.*)',\n        component: () => import('@/views/redirect')\n      }\n    ]\n  },\n  {\n    path: '/login',\n    component: () => import('@/views/login'),\n    hidden: true\n  },\n  {\n    path: '/register',\n    component: () => import('@/views/register'),\n    hidden: true\n  },\n  {\n    path: '/404',\n    component: () => import('@/views/error/404'),\n    hidden: true\n  },\n  {\n    path: '/401',\n    component: () => import('@/views/error/401'),\n    hidden: true\n  },\n  {\n    path: '',\n    component: Layout,\n    redirect: 'index',\n    children: [\n      {\n        path: 'index',\n        component: () => import('@/views/index'),\n        name: 'Index',\n        meta: { title: '首页', icon: 'dashboard', affix: true }\n      }\n    ]\n  },\n  {\n    path: '/user',\n    component: Layout,\n    hidden: true,\n    redirect: 'noredirect',\n    children: [\n      {\n        path: 'profile',\n        component: () => import('@/views/system/user/profile/index'),\n        name: 'Profile',\n        meta: { title: '个人中心', icon: 'user' }\n      }\n    ]\n  }\n]\n\n// 动态路由，基于用户权限动态去加载\nexport const dynamicRoutes = [\n  {\n    path: '/system/user-auth',\n    component: Layout,\n    hidden: true,\n    permissions: ['system:user:edit'],\n    children: [\n      {\n        path: 'role/:userId(\\\\d+)',\n        component: () => import('@/views/system/user/authRole'),\n        name: 'AuthRole',\n        meta: { title: '分配角色', activeMenu: '/system/user' }\n      }\n    ]\n  },\n  {\n    path: '/system/role-auth',\n    component: Layout,\n    hidden: true,\n    permissions: ['system:role:edit'],\n    children: [\n      {\n        path: 'user/:roleId(\\\\d+)',\n        component: () => import('@/views/system/role/authUser'),\n        name: 'AuthUser',\n        meta: { title: '分配用户', activeMenu: '/system/role' }\n      }\n    ]\n  },\n  {\n    path: '/system/dict-data',\n    component: Layout,\n    hidden: true,\n    permissions: ['system:dict:list'],\n    children: [\n      {\n        path: 'index/:dictId(\\\\d+)',\n        component: () => import('@/views/system/dict/data'),\n        name: 'Data',\n        meta: { title: '字典数据', activeMenu: '/system/dict' }\n      }\n    ]\n  },\n  {\n    path: '/system/oss-config',\n    component: Layout,\n    hidden: true,\n    permissions: ['system:oss:list'],\n    children: [\n      {\n        path: 'index',\n        component: () => import('@/views/system/oss/config'),\n        name: 'OssConfig',\n        meta: { title: '配置管理', activeMenu: '/system/oss' }\n      }\n    ]\n  },\n  {\n    path: '/tool/gen-edit',\n    component: Layout,\n    hidden: true,\n    permissions: ['tool:gen:edit'],\n    children: [\n      {\n        path: 'index/:tableId(\\\\d+)',\n        component: () => import('@/views/tool/gen/editTable'),\n        name: 'GenEdit',\n        meta: { title: '修改生成配置', activeMenu: '/tool/gen' }\n      }\n    ]\n  }\n]\n\n// 防止连续点击多次路由报错\nlet routerPush = Router.prototype.push;\nlet routerReplace = Router.prototype.replace;\n// push\nRouter.prototype.push = function push(location) {\n  return routerPush.call(this, location).catch(err => err)\n}\n// replace\nRouter.prototype.replace = function push(location) {\n  return routerReplace.call(this, location).catch(err => err)\n}\n\nexport default new Router({\n  base: process.env.VUE_APP_CONTEXT_PATH,\n  mode: 'history', // 去掉url中的#\n  scrollBehavior: () => ({ y: 0 }),\n  routes: constantRoutes\n})\n"
  },
  {
    "path": "ruoyi-ui/src/settings.js",
    "content": "module.exports = {\n  /**\n   * 侧边栏主题 深色主题theme-dark，浅色主题theme-light\n   */\n  sideTheme: 'theme-dark',\n\n  /**\n   * 是否系统布局配置\n   */\n  showSettings: false,\n\n  /**\n   * 是否显示顶部导航\n   */\n  topNav: false,\n\n  /**\n   * 是否显示 tagsView\n   */\n  tagsView: true,\n\n  /**\n   * 是否固定头部\n   */\n  fixedHeader: false,\n\n  /**\n   * 是否显示logo\n   */\n  sidebarLogo: true,\n\n  /**\n   * 是否显示动态标题\n   */\n  dynamicTitle: false,\n\n  /**\n   * @type {string | array} 'production' | ['production', 'development']\n   * @description Need show err logs component.\n   * The default is only used in the production env\n   * If you want to also use it in dev, you can pass ['production', 'development']\n   */\n  errorLog: 'production'\n}\n"
  },
  {
    "path": "ruoyi-ui/src/store/getters.js",
    "content": "const getters = {\n  sidebar: state => state.app.sidebar,\n  size: state => state.app.size,\n  device: state => state.app.device,\n  dict: state => state.dict.dict,\n  visitedViews: state => state.tagsView.visitedViews,\n  cachedViews: state => state.tagsView.cachedViews,\n  token: state => state.user.token,\n  avatar: state => state.user.avatar,\n  name: state => state.user.name,\n  introduction: state => state.user.introduction,\n  roles: state => state.user.roles,\n  permissions: state => state.user.permissions,\n  permission_routes: state => state.permission.routes,\n  topbarRouters:state => state.permission.topbarRouters,\n  defaultRoutes:state => state.permission.defaultRoutes,\n  sidebarRouters:state => state.permission.sidebarRouters,\n}\nexport default getters\n"
  },
  {
    "path": "ruoyi-ui/src/store/index.js",
    "content": "import Vue from 'vue'\nimport Vuex from 'vuex'\nimport app from './modules/app'\nimport dict from './modules/dict'\nimport user from './modules/user'\nimport tagsView from './modules/tagsView'\nimport permission from './modules/permission'\nimport settings from './modules/settings'\nimport getters from './getters'\n\nVue.use(Vuex)\n\nconst store = new Vuex.Store({\n  modules: {\n    app,\n    dict,\n    user,\n    tagsView,\n    permission,\n    settings\n  },\n  getters\n})\n\nexport default store\n"
  },
  {
    "path": "ruoyi-ui/src/store/modules/app.js",
    "content": "import Cookies from 'js-cookie'\n\nconst state = {\n  sidebar: {\n    opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,\n    withoutAnimation: false,\n    hide: false\n  },\n  device: 'desktop',\n  size: Cookies.get('size') || 'medium'\n}\n\nconst mutations = {\n  TOGGLE_SIDEBAR: state => {\n    if (state.sidebar.hide) {\n      return false;\n    }\n    state.sidebar.opened = !state.sidebar.opened\n    state.sidebar.withoutAnimation = false\n    if (state.sidebar.opened) {\n      Cookies.set('sidebarStatus', 1)\n    } else {\n      Cookies.set('sidebarStatus', 0)\n    }\n  },\n  CLOSE_SIDEBAR: (state, withoutAnimation) => {\n    Cookies.set('sidebarStatus', 0)\n    state.sidebar.opened = false\n    state.sidebar.withoutAnimation = withoutAnimation\n  },\n  TOGGLE_DEVICE: (state, device) => {\n    state.device = device\n  },\n  SET_SIZE: (state, size) => {\n    state.size = size\n    Cookies.set('size', size)\n  },\n  SET_SIDEBAR_HIDE: (state, status) => {\n    state.sidebar.hide = status\n  }\n}\n\nconst actions = {\n  toggleSideBar({ commit }) {\n    commit('TOGGLE_SIDEBAR')\n  },\n  closeSideBar({ commit }, { withoutAnimation }) {\n    commit('CLOSE_SIDEBAR', withoutAnimation)\n  },\n  toggleDevice({ commit }, device) {\n    commit('TOGGLE_DEVICE', device)\n  },\n  setSize({ commit }, size) {\n    commit('SET_SIZE', size)\n  },\n  toggleSideBarHide({ commit }, status) {\n    commit('SET_SIDEBAR_HIDE', status)\n  }\n}\n\nexport default {\n  namespaced: true,\n  state,\n  mutations,\n  actions\n}\n"
  },
  {
    "path": "ruoyi-ui/src/store/modules/dict.js",
    "content": "const state = {\n  dict: new Array()\n}\nconst mutations = {\n  SET_DICT: (state, { key, value }) => {\n    if (key !== null && key !== \"\") {\n      state.dict.push({\n        key: key,\n        value: value\n      })\n    }\n  },\n  REMOVE_DICT: (state, key) => {\n    try {\n      for (let i = 0; i < state.dict.length; i++) {\n        if (state.dict[i].key == key) {\n          state.dict.splice(i, i)\n          return true\n        }\n      }\n    } catch (e) {\n    }\n  },\n  CLEAN_DICT: (state) => {\n    state.dict = new Array()\n  }\n}\n\nconst actions = {\n  // 设置字典\n  setDict({ commit }, data) {\n    commit('SET_DICT', data)\n  },\n  // 删除字典\n  removeDict({ commit }, key) {\n    commit('REMOVE_DICT', key)\n  },\n  // 清空字典\n  cleanDict({ commit }) {\n    commit('CLEAN_DICT')\n  }\n}\n\nexport default {\n  namespaced: true,\n  state,\n  mutations,\n  actions\n}\n\n"
  },
  {
    "path": "ruoyi-ui/src/store/modules/permission.js",
    "content": "import auth from '@/plugins/auth'\nimport router, { constantRoutes, dynamicRoutes } from '@/router'\nimport { getRouters } from '@/api/menu'\nimport Layout from '@/layout/index'\nimport ParentView from '@/components/ParentView'\nimport InnerLink from '@/layout/components/InnerLink'\n\nconst permission = {\n  state: {\n    routes: [],\n    addRoutes: [],\n    defaultRoutes: [],\n    topbarRouters: [],\n    sidebarRouters: []\n  },\n  mutations: {\n    SET_ROUTES: (state, routes) => {\n      state.addRoutes = routes\n      state.routes = constantRoutes.concat(routes)\n    },\n    SET_DEFAULT_ROUTES: (state, routes) => {\n      state.defaultRoutes = constantRoutes.concat(routes)\n    },\n    SET_TOPBAR_ROUTES: (state, routes) => {\n      state.topbarRouters = routes\n    },\n    SET_SIDEBAR_ROUTERS: (state, routes) => {\n      state.sidebarRouters = routes\n    },\n  },\n  actions: {\n    // 生成路由\n    GenerateRoutes({ commit }) {\n      return new Promise(resolve => {\n        // 向后端请求路由数据\n        getRouters().then(res => {\n          const sdata = JSON.parse(JSON.stringify(res.data))\n          const rdata = JSON.parse(JSON.stringify(res.data))\n          const sidebarRoutes = filterAsyncRouter(sdata)\n          const rewriteRoutes = filterAsyncRouter(rdata, false, true)\n          const asyncRoutes = filterDynamicRoutes(dynamicRoutes);\n          rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })\n          router.addRoutes(asyncRoutes);\n          commit('SET_ROUTES', rewriteRoutes)\n          commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))\n          commit('SET_DEFAULT_ROUTES', sidebarRoutes)\n          commit('SET_TOPBAR_ROUTES', sidebarRoutes)\n          resolve(rewriteRoutes)\n        })\n      })\n    }\n  }\n}\n\n// 遍历后台传来的路由字符串，转换为组件对象\nfunction filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {\n  return asyncRouterMap.filter(route => {\n    if (type && route.children) {\n      route.children = filterChildren(route.children)\n    }\n    if (route.component) {\n      // Layout ParentView 组件特殊处理\n      if (route.component === 'Layout') {\n        route.component = Layout\n      } else if (route.component === 'ParentView') {\n        route.component = ParentView\n      } else if (route.component === 'InnerLink') {\n        route.component = InnerLink\n      } else {\n        route.component = loadView(route.component)\n      }\n    }\n    if (route.children != null && route.children && route.children.length) {\n      route.children = filterAsyncRouter(route.children, route, type)\n    } else {\n      delete route['children']\n      delete route['redirect']\n    }\n    return true\n  })\n}\n\nfunction filterChildren(childrenMap, lastRouter = false) {\n  var children = []\n  childrenMap.forEach((el, index) => {\n    if (el.children && el.children.length) {\n      if (el.component === 'ParentView' && !lastRouter) {\n        el.children.forEach(c => {\n          c.path = el.path + '/' + c.path\n          if (c.children && c.children.length) {\n            children = children.concat(filterChildren(c.children, c))\n            return\n          }\n          children.push(c)\n        })\n        return\n      }\n    }\n    if (lastRouter) {\n      el.path = lastRouter.path + '/' + el.path\n    }\n    children = children.concat(el)\n  })\n  return children\n}\n\n// 动态路由遍历，验证是否具备权限\nexport function filterDynamicRoutes(routes) {\n  const res = []\n  routes.forEach(route => {\n    if (route.permissions) {\n      if (auth.hasPermiOr(route.permissions)) {\n        res.push(route)\n      }\n    } else if (route.roles) {\n      if (auth.hasRoleOr(route.roles)) {\n        res.push(route)\n      }\n    }\n  })\n  return res\n}\n\nexport const loadView = (view) => {\n  if (process.env.NODE_ENV === 'development') {\n    return (resolve) => require([`@/views/${view}`], resolve)\n  } else {\n    // 使用 import 实现生产环境的路由懒加载\n    return () => import(`@/views/${view}`)\n  }\n}\n\nexport default permission\n"
  },
  {
    "path": "ruoyi-ui/src/store/modules/settings.js",
    "content": "import defaultSettings from '@/settings'\n\nconst { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle } = defaultSettings\n\nconst storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || ''\nconst state = {\n  title: '',\n  theme: storageSetting.theme || '#409EFF',\n  sideTheme: storageSetting.sideTheme || sideTheme,\n  showSettings: showSettings,\n  topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav,\n  tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView,\n  fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader,\n  sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo,\n  dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle\n}\nconst mutations = {\n  CHANGE_SETTING: (state, { key, value }) => {\n    if (state.hasOwnProperty(key)) {\n      state[key] = value\n    }\n  }\n}\n\nconst actions = {\n  // 修改布局设置\n  changeSetting({ commit }, data) {\n    commit('CHANGE_SETTING', data)\n  },\n  // 设置网页标题\n  setTitle({ commit }, title) {\n    state.title = title\n  }\n}\n\nexport default {\n  namespaced: true,\n  state,\n  mutations,\n  actions\n}\n\n"
  },
  {
    "path": "ruoyi-ui/src/store/modules/tagsView.js",
    "content": "const state = {\n  visitedViews: [],\n  cachedViews: [],\n  iframeViews: []\n}\n\nconst mutations = {\n  ADD_IFRAME_VIEW: (state, view) => {\n    if (state.iframeViews.some(v => v.path === view.path)) return\n    state.iframeViews.push(\n      Object.assign({}, view, {\n        title: view.meta.title || 'no-name'\n      })\n    )\n  },\n  ADD_VISITED_VIEW: (state, view) => {\n    if (state.visitedViews.some(v => v.path === view.path)) return\n    state.visitedViews.push(\n      Object.assign({}, view, {\n        title: view.meta.title || 'no-name'\n      })\n    )\n  },\n  ADD_CACHED_VIEW: (state, view) => {\n    if (state.cachedViews.includes(view.name)) return\n    if (view.meta && !view.meta.noCache) {\n      state.cachedViews.push(view.name)\n    }\n  },\n  DEL_VISITED_VIEW: (state, view) => {\n    for (const [i, v] of state.visitedViews.entries()) {\n      if (v.path === view.path) {\n        state.visitedViews.splice(i, 1)\n        break\n      }\n    }\n    state.iframeViews = state.iframeViews.filter(item => item.path !== view.path)\n  },\n  DEL_IFRAME_VIEW: (state, view) => {\n    state.iframeViews = state.iframeViews.filter(item => item.path !== view.path)\n  },\n  DEL_CACHED_VIEW: (state, view) => {\n    const index = state.cachedViews.indexOf(view.name)\n    index > -1 && state.cachedViews.splice(index, 1)\n  },\n\n  DEL_OTHERS_VISITED_VIEWS: (state, view) => {\n    state.visitedViews = state.visitedViews.filter(v => {\n      return v.meta.affix || v.path === view.path\n    })\n    state.iframeViews = state.iframeViews.filter(item => item.path === view.path)\n  },\n  DEL_OTHERS_CACHED_VIEWS: (state, view) => {\n    const index = state.cachedViews.indexOf(view.name)\n    if (index > -1) {\n      state.cachedViews = state.cachedViews.slice(index, index + 1)\n    } else {\n      state.cachedViews = []\n    }\n  },\n  DEL_ALL_VISITED_VIEWS: state => {\n    // keep affix tags\n    const affixTags = state.visitedViews.filter(tag => tag.meta.affix)\n    state.visitedViews = affixTags\n    state.iframeViews = []\n  },\n  DEL_ALL_CACHED_VIEWS: state => {\n    state.cachedViews = []\n  },\n  UPDATE_VISITED_VIEW: (state, view) => {\n    for (let v of state.visitedViews) {\n      if (v.path === view.path) {\n        v = Object.assign(v, view)\n        break\n      }\n    }\n  },\n  DEL_RIGHT_VIEWS: (state, view) => {\n    const index = state.visitedViews.findIndex(v => v.path === view.path)\n    if (index === -1) {\n      return\n    }\n    state.visitedViews = state.visitedViews.filter((item, idx) => {\n      if (idx <= index || (item.meta && item.meta.affix)) {\n        return true\n      }\n      const i = state.cachedViews.indexOf(item.name)\n      if (i > -1) {\n        state.cachedViews.splice(i, 1)\n      }\n      if(item.meta.link) {\n        const fi = state.iframeViews.findIndex(v => v.path === item.path)\n        state.iframeViews.splice(fi, 1)\n      }\n      return false\n    })\n  },\n  DEL_LEFT_VIEWS: (state, view) => {\n    const index = state.visitedViews.findIndex(v => v.path === view.path)\n    if (index === -1) {\n      return\n    }\n    state.visitedViews = state.visitedViews.filter((item, idx) => {\n      if (idx >= index || (item.meta && item.meta.affix)) {\n        return true\n      }\n      const i = state.cachedViews.indexOf(item.name)\n      if (i > -1) {\n        state.cachedViews.splice(i, 1)\n      }\n      if(item.meta.link) {\n        const fi = state.iframeViews.findIndex(v => v.path === item.path)\n        state.iframeViews.splice(fi, 1)\n      }\n      return false\n    })\n  }\n}\n\nconst actions = {\n  addView({ dispatch }, view) {\n    dispatch('addVisitedView', view)\n    dispatch('addCachedView', view)\n  },\n  addIframeView({ commit }, view) {\n    commit('ADD_IFRAME_VIEW', view)\n  },\n  addVisitedView({ commit }, view) {\n    commit('ADD_VISITED_VIEW', view)\n  },\n  addCachedView({ commit }, view) {\n    commit('ADD_CACHED_VIEW', view)\n  },\n  delView({ dispatch, state }, view) {\n    return new Promise(resolve => {\n      dispatch('delVisitedView', view)\n      dispatch('delCachedView', view)\n      resolve({\n        visitedViews: [...state.visitedViews],\n        cachedViews: [...state.cachedViews]\n      })\n    })\n  },\n  delVisitedView({ commit, state }, view) {\n    return new Promise(resolve => {\n      commit('DEL_VISITED_VIEW', view)\n      resolve([...state.visitedViews])\n    })\n  },\n  delIframeView({ commit, state }, view) {\n    return new Promise(resolve => {\n      commit('DEL_IFRAME_VIEW', view)\n      resolve([...state.iframeViews])\n    })\n  },\n  delCachedView({ commit, state }, view) {\n    return new Promise(resolve => {\n      commit('DEL_CACHED_VIEW', view)\n      resolve([...state.cachedViews])\n    })\n  },\n  delOthersViews({ dispatch, state }, view) {\n    return new Promise(resolve => {\n      dispatch('delOthersVisitedViews', view)\n      dispatch('delOthersCachedViews', view)\n      resolve({\n        visitedViews: [...state.visitedViews],\n        cachedViews: [...state.cachedViews]\n      })\n    })\n  },\n  delOthersVisitedViews({ commit, state }, view) {\n    return new Promise(resolve => {\n      commit('DEL_OTHERS_VISITED_VIEWS', view)\n      resolve([...state.visitedViews])\n    })\n  },\n  delOthersCachedViews({ commit, state }, view) {\n    return new Promise(resolve => {\n      commit('DEL_OTHERS_CACHED_VIEWS', view)\n      resolve([...state.cachedViews])\n    })\n  },\n  delAllViews({ dispatch, state }, view) {\n    return new Promise(resolve => {\n      dispatch('delAllVisitedViews', view)\n      dispatch('delAllCachedViews', view)\n      resolve({\n        visitedViews: [...state.visitedViews],\n        cachedViews: [...state.cachedViews]\n      })\n    })\n  },\n  delAllVisitedViews({ commit, state }) {\n    return new Promise(resolve => {\n      commit('DEL_ALL_VISITED_VIEWS')\n      resolve([...state.visitedViews])\n    })\n  },\n  delAllCachedViews({ commit, state }) {\n    return new Promise(resolve => {\n      commit('DEL_ALL_CACHED_VIEWS')\n      resolve([...state.cachedViews])\n    })\n  },\n  updateVisitedView({ commit }, view) {\n    commit('UPDATE_VISITED_VIEW', view)\n  },\n  delRightTags({ commit }, view) {\n    return new Promise(resolve => {\n      commit('DEL_RIGHT_VIEWS', view)\n      resolve([...state.visitedViews])\n    })\n  },\n  delLeftTags({ commit }, view) {\n    return new Promise(resolve => {\n      commit('DEL_LEFT_VIEWS', view)\n      resolve([...state.visitedViews])\n    })\n  },\n}\n\nexport default {\n  namespaced: true,\n  state,\n  mutations,\n  actions\n}\n"
  },
  {
    "path": "ruoyi-ui/src/store/modules/user.js",
    "content": "import { login, logout, getInfo } from '@/api/login'\nimport { getToken, setToken, removeToken } from '@/utils/auth'\n\nconst user = {\n  state: {\n    token: getToken(),\n    name: '',\n    avatar: '',\n    roles: [],\n    permissions: []\n  },\n\n  mutations: {\n    SET_TOKEN: (state, token) => {\n      state.token = token\n    },\n    SET_NAME: (state, name) => {\n      state.name = name\n    },\n    SET_AVATAR: (state, avatar) => {\n      state.avatar = avatar\n    },\n    SET_ROLES: (state, roles) => {\n      state.roles = roles\n    },\n    SET_PERMISSIONS: (state, permissions) => {\n      state.permissions = permissions\n    }\n  },\n\n  actions: {\n    // 登录\n    Login({ commit }, userInfo) {\n      const username = userInfo.username.trim()\n      const password = userInfo.password\n      const code = userInfo.code\n      const uuid = userInfo.uuid\n      return new Promise((resolve, reject) => {\n        login(username, password, code, uuid).then(res => {\n          setToken(res.data.token)\n          commit('SET_TOKEN', res.data.token)\n          resolve()\n        }).catch(error => {\n          reject(error)\n        })\n      })\n    },\n\n    // 获取用户信息\n    GetInfo({ commit, state }) {\n      return new Promise((resolve, reject) => {\n        getInfo().then(res => {\n          const user = res.data.user\n          const avatar = (user.avatar == \"\" || user.avatar == null) ? require(\"@/assets/images/1.png\") : user.avatar;\n          if (res.data.roles && res.data.roles.length > 0) { // 验证返回的roles是否是一个非空数组\n            commit('SET_ROLES', res.data.roles)\n            commit('SET_PERMISSIONS', res.data.permissions)\n          } else {\n            commit('SET_ROLES', ['ROLE_DEFAULT'])\n          }\n          commit('SET_NAME', user.userName)\n          commit('SET_AVATAR', avatar)\n          resolve(res)\n        }).catch(error => {\n          reject(error)\n        })\n      })\n    },\n\n    // 退出系统\n    LogOut({ commit, state }) {\n      return new Promise((resolve, reject) => {\n        logout(state.token).then(() => {\n          commit('SET_TOKEN', '')\n          commit('SET_ROLES', [])\n          commit('SET_PERMISSIONS', [])\n          removeToken()\n          resolve()\n        }).catch(error => {\n          reject(error)\n        })\n      })\n    },\n\n    // 前端 登出\n    FedLogOut({ commit }) {\n      return new Promise(resolve => {\n        commit('SET_TOKEN', '')\n        removeToken()\n        resolve()\n      })\n    }\n  }\n}\n\nexport default user\n"
  },
  {
    "path": "ruoyi-ui/src/utils/auth.js",
    "content": "import Cookies from 'js-cookie'\n\nconst TokenKey = 'Admin-Token'\n\nexport function getToken() {\n  return Cookies.get(TokenKey)\n}\n\nexport function setToken(token) {\n  return Cookies.set(TokenKey, token)\n}\n\nexport function removeToken() {\n  return Cookies.remove(TokenKey)\n}\n"
  },
  {
    "path": "ruoyi-ui/src/utils/dict/Dict.js",
    "content": "import Vue from 'vue'\nimport { mergeRecursive } from \"@/utils/ruoyi\";\nimport DictMeta from './DictMeta'\nimport DictData from './DictData'\n\nconst DEFAULT_DICT_OPTIONS = {\n  types: [],\n}\n\n/**\n * @classdesc 字典\n * @property {Object} label 标签对象，内部属性名为字典类型名称\n * @property {Object} dict 字段数组，内部属性名为字典类型名称\n * @property {Array.<DictMeta>} _dictMetas 字典元数据数组\n */\nexport default class Dict {\n  constructor() {\n    this.owner = null\n    this.label = {}\n    this.type = {}\n  }\n\n  init(options) {\n    if (options instanceof Array) {\n      options = { types: options }\n    }\n    const opts = mergeRecursive(DEFAULT_DICT_OPTIONS, options)\n    if (opts.types === undefined) {\n      throw new Error('need dict types')\n    }\n    const ps = []\n    this._dictMetas = opts.types.map(t => DictMeta.parse(t))\n    this._dictMetas.forEach(dictMeta => {\n      const type = dictMeta.type\n      Vue.set(this.label, type, {})\n      Vue.set(this.type, type, [])\n      if (dictMeta.lazy) {\n        return\n      }\n      ps.push(loadDict(this, dictMeta))\n    })\n    return Promise.all(ps)\n  }\n\n  /**\n   * 重新加载字典\n   * @param {String} type 字典类型\n   */\n  reloadDict(type) {\n    const dictMeta = this._dictMetas.find(e => e.type === type)\n    if (dictMeta === undefined) {\n      return Promise.reject(`the dict meta of ${type} was not found`)\n    }\n    return loadDict(this, dictMeta)\n  }\n}\n\n/**\n * 加载字典\n * @param {Dict} dict 字典\n * @param {DictMeta} dictMeta 字典元数据\n * @returns {Promise}\n */\nfunction loadDict(dict, dictMeta) {\n  return dictMeta.request(dictMeta)\n    .then(response => {\n      const type = dictMeta.type\n      let dicts = dictMeta.responseConverter(response, dictMeta)\n      if (!(dicts instanceof Array)) {\n        console.error('the return of responseConverter must be Array.<DictData>')\n        dicts = []\n      } else if (dicts.filter(d => d instanceof DictData).length !== dicts.length) {\n        console.error('the type of elements in dicts must be DictData')\n        dicts = []\n      }\n      dict.type[type].splice(0, Number.MAX_SAFE_INTEGER, ...dicts)\n      dicts.forEach(d => {\n        Vue.set(dict.label[type], d.value, d.label)\n      })\n      return dicts\n    })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/utils/dict/DictConverter.js",
    "content": "import DictOptions from './DictOptions'\nimport DictData from './DictData'\n\nexport default function(dict, dictMeta) {\n  const label = determineDictField(dict, dictMeta.labelField, ...DictOptions.DEFAULT_LABEL_FIELDS)\n  const value = determineDictField(dict, dictMeta.valueField, ...DictOptions.DEFAULT_VALUE_FIELDS)\n  return new DictData(dict[label], dict[value], dict)\n}\n\n/**\n * 确定字典字段\n * @param {DictData} dict\n * @param  {...String} fields\n */\nfunction determineDictField(dict, ...fields) {\n  return fields.find(f => Object.prototype.hasOwnProperty.call(dict, f))\n}\n"
  },
  {
    "path": "ruoyi-ui/src/utils/dict/DictData.js",
    "content": "/**\n * @classdesc 字典数据\n * @property {String} label 标签\n * @property {*} value 标签\n * @property {Object} raw 原始数据\n */\nexport default class DictData {\n  constructor(label, value, raw) {\n    this.label = label\n    this.value = value\n    this.raw = raw\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/src/utils/dict/DictMeta.js",
    "content": "import { mergeRecursive } from \"@/utils/ruoyi\";\nimport DictOptions from './DictOptions'\n\n/**\n * @classdesc 字典元数据\n * @property {String} type 类型\n * @property {Function} request 请求\n * @property {String} label 标签字段\n * @property {String} value 值字段\n */\nexport default class DictMeta {\n  constructor(options) {\n    this.type = options.type\n    this.request = options.request\n    this.responseConverter = options.responseConverter\n    this.labelField = options.labelField\n    this.valueField = options.valueField\n    this.lazy = options.lazy === true\n  }\n}\n\n\n/**\n * 解析字典元数据\n * @param {Object} options\n * @returns {DictMeta}\n */\nDictMeta.parse= function(options) {\n  let opts = null\n  if (typeof options === 'string') {\n    opts = DictOptions.metas[options] || {}\n    opts.type = options\n  } else if (typeof options === 'object') {\n    opts = options\n  }\n  opts = mergeRecursive(DictOptions.metas['*'], opts)\n  return new DictMeta(opts)\n}\n"
  },
  {
    "path": "ruoyi-ui/src/utils/dict/DictOptions.js",
    "content": "import { mergeRecursive } from \"@/utils/ruoyi\";\nimport dictConverter from './DictConverter'\n\nexport const options = {\n  metas: {\n    '*': {\n      /**\n       * 字典请求，方法签名为function(dictMeta: DictMeta): Promise\n       */\n      request: (dictMeta) => {\n        console.log(`load dict ${dictMeta.type}`)\n        return Promise.resolve([])\n      },\n      /**\n       * 字典响应数据转换器，方法签名为function(response: Object, dictMeta: DictMeta): DictData\n       */\n      responseConverter,\n      labelField: 'label',\n      valueField: 'value',\n    },\n  },\n  /**\n   * 默认标签字段\n   */\n  DEFAULT_LABEL_FIELDS: ['label', 'name', 'title'],\n  /**\n   * 默认值字段\n   */\n  DEFAULT_VALUE_FIELDS: ['value', 'id', 'uid', 'key'],\n}\n\n/**\n * 映射字典\n * @param {Object} response 字典数据\n * @param {DictMeta} dictMeta 字典元数据\n * @returns {DictData}\n */\nfunction responseConverter(response, dictMeta) {\n  const dicts = response.content instanceof Array ? response.content : response\n  if (dicts === undefined) {\n    console.warn(`no dict data of \"${dictMeta.type}\" found in the response`)\n    return []\n  }\n  return dicts.map(d => dictConverter(d, dictMeta))\n}\n\nexport function mergeOptions(src) {\n  mergeRecursive(options, src)\n}\n\nexport default options\n"
  },
  {
    "path": "ruoyi-ui/src/utils/dict/index.js",
    "content": "import Dict from './Dict'\nimport { mergeOptions } from './DictOptions'\n\nexport default function(Vue, options) {\n  mergeOptions(options)\n  Vue.mixin({\n    data() {\n      if (this.$options === undefined || this.$options.dicts === undefined || this.$options.dicts === null) {\n        return {}\n      }\n      const dict = new Dict()\n      dict.owner = this\n      return {\n        dict\n      }\n    },\n    created() {\n      if (!(this.dict instanceof Dict)) {\n        return\n      }\n      options.onCreated && options.onCreated(this.dict)\n      this.dict.init(this.$options.dicts).then(() => {\n        options.onReady && options.onReady(this.dict)\n        this.$nextTick(() => {\n          this.$emit('dictReady', this.dict)\n          if (this.$options.methods && this.$options.methods.onDictReady instanceof Function) {\n            this.$options.methods.onDictReady.call(this, this.dict)\n          }\n        })\n      })\n    },\n  })\n}\n"
  },
  {
    "path": "ruoyi-ui/src/utils/errorCode.js",
    "content": "export default {\n  '401': '认证失败，无法访问系统资源',\n  '403': '当前操作没有权限',\n  '404': '访问资源不存在',\n  'default': '系统未知错误，请反馈给管理员'\n}\n"
  },
  {
    "path": "ruoyi-ui/src/utils/generator/config.js",
    "content": "export const formConf = {\n  formRef: 'elForm',\n  formModel: 'formData',\n  size: 'medium',\n  labelPosition: 'right',\n  labelWidth: 100,\n  formRules: 'rules',\n  gutter: 15,\n  disabled: false,\n  span: 24,\n  formBtns: true\n}\n\nexport const inputComponents = [\n  {\n    label: '单行文本',\n    tag: 'el-input',\n    tagIcon: 'input',\n    placeholder: '请输入',\n    defaultValue: undefined,\n    span: 24,\n    labelWidth: null,\n    style: { width: '100%' },\n    clearable: true,\n    prepend: '',\n    append: '',\n    'prefix-icon': '',\n    'suffix-icon': '',\n    maxlength: null,\n    'show-word-limit': false,\n    readonly: false,\n    disabled: false,\n    required: true,\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/input'\n  },\n  {\n    label: '多行文本',\n    tag: 'el-input',\n    tagIcon: 'textarea',\n    type: 'textarea',\n    placeholder: '请输入',\n    defaultValue: undefined,\n    span: 24,\n    labelWidth: null,\n    autosize: {\n      minRows: 4,\n      maxRows: 4\n    },\n    style: { width: '100%' },\n    maxlength: null,\n    'show-word-limit': false,\n    readonly: false,\n    disabled: false,\n    required: true,\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/input'\n  },\n  {\n    label: '密码',\n    tag: 'el-input',\n    tagIcon: 'password',\n    placeholder: '请输入',\n    defaultValue: undefined,\n    span: 24,\n    'show-password': true,\n    labelWidth: null,\n    style: { width: '100%' },\n    clearable: true,\n    prepend: '',\n    append: '',\n    'prefix-icon': '',\n    'suffix-icon': '',\n    maxlength: null,\n    'show-word-limit': false,\n    readonly: false,\n    disabled: false,\n    required: true,\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/input'\n  },\n  {\n    label: '计数器',\n    tag: 'el-input-number',\n    tagIcon: 'number',\n    placeholder: '',\n    defaultValue: undefined,\n    span: 24,\n    labelWidth: null,\n    min: undefined,\n    max: undefined,\n    step: undefined,\n    'step-strictly': false,\n    precision: undefined,\n    'controls-position': '',\n    disabled: false,\n    required: true,\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/input-number'\n  }\n]\n\nexport const selectComponents = [\n  {\n    label: '下拉选择',\n    tag: 'el-select',\n    tagIcon: 'select',\n    placeholder: '请选择',\n    defaultValue: undefined,\n    span: 24,\n    labelWidth: null,\n    style: { width: '100%' },\n    clearable: true,\n    disabled: false,\n    required: true,\n    filterable: false,\n    multiple: false,\n    options: [{\n      label: '选项一',\n      value: 1\n    }, {\n      label: '选项二',\n      value: 2\n    }],\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/select'\n  },\n  {\n    label: '级联选择',\n    tag: 'el-cascader',\n    tagIcon: 'cascader',\n    placeholder: '请选择',\n    defaultValue: [],\n    span: 24,\n    labelWidth: null,\n    style: { width: '100%' },\n    props: {\n      props: {\n        multiple: false\n      }\n    },\n    'show-all-levels': true,\n    disabled: false,\n    clearable: true,\n    filterable: false,\n    required: true,\n    options: [{\n      id: 1,\n      value: 1,\n      label: '选项1',\n      children: [{\n        id: 2,\n        value: 2,\n        label: '选项1-1'\n      }]\n    }],\n    dataType: 'dynamic',\n    labelKey: 'label',\n    valueKey: 'value',\n    childrenKey: 'children',\n    separator: '/',\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/cascader'\n  },\n  {\n    label: '单选框组',\n    tag: 'el-radio-group',\n    tagIcon: 'radio',\n    defaultValue: undefined,\n    span: 24,\n    labelWidth: null,\n    style: {},\n    optionType: 'default',\n    border: false,\n    size: 'medium',\n    disabled: false,\n    required: true,\n    options: [{\n      label: '选项一',\n      value: 1\n    }, {\n      label: '选项二',\n      value: 2\n    }],\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/radio'\n  },\n  {\n    label: '多选框组',\n    tag: 'el-checkbox-group',\n    tagIcon: 'checkbox',\n    defaultValue: [],\n    span: 24,\n    labelWidth: null,\n    style: {},\n    optionType: 'default',\n    border: false,\n    size: 'medium',\n    disabled: false,\n    required: true,\n    options: [{\n      label: '选项一',\n      value: 1\n    }, {\n      label: '选项二',\n      value: 2\n    }],\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/checkbox'\n  },\n  {\n    label: '开关',\n    tag: 'el-switch',\n    tagIcon: 'switch',\n    defaultValue: false,\n    span: 24,\n    labelWidth: null,\n    style: {},\n    disabled: false,\n    required: true,\n    'active-text': '',\n    'inactive-text': '',\n    'active-color': null,\n    'inactive-color': null,\n    'active-value': true,\n    'inactive-value': false,\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/switch'\n  },\n  {\n    label: '滑块',\n    tag: 'el-slider',\n    tagIcon: 'slider',\n    defaultValue: null,\n    span: 24,\n    labelWidth: null,\n    disabled: false,\n    required: true,\n    min: 0,\n    max: 100,\n    step: 1,\n    'show-stops': false,\n    range: false,\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/slider'\n  },\n  {\n    label: '时间选择',\n    tag: 'el-time-picker',\n    tagIcon: 'time',\n    placeholder: '请选择',\n    defaultValue: null,\n    span: 24,\n    labelWidth: null,\n    style: { width: '100%' },\n    disabled: false,\n    clearable: true,\n    required: true,\n    'picker-options': {\n      selectableRange: '00:00:00-23:59:59'\n    },\n    format: 'HH:mm:ss',\n    'value-format': 'HH:mm:ss',\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/time-picker'\n  },\n  {\n    label: '时间范围',\n    tag: 'el-time-picker',\n    tagIcon: 'time-range',\n    defaultValue: null,\n    span: 24,\n    labelWidth: null,\n    style: { width: '100%' },\n    disabled: false,\n    clearable: true,\n    required: true,\n    'is-range': true,\n    'range-separator': '至',\n    'start-placeholder': '开始时间',\n    'end-placeholder': '结束时间',\n    format: 'HH:mm:ss',\n    'value-format': 'HH:mm:ss',\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/time-picker'\n  },\n  {\n    label: '日期选择',\n    tag: 'el-date-picker',\n    tagIcon: 'date',\n    placeholder: '请选择',\n    defaultValue: null,\n    type: 'date',\n    span: 24,\n    labelWidth: null,\n    style: { width: '100%' },\n    disabled: false,\n    clearable: true,\n    required: true,\n    format: 'yyyy-MM-dd',\n    'value-format': 'yyyy-MM-dd',\n    readonly: false,\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/date-picker'\n  },\n  {\n    label: '日期范围',\n    tag: 'el-date-picker',\n    tagIcon: 'date-range',\n    defaultValue: null,\n    span: 24,\n    labelWidth: null,\n    style: { width: '100%' },\n    type: 'daterange',\n    'range-separator': '至',\n    'start-placeholder': '开始日期',\n    'end-placeholder': '结束日期',\n    disabled: false,\n    clearable: true,\n    required: true,\n    format: 'yyyy-MM-dd',\n    'value-format': 'yyyy-MM-dd',\n    readonly: false,\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/date-picker'\n  },\n  {\n    label: '评分',\n    tag: 'el-rate',\n    tagIcon: 'rate',\n    defaultValue: 0,\n    span: 24,\n    labelWidth: null,\n    style: {},\n    max: 5,\n    'allow-half': false,\n    'show-text': false,\n    'show-score': false,\n    disabled: false,\n    required: true,\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/rate'\n  },\n  {\n    label: '颜色选择',\n    tag: 'el-color-picker',\n    tagIcon: 'color',\n    defaultValue: null,\n    labelWidth: null,\n    'show-alpha': false,\n    'color-format': '',\n    disabled: false,\n    required: true,\n    size: 'medium',\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/color-picker'\n  },\n  {\n    label: '上传',\n    tag: 'el-upload',\n    tagIcon: 'upload',\n    action: 'https://jsonplaceholder.typicode.com/posts/',\n    defaultValue: null,\n    labelWidth: null,\n    disabled: false,\n    required: true,\n    accept: '',\n    name: 'file',\n    'auto-upload': true,\n    showTip: false,\n    buttonText: '点击上传',\n    fileSize: 2,\n    sizeUnit: 'MB',\n    'list-type': 'text',\n    multiple: false,\n    regList: [],\n    changeTag: true,\n    document: 'https://element.eleme.cn/#/zh-CN/component/upload'\n  }\n]\n\nexport const layoutComponents = [\n  {\n    layout: 'rowFormItem',\n    tagIcon: 'row',\n    type: 'default',\n    justify: 'start',\n    align: 'top',\n    label: '行容器',\n    layoutTree: true,\n    children: [],\n    document: 'https://element.eleme.cn/#/zh-CN/component/layout'\n  },\n  {\n    layout: 'colFormItem',\n    label: '按钮',\n    changeTag: true,\n    labelWidth: null,\n    tag: 'el-button',\n    tagIcon: 'button',\n    span: 24,\n    default: '主要按钮',\n    type: 'primary',\n    icon: 'el-icon-search',\n    size: 'medium',\n    disabled: false,\n    document: 'https://element.eleme.cn/#/zh-CN/component/button'\n  }\n]\n\n// 组件rule的触发方式，无触发方式的组件不生成rule\nexport const trigger = {\n  'el-input': 'blur',\n  'el-input-number': 'blur',\n  'el-select': 'change',\n  'el-radio-group': 'change',\n  'el-checkbox-group': 'change',\n  'el-cascader': 'change',\n  'el-time-picker': 'change',\n  'el-date-picker': 'change',\n  'el-rate': 'change'\n}\n"
  },
  {
    "path": "ruoyi-ui/src/utils/generator/css.js",
    "content": "const styles = {\n  'el-rate': '.el-rate{display: inline-block; vertical-align: text-top;}',\n  'el-upload': '.el-upload__tip{line-height: 1.2;}'\n}\n\nfunction addCss(cssList, el) {\n  const css = styles[el.tag]\n  css && cssList.indexOf(css) === -1 && cssList.push(css)\n  if (el.children) {\n    el.children.forEach(el2 => addCss(cssList, el2))\n  }\n}\n\nexport function makeUpCss(conf) {\n  const cssList = []\n  conf.fields.forEach(el => addCss(cssList, el))\n  return cssList.join('\\n')\n}\n"
  },
  {
    "path": "ruoyi-ui/src/utils/generator/drawingDefault.js",
    "content": "export default [\n  {\n    layout: 'colFormItem',\n    tagIcon: 'input',\n    label: '手机号',\n    vModel: 'mobile',\n    formId: 6,\n    tag: 'el-input',\n    placeholder: '请输入手机号',\n    defaultValue: '',\n    span: 24,\n    style: { width: '100%' },\n    clearable: true,\n    prepend: '',\n    append: '',\n    'prefix-icon': 'el-icon-mobile',\n    'suffix-icon': '',\n    maxlength: 11,\n    'show-word-limit': true,\n    readonly: false,\n    disabled: false,\n    required: true,\n    changeTag: true,\n    regList: [{\n      pattern: '/^1(3|4|5|7|8|9)\\\\d{9}$/',\n      message: '手机号格式错误'\n    }]\n  }\n]\n"
  },
  {
    "path": "ruoyi-ui/src/utils/generator/html.js",
    "content": "/* eslint-disable max-len */\nimport { trigger } from './config'\n\nlet confGlobal\nlet someSpanIsNot24\n\nexport function dialogWrapper(str) {\n  return `<el-dialog v-bind=\"$attrs\" v-on=\"$listeners\" @open=\"onOpen\" @close=\"onClose\" title=\"Dialog Title\">\n    ${str}\n    <div slot=\"footer\">\n      <el-button @click=\"close\">取消</el-button>\n      <el-button type=\"primary\" @click=\"handleConfirm\">确定</el-button>\n    </div>\n  </el-dialog>`\n}\n\nexport function vueTemplate(str) {\n  return `<template>\n    <div>\n      ${str}\n    </div>\n  </template>`\n}\n\nexport function vueScript(str) {\n  return `<script>\n    ${str}\n  </script>`\n}\n\nexport function cssStyle(cssStr) {\n  return `<style>\n    ${cssStr}\n  </style>`\n}\n\nfunction buildFormTemplate(conf, child, type) {\n  let labelPosition = ''\n  if (conf.labelPosition !== 'right') {\n    labelPosition = `label-position=\"${conf.labelPosition}\"`\n  }\n  const disabled = conf.disabled ? `:disabled=\"${conf.disabled}\"` : ''\n  let str = `<el-form ref=\"${conf.formRef}\" :model=\"${conf.formModel}\" :rules=\"${conf.formRules}\" size=\"${conf.size}\" ${disabled} label-width=\"${conf.labelWidth}px\" ${labelPosition}>\n      ${child}\n      ${buildFromBtns(conf, type)}\n    </el-form>`\n  if (someSpanIsNot24) {\n    str = `<el-row :gutter=\"${conf.gutter}\">\n        ${str}\n      </el-row>`\n  }\n  return str\n}\n\nfunction buildFromBtns(conf, type) {\n  let str = ''\n  if (conf.formBtns && type === 'file') {\n    str = `<el-form-item size=\"large\">\n          <el-button type=\"primary\" @click=\"submitForm\">提交</el-button>\n          <el-button @click=\"resetForm\">重置</el-button>\n        </el-form-item>`\n    if (someSpanIsNot24) {\n      str = `<el-col :span=\"24\">\n          ${str}\n        </el-col>`\n    }\n  }\n  return str\n}\n\n// span不为24的用el-col包裹\nfunction colWrapper(element, str) {\n  if (someSpanIsNot24 || element.span !== 24) {\n    return `<el-col :span=\"${element.span}\">\n      ${str}\n    </el-col>`\n  }\n  return str\n}\n\nconst layouts = {\n  colFormItem(element) {\n    let labelWidth = ''\n    if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) {\n      labelWidth = `label-width=\"${element.labelWidth}px\"`\n    }\n    const required = !trigger[element.tag] && element.required ? 'required' : ''\n    const tagDom = tags[element.tag] ? tags[element.tag](element) : null\n    let str = `<el-form-item ${labelWidth} label=\"${element.label}\" prop=\"${element.vModel}\" ${required}>\n        ${tagDom}\n      </el-form-item>`\n    str = colWrapper(element, str)\n    return str\n  },\n  rowFormItem(element) {\n    const type = element.type === 'default' ? '' : `type=\"${element.type}\"`\n    const justify = element.type === 'default' ? '' : `justify=\"${element.justify}\"`\n    const align = element.type === 'default' ? '' : `align=\"${element.align}\"`\n    const gutter = element.gutter ? `gutter=\"${element.gutter}\"` : ''\n    const children = element.children.map(el => layouts[el.layout](el))\n    let str = `<el-row ${type} ${justify} ${align} ${gutter}>\n      ${children.join('\\n')}\n    </el-row>`\n    str = colWrapper(element, str)\n    return str\n  }\n}\n\nconst tags = {\n  'el-button': el => {\n    const {\n      tag, disabled\n    } = attrBuilder(el)\n    const type = el.type ? `type=\"${el.type}\"` : ''\n    const icon = el.icon ? `icon=\"${el.icon}\"` : ''\n    const size = el.size ? `size=\"${el.size}\"` : ''\n    let child = buildElButtonChild(el)\n\n    if (child) child = `\\n${child}\\n` // 换行\n    return `<${el.tag} ${type} ${icon} ${size} ${disabled}>${child}</${el.tag}>`\n  },\n  'el-input': el => {\n    const {\n      disabled, vModel, clearable, placeholder, width\n    } = attrBuilder(el)\n    const maxlength = el.maxlength ? `:maxlength=\"${el.maxlength}\"` : ''\n    const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : ''\n    const readonly = el.readonly ? 'readonly' : ''\n    const prefixIcon = el['prefix-icon'] ? `prefix-icon='${el['prefix-icon']}'` : ''\n    const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : ''\n    const showPassword = el['show-password'] ? 'show-password' : ''\n    const type = el.type ? `type=\"${el.type}\"` : ''\n    const autosize = el.autosize && el.autosize.minRows\n      ? `:autosize=\"{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}\"`\n      : ''\n    let child = buildElInputChild(el)\n\n    if (child) child = `\\n${child}\\n` // 换行\n    return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}</${el.tag}>`\n  },\n  'el-input-number': el => {\n    const { disabled, vModel, placeholder } = attrBuilder(el)\n    const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : ''\n    const min = el.min ? `:min='${el.min}'` : ''\n    const max = el.max ? `:max='${el.max}'` : ''\n    const step = el.step ? `:step='${el.step}'` : ''\n    const stepStrictly = el['step-strictly'] ? 'step-strictly' : ''\n    const precision = el.precision ? `:precision='${el.precision}'` : ''\n\n    return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${el.tag}>`\n  },\n  'el-select': el => {\n    const {\n      disabled, vModel, clearable, placeholder, width\n    } = attrBuilder(el)\n    const filterable = el.filterable ? 'filterable' : ''\n    const multiple = el.multiple ? 'multiple' : ''\n    let child = buildElSelectChild(el)\n\n    if (child) child = `\\n${child}\\n` // 换行\n    return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${el.tag}>`\n  },\n  'el-radio-group': el => {\n    const { disabled, vModel } = attrBuilder(el)\n    const size = `size=\"${el.size}\"`\n    let child = buildElRadioGroupChild(el)\n\n    if (child) child = `\\n${child}\\n` // 换行\n    return `<${el.tag} ${vModel} ${size} ${disabled}>${child}</${el.tag}>`\n  },\n  'el-checkbox-group': el => {\n    const { disabled, vModel } = attrBuilder(el)\n    const size = `size=\"${el.size}\"`\n    const min = el.min ? `:min=\"${el.min}\"` : ''\n    const max = el.max ? `:max=\"${el.max}\"` : ''\n    let child = buildElCheckboxGroupChild(el)\n\n    if (child) child = `\\n${child}\\n` // 换行\n    return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}</${el.tag}>`\n  },\n  'el-switch': el => {\n    const { disabled, vModel } = attrBuilder(el)\n    const activeText = el['active-text'] ? `active-text=\"${el['active-text']}\"` : ''\n    const inactiveText = el['inactive-text'] ? `inactive-text=\"${el['inactive-text']}\"` : ''\n    const activeColor = el['active-color'] ? `active-color=\"${el['active-color']}\"` : ''\n    const inactiveColor = el['inactive-color'] ? `inactive-color=\"${el['inactive-color']}\"` : ''\n    const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : ''\n    const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : ''\n\n    return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${el.tag}>`\n  },\n  'el-cascader': el => {\n    const {\n      disabled, vModel, clearable, placeholder, width\n    } = attrBuilder(el)\n    const options = el.options ? `:options=\"${el.vModel}Options\"` : ''\n    const props = el.props ? `:props=\"${el.vModel}Props\"` : ''\n    const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels=\"false\"'\n    const filterable = el.filterable ? 'filterable' : ''\n    const separator = el.separator === '/' ? '' : `separator=\"${el.separator}\"`\n\n    return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}></${el.tag}>`\n  },\n  'el-slider': el => {\n    const { disabled, vModel } = attrBuilder(el)\n    const min = el.min ? `:min='${el.min}'` : ''\n    const max = el.max ? `:max='${el.max}'` : ''\n    const step = el.step ? `:step='${el.step}'` : ''\n    const range = el.range ? 'range' : ''\n    const showStops = el['show-stops'] ? `:show-stops=\"${el['show-stops']}\"` : ''\n\n    return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${el.tag}>`\n  },\n  'el-time-picker': el => {\n    const {\n      disabled, vModel, clearable, placeholder, width\n    } = attrBuilder(el)\n    const startPlaceholder = el['start-placeholder'] ? `start-placeholder=\"${el['start-placeholder']}\"` : ''\n    const endPlaceholder = el['end-placeholder'] ? `end-placeholder=\"${el['end-placeholder']}\"` : ''\n    const rangeSeparator = el['range-separator'] ? `range-separator=\"${el['range-separator']}\"` : ''\n    const isRange = el['is-range'] ? 'is-range' : ''\n    const format = el.format ? `format=\"${el.format}\"` : ''\n    const valueFormat = el['value-format'] ? `value-format=\"${el['value-format']}\"` : ''\n    const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : ''\n\n    return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${el.tag}>`\n  },\n  'el-date-picker': el => {\n    const {\n      disabled, vModel, clearable, placeholder, width\n    } = attrBuilder(el)\n    const startPlaceholder = el['start-placeholder'] ? `start-placeholder=\"${el['start-placeholder']}\"` : ''\n    const endPlaceholder = el['end-placeholder'] ? `end-placeholder=\"${el['end-placeholder']}\"` : ''\n    const rangeSeparator = el['range-separator'] ? `range-separator=\"${el['range-separator']}\"` : ''\n    const format = el.format ? `format=\"${el.format}\"` : ''\n    const valueFormat = el['value-format'] ? `value-format=\"${el['value-format']}\"` : ''\n    const type = el.type === 'date' ? '' : `type=\"${el.type}\"`\n    const readonly = el.readonly ? 'readonly' : ''\n\n    return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}></${el.tag}>`\n  },\n  'el-rate': el => {\n    const { disabled, vModel } = attrBuilder(el)\n    const max = el.max ? `:max='${el.max}'` : ''\n    const allowHalf = el['allow-half'] ? 'allow-half' : ''\n    const showText = el['show-text'] ? 'show-text' : ''\n    const showScore = el['show-score'] ? 'show-score' : ''\n\n    return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}></${el.tag}>`\n  },\n  'el-color-picker': el => {\n    const { disabled, vModel } = attrBuilder(el)\n    const size = `size=\"${el.size}\"`\n    const showAlpha = el['show-alpha'] ? 'show-alpha' : ''\n    const colorFormat = el['color-format'] ? `color-format=\"${el['color-format']}\"` : ''\n\n    return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${el.tag}>`\n  },\n  'el-upload': el => {\n    const disabled = el.disabled ? ':disabled=\\'true\\'' : ''\n    const action = el.action ? `:action=\"${el.vModel}Action\"` : ''\n    const multiple = el.multiple ? 'multiple' : ''\n    const listType = el['list-type'] !== 'text' ? `list-type=\"${el['list-type']}\"` : ''\n    const accept = el.accept ? `accept=\"${el.accept}\"` : ''\n    const name = el.name !== 'file' ? `name=\"${el.name}\"` : ''\n    const autoUpload = el['auto-upload'] === false ? ':auto-upload=\"false\"' : ''\n    const beforeUpload = `:before-upload=\"${el.vModel}BeforeUpload\"`\n    const fileList = `:file-list=\"${el.vModel}fileList\"`\n    const ref = `ref=\"${el.vModel}\"`\n    let child = buildElUploadChild(el)\n\n    if (child) child = `\\n${child}\\n` // 换行\n    return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}</${el.tag}>`\n  }\n}\n\nfunction attrBuilder(el) {\n  return {\n    vModel: `v-model=\"${confGlobal.formModel}.${el.vModel}\"`,\n    clearable: el.clearable ? 'clearable' : '',\n    placeholder: el.placeholder ? `placeholder=\"${el.placeholder}\"` : '',\n    width: el.style && el.style.width ? ':style=\"{width: \\'100%\\'}\"' : '',\n    disabled: el.disabled ? ':disabled=\\'true\\'' : ''\n  }\n}\n\n// el-buttin 子级\nfunction buildElButtonChild(conf) {\n  const children = []\n  if (conf.default) {\n    children.push(conf.default)\n  }\n  return children.join('\\n')\n}\n\n// el-input innerHTML\nfunction buildElInputChild(conf) {\n  const children = []\n  if (conf.prepend) {\n    children.push(`<template slot=\"prepend\">${conf.prepend}</template>`)\n  }\n  if (conf.append) {\n    children.push(`<template slot=\"append\">${conf.append}</template>`)\n  }\n  return children.join('\\n')\n}\n\nfunction buildElSelectChild(conf) {\n  const children = []\n  if (conf.options && conf.options.length) {\n    children.push(`<el-option v-for=\"(item, index) in ${conf.vModel}Options\" :key=\"index\" :label=\"item.label\" :value=\"item.value\" :disabled=\"item.disabled\"></el-option>`)\n  }\n  return children.join('\\n')\n}\n\nfunction buildElRadioGroupChild(conf) {\n  const children = []\n  if (conf.options && conf.options.length) {\n    const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio'\n    const border = conf.border ? 'border' : ''\n    children.push(`<${tag} v-for=\"(item, index) in ${conf.vModel}Options\" :key=\"index\" :label=\"item.value\" :disabled=\"item.disabled\" ${border}>{{item.label}}</${tag}>`)\n  }\n  return children.join('\\n')\n}\n\nfunction buildElCheckboxGroupChild(conf) {\n  const children = []\n  if (conf.options && conf.options.length) {\n    const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox'\n    const border = conf.border ? 'border' : ''\n    children.push(`<${tag} v-for=\"(item, index) in ${conf.vModel}Options\" :key=\"index\" :label=\"item.value\" :disabled=\"item.disabled\" ${border}>{{item.label}}</${tag}>`)\n  }\n  return children.join('\\n')\n}\n\nfunction buildElUploadChild(conf) {\n  const list = []\n  if (conf['list-type'] === 'picture-card') list.push('<i class=\"el-icon-plus\"></i>')\n  else list.push(`<el-button size=\"small\" type=\"primary\" icon=\"el-icon-upload\">${conf.buttonText}</el-button>`)\n  if (conf.showTip) list.push(`<div slot=\"tip\" class=\"el-upload__tip\">只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件</div>`)\n  return list.join('\\n')\n}\n\nexport function makeUpHtml(conf, type) {\n  const htmlList = []\n  confGlobal = conf\n  someSpanIsNot24 = conf.fields.some(item => item.span !== 24)\n  conf.fields.forEach(el => {\n    htmlList.push(layouts[el.layout](el))\n  })\n  const htmlStr = htmlList.join('\\n')\n\n  let temp = buildFormTemplate(conf, htmlStr, type)\n  if (type === 'dialog') {\n    temp = dialogWrapper(temp)\n  }\n  confGlobal = null\n  return temp\n}\n"
  },
  {
    "path": "ruoyi-ui/src/utils/generator/icon.json",
    "content": "[\"platform-eleme\",\"eleme\",\"delete-solid\",\"delete\",\"s-tools\",\"setting\",\"user-solid\",\"user\",\"phone\",\"phone-outline\",\"more\",\"more-outline\",\"star-on\",\"star-off\",\"s-goods\",\"goods\",\"warning\",\"warning-outline\",\"question\",\"info\",\"remove\",\"circle-plus\",\"success\",\"error\",\"zoom-in\",\"zoom-out\",\"remove-outline\",\"circle-plus-outline\",\"circle-check\",\"circle-close\",\"s-help\",\"help\",\"minus\",\"plus\",\"check\",\"close\",\"picture\",\"picture-outline\",\"picture-outline-round\",\"upload\",\"upload2\",\"download\",\"camera-solid\",\"camera\",\"video-camera-solid\",\"video-camera\",\"message-solid\",\"bell\",\"s-cooperation\",\"s-order\",\"s-platform\",\"s-fold\",\"s-unfold\",\"s-operation\",\"s-promotion\",\"s-home\",\"s-release\",\"s-ticket\",\"s-management\",\"s-open\",\"s-shop\",\"s-marketing\",\"s-flag\",\"s-comment\",\"s-finance\",\"s-claim\",\"s-custom\",\"s-opportunity\",\"s-data\",\"s-check\",\"s-grid\",\"menu\",\"share\",\"d-caret\",\"caret-left\",\"caret-right\",\"caret-bottom\",\"caret-top\",\"bottom-left\",\"bottom-right\",\"back\",\"right\",\"bottom\",\"top\",\"top-left\",\"top-right\",\"arrow-left\",\"arrow-right\",\"arrow-down\",\"arrow-up\",\"d-arrow-left\",\"d-arrow-right\",\"video-pause\",\"video-play\",\"refresh\",\"refresh-right\",\"refresh-left\",\"finished\",\"sort\",\"sort-up\",\"sort-down\",\"rank\",\"loading\",\"view\",\"c-scale-to-original\",\"date\",\"edit\",\"edit-outline\",\"folder\",\"folder-opened\",\"folder-add\",\"folder-remove\",\"folder-delete\",\"folder-checked\",\"tickets\",\"document-remove\",\"document-delete\",\"document-copy\",\"document-checked\",\"document\",\"document-add\",\"printer\",\"paperclip\",\"takeaway-box\",\"search\",\"monitor\",\"attract\",\"mobile\",\"scissors\",\"umbrella\",\"headset\",\"brush\",\"mouse\",\"coordinate\",\"magic-stick\",\"reading\",\"data-line\",\"data-board\",\"pie-chart\",\"data-analysis\",\"collection-tag\",\"film\",\"suitcase\",\"suitcase-1\",\"receiving\",\"collection\",\"files\",\"notebook-1\",\"notebook-2\",\"toilet-paper\",\"office-building\",\"school\",\"table-lamp\",\"house\",\"no-smoking\",\"smoking\",\"shopping-cart-full\",\"shopping-cart-1\",\"shopping-cart-2\",\"shopping-bag-1\",\"shopping-bag-2\",\"sold-out\",\"sell\",\"present\",\"box\",\"bank-card\",\"money\",\"coin\",\"wallet\",\"discount\",\"price-tag\",\"news\",\"guide\",\"male\",\"female\",\"thumb\",\"cpu\",\"link\",\"connection\",\"open\",\"turn-off\",\"set-up\",\"chat-round\",\"chat-line-round\",\"chat-square\",\"chat-dot-round\",\"chat-dot-square\",\"chat-line-square\",\"message\",\"postcard\",\"position\",\"turn-off-microphone\",\"microphone\",\"close-notification\",\"bangzhu\",\"time\",\"odometer\",\"crop\",\"aim\",\"switch-button\",\"full-screen\",\"copy-document\",\"mic\",\"stopwatch\",\"medal-1\",\"medal\",\"trophy\",\"trophy-1\",\"first-aid-kit\",\"discover\",\"place\",\"location\",\"location-outline\",\"location-information\",\"add-location\",\"delete-location\",\"map-location\",\"alarm-clock\",\"timer\",\"watch-1\",\"watch\",\"lock\",\"unlock\",\"key\",\"service\",\"mobile-phone\",\"bicycle\",\"truck\",\"ship\",\"basketball\",\"football\",\"soccer\",\"baseball\",\"wind-power\",\"light-rain\",\"lightning\",\"heavy-rain\",\"sunrise\",\"sunrise-1\",\"sunset\",\"sunny\",\"cloudy\",\"partly-cloudy\",\"cloudy-and-sunny\",\"moon\",\"moon-night\",\"dish\",\"dish-1\",\"food\",\"chicken\",\"fork-spoon\",\"knife-fork\",\"burger\",\"tableware\",\"sugar\",\"dessert\",\"ice-cream\",\"hot-water\",\"water-cup\",\"coffee-cup\",\"cold-drink\",\"goblet\",\"goblet-full\",\"goblet-square\",\"goblet-square-full\",\"refrigerator\",\"grape\",\"watermelon\",\"cherry\",\"apple\",\"pear\",\"orange\",\"coffee\",\"ice-tea\",\"ice-drink\",\"milk-tea\",\"potato-strips\",\"lollipop\",\"ice-cream-square\",\"ice-cream-round\"]"
  },
  {
    "path": "ruoyi-ui/src/utils/generator/js.js",
    "content": "import { isArray } from 'util'\nimport { exportDefault, titleCase } from '@/utils/index'\nimport { trigger } from './config'\n\nconst units = {\n  KB: '1024',\n  MB: '1024 / 1024',\n  GB: '1024 / 1024 / 1024'\n}\nlet confGlobal\nconst inheritAttrs = {\n  file: '',\n  dialog: 'inheritAttrs: false,'\n}\n\n\nexport function makeUpJs(conf, type) {\n  confGlobal = conf = JSON.parse(JSON.stringify(conf))\n  const dataList = []\n  const ruleList = []\n  const optionsList = []\n  const propsList = []\n  const methodList = mixinMethod(type)\n  const uploadVarList = []\n\n  conf.fields.forEach(el => {\n    buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)\n  })\n\n  const script = buildexport(\n    conf,\n    type,\n    dataList.join('\\n'),\n    ruleList.join('\\n'),\n    optionsList.join('\\n'),\n    uploadVarList.join('\\n'),\n    propsList.join('\\n'),\n    methodList.join('\\n')\n  )\n  confGlobal = null\n  return script\n}\n\nfunction buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) {\n  buildData(el, dataList)\n  buildRules(el, ruleList)\n\n  if (el.options && el.options.length) {\n    buildOptions(el, optionsList)\n    if (el.dataType === 'dynamic') {\n      const model = `${el.vModel}Options`\n      const options = titleCase(model)\n      buildOptionMethod(`get${options}`, model, methodList)\n    }\n  }\n\n  if (el.props && el.props.props) {\n    buildProps(el, propsList)\n  }\n\n  if (el.action && el.tag === 'el-upload') {\n    uploadVarList.push(\n      `${el.vModel}Action: '${el.action}',\n      ${el.vModel}fileList: [],`\n    )\n    methodList.push(buildBeforeUpload(el))\n    if (!el['auto-upload']) {\n      methodList.push(buildSubmitUpload(el))\n    }\n  }\n\n  if (el.children) {\n    el.children.forEach(el2 => {\n      buildAttributes(el2, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)\n    })\n  }\n}\n\nfunction mixinMethod(type) {\n  const list = []; const\n    minxins = {\n      file: confGlobal.formBtns ? {\n        submitForm: `submitForm() {\n        this.$refs['${confGlobal.formRef}'].validate(valid => {\n          if(!valid) return\n          // TODO 提交表单\n        })\n      },`,\n        resetForm: `resetForm() {\n        this.$refs['${confGlobal.formRef}'].resetFields()\n      },`\n      } : null,\n      dialog: {\n        onOpen: 'onOpen() {},',\n        onClose: `onClose() {\n        this.$refs['${confGlobal.formRef}'].resetFields()\n      },`,\n        close: `close() {\n        this.$emit('update:visible', false)\n      },`,\n        handleConfirm: `handleConfirm() {\n        this.$refs['${confGlobal.formRef}'].validate(valid => {\n          if(!valid) return\n          this.close()\n        })\n      },`\n      }\n    }\n\n  const methods = minxins[type]\n  if (methods) {\n    Object.keys(methods).forEach(key => {\n      list.push(methods[key])\n    })\n  }\n\n  return list\n}\n\nfunction buildData(conf, dataList) {\n  if (conf.vModel === undefined) return\n  let defaultValue\n  if (typeof (conf.defaultValue) === 'string' && !conf.multiple) {\n    defaultValue = `'${conf.defaultValue}'`\n  } else {\n    defaultValue = `${JSON.stringify(conf.defaultValue)}`\n  }\n  dataList.push(`${conf.vModel}: ${defaultValue},`)\n}\n\nfunction buildRules(conf, ruleList) {\n  if (conf.vModel === undefined) return\n  const rules = []\n  if (trigger[conf.tag]) {\n    if (conf.required) {\n      const type = isArray(conf.defaultValue) ? 'type: \\'array\\',' : ''\n      let message = isArray(conf.defaultValue) ? `请至少选择一个${conf.vModel}` : conf.placeholder\n      if (message === undefined) message = `${conf.label}不能为空`\n      rules.push(`{ required: true, ${type} message: '${message}', trigger: '${trigger[conf.tag]}' }`)\n    }\n    if (conf.regList && isArray(conf.regList)) {\n      conf.regList.forEach(item => {\n        if (item.pattern) {\n          rules.push(`{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${trigger[conf.tag]}' }`)\n        }\n      })\n    }\n    ruleList.push(`${conf.vModel}: [${rules.join(',')}],`)\n  }\n}\n\nfunction buildOptions(conf, optionsList) {\n  if (conf.vModel === undefined) return\n  if (conf.dataType === 'dynamic') { conf.options = [] }\n  const str = `${conf.vModel}Options: ${JSON.stringify(conf.options)},`\n  optionsList.push(str)\n}\n\nfunction buildProps(conf, propsList) {\n  if (conf.dataType === 'dynamic') {\n    conf.valueKey !== 'value' && (conf.props.props.value = conf.valueKey)\n    conf.labelKey !== 'label' && (conf.props.props.label = conf.labelKey)\n    conf.childrenKey !== 'children' && (conf.props.props.children = conf.childrenKey)\n  }\n  const str = `${conf.vModel}Props: ${JSON.stringify(conf.props.props)},`\n  propsList.push(str)\n}\n\nfunction buildBeforeUpload(conf) {\n  const unitNum = units[conf.sizeUnit]; let rightSizeCode = ''; let acceptCode = ''; const\n    returnList = []\n  if (conf.fileSize) {\n    rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${conf.fileSize}\n    if(!isRightSize){\n      this.$message.error('文件大小超过 ${conf.fileSize}${conf.sizeUnit}')\n    }`\n    returnList.push('isRightSize')\n  }\n  if (conf.accept) {\n    acceptCode = `let isAccept = new RegExp('${conf.accept}').test(file.type)\n    if(!isAccept){\n      this.$message.error('应该选择${conf.accept}类型的文件')\n    }`\n    returnList.push('isAccept')\n  }\n  const str = `${conf.vModel}BeforeUpload(file) {\n    ${rightSizeCode}\n    ${acceptCode}\n    return ${returnList.join('&&')}\n  },`\n  return returnList.length ? str : ''\n}\n\nfunction buildSubmitUpload(conf) {\n  const str = `submitUpload() {\n    this.$refs['${conf.vModel}'].submit()\n  },`\n  return str\n}\n\nfunction buildOptionMethod(methodName, model, methodList) {\n  const str = `${methodName}() {\n    // TODO 发起请求获取数据\n    this.${model}\n  },`\n  methodList.push(str)\n}\n\nfunction buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods) {\n  const str = `${exportDefault}{\n  ${inheritAttrs[type]}\n  components: {},\n  props: [],\n  data () {\n    return {\n      ${conf.formModel}: {\n        ${data}\n      },\n      ${conf.formRules}: {\n        ${rules}\n      },\n      ${uploadVar}\n      ${selectOptions}\n      ${props}\n    }\n  },\n  computed: {},\n  watch: {},\n  created () {},\n  mounted () {},\n  methods: {\n    ${methods}\n  }\n}`\n  return str\n}\n"
  },
  {
    "path": "ruoyi-ui/src/utils/generator/render.js",
    "content": "import { makeMap } from '@/utils/index'\n\n// 参考https://github.com/vuejs/vue/blob/v2.6.10/src/platforms/web/server/util.js\nconst isAttr = makeMap(\n  'accept,accept-charset,accesskey,action,align,alt,async,autocomplete,'\n  + 'autofocus,autoplay,autosave,bgcolor,border,buffered,challenge,charset,'\n  + 'checked,cite,class,code,codebase,color,cols,colspan,content,http-equiv,'\n  + 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,'\n  + 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,for,'\n  + 'form,formaction,headers,height,hidden,high,href,hreflang,http-equiv,'\n  + 'icon,id,ismap,itemprop,keytype,kind,label,lang,language,list,loop,low,'\n  + 'manifest,max,maxlength,media,method,GET,POST,min,multiple,email,file,'\n  + 'muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,'\n  + 'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,'\n  + 'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,'\n  + 'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,'\n  + 'target,title,type,usemap,value,width,wrap'\n)\n\nfunction vModel(self, dataObject, defaultValue) {\n  dataObject.props.value = defaultValue\n\n  dataObject.on.input = val => {\n    self.$emit('input', val)\n  }\n}\n\nconst componentChild = {\n  'el-button': {\n    default(h, conf, key) {\n      return conf[key]\n    },\n  },\n  'el-input': {\n    prepend(h, conf, key) {\n      return <template slot=\"prepend\">{conf[key]}</template>\n    },\n    append(h, conf, key) {\n      return <template slot=\"append\">{conf[key]}</template>\n    }\n  },\n  'el-select': {\n    options(h, conf, key) {\n      const list = []\n      conf.options.forEach(item => {\n        list.push(<el-option label={item.label} value={item.value} disabled={item.disabled}></el-option>)\n      })\n      return list\n    }\n  },\n  'el-radio-group': {\n    options(h, conf, key) {\n      const list = []\n      conf.options.forEach(item => {\n        if (conf.optionType === 'button') list.push(<el-radio-button label={item.value}>{item.label}</el-radio-button>)\n        else list.push(<el-radio label={item.value} border={conf.border}>{item.label}</el-radio>)\n      })\n      return list\n    }\n  },\n  'el-checkbox-group': {\n    options(h, conf, key) {\n      const list = []\n      conf.options.forEach(item => {\n        if (conf.optionType === 'button') {\n          list.push(<el-checkbox-button label={item.value}>{item.label}</el-checkbox-button>)\n        } else {\n          list.push(<el-checkbox label={item.value} border={conf.border}>{item.label}</el-checkbox>)\n        }\n      })\n      return list\n    }\n  },\n  'el-upload': {\n    'list-type': (h, conf, key) => {\n      const list = []\n      if (conf['list-type'] === 'picture-card') {\n        list.push(<i class=\"el-icon-plus\"></i>)\n      } else {\n        list.push(<el-button size=\"small\" type=\"primary\" icon=\"el-icon-upload\">{conf.buttonText}</el-button>)\n      }\n      if (conf.showTip) {\n        list.push(<div slot=\"tip\" class=\"el-upload__tip\">只能上传不超过 {conf.fileSize}{conf.sizeUnit} 的{conf.accept}文件</div>)\n      }\n      return list\n    }\n  }\n}\n\nexport default {\n  render(h) {\n    const dataObject = {\n      attrs: {},\n      props: {},\n      on: {},\n      style: {}\n    }\n    const confClone = JSON.parse(JSON.stringify(this.conf))\n    const children = []\n\n    const childObjs = componentChild[confClone.tag]\n    if (childObjs) {\n      Object.keys(childObjs).forEach(key => {\n        const childFunc = childObjs[key]\n        if (confClone[key]) {\n          children.push(childFunc(h, confClone, key))\n        }\n      })\n    }\n\n    Object.keys(confClone).forEach(key => {\n      const val = confClone[key]\n      if (key === 'vModel') {\n        vModel(this, dataObject, confClone.defaultValue)\n      } else if (dataObject[key]) {\n        dataObject[key] = val\n      } else if (!isAttr(key)) {\n        dataObject.props[key] = val\n      } else {\n        dataObject.attrs[key] = val\n      }\n    })\n    return h(this.conf.tag, dataObject, children)\n  },\n  props: ['conf']\n}\n"
  },
  {
    "path": "ruoyi-ui/src/utils/index.js",
    "content": "import { parseTime } from './ruoyi'\n\n/**\n * 表格时间格式化\n */\nexport function formatDate(cellValue) {\n  if (cellValue == null || cellValue == \"\") return \"\";\n  var date = new Date(cellValue)\n  var year = date.getFullYear()\n  var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1\n  var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()\n  var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()\n  var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()\n  var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()\n  return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds\n}\n\n/**\n * @param {number} time\n * @param {string} option\n * @returns {string}\n */\nexport function formatTime(time, option) {\n  if (('' + time).length === 10) {\n    time = parseInt(time) * 1000\n  } else {\n    time = +time\n  }\n  const d = new Date(time)\n  const now = Date.now()\n\n  const diff = (now - d) / 1000\n\n  if (diff < 30) {\n    return '刚刚'\n  } else if (diff < 3600) {\n    // less 1 hour\n    return Math.ceil(diff / 60) + '分钟前'\n  } else if (diff < 3600 * 24) {\n    return Math.ceil(diff / 3600) + '小时前'\n  } else if (diff < 3600 * 24 * 2) {\n    return '1天前'\n  }\n  if (option) {\n    return parseTime(time, option)\n  } else {\n    return (\n      d.getMonth() +\n      1 +\n      '月' +\n      d.getDate() +\n      '日' +\n      d.getHours() +\n      '时' +\n      d.getMinutes() +\n      '分'\n    )\n  }\n}\n\n/**\n * @param {string} url\n * @returns {Object}\n */\nexport function getQueryObject(url) {\n  url = url == null ? window.location.href : url\n  const search = url.substring(url.lastIndexOf('?') + 1)\n  const obj = {}\n  const reg = /([^?&=]+)=([^?&=]*)/g\n  search.replace(reg, (rs, $1, $2) => {\n    const name = decodeURIComponent($1)\n    let val = decodeURIComponent($2)\n    val = String(val)\n    obj[name] = val\n    return rs\n  })\n  return obj\n}\n\n/**\n * @param {string} input value\n * @returns {number} output value\n */\nexport function byteLength(str) {\n  // returns the byte length of an utf8 string\n  let s = str.length\n  for (var i = str.length - 1; i >= 0; i--) {\n    const code = str.charCodeAt(i)\n    if (code > 0x7f && code <= 0x7ff) s++\n    else if (code > 0x7ff && code <= 0xffff) s += 2\n    if (code >= 0xDC00 && code <= 0xDFFF) i--\n  }\n  return s\n}\n\n/**\n * @param {Array} actual\n * @returns {Array}\n */\nexport function cleanArray(actual) {\n  const newArray = []\n  for (let i = 0; i < actual.length; i++) {\n    if (actual[i]) {\n      newArray.push(actual[i])\n    }\n  }\n  return newArray\n}\n\n/**\n * @param {Object} json\n * @returns {Array}\n */\nexport function param(json) {\n  if (!json) return ''\n  return cleanArray(\n    Object.keys(json).map(key => {\n      if (json[key] === undefined) return ''\n      return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])\n    })\n  ).join('&')\n}\n\n/**\n * @param {string} url\n * @returns {Object}\n */\nexport function param2Obj(url) {\n  const search = decodeURIComponent(url.split('?')[1]).replace(/\\+/g, ' ')\n  if (!search) {\n    return {}\n  }\n  const obj = {}\n  const searchArr = search.split('&')\n  searchArr.forEach(v => {\n    const index = v.indexOf('=')\n    if (index !== -1) {\n      const name = v.substring(0, index)\n      const val = v.substring(index + 1, v.length)\n      obj[name] = val\n    }\n  })\n  return obj\n}\n\n/**\n * @param {string} val\n * @returns {string}\n */\nexport function html2Text(val) {\n  const div = document.createElement('div')\n  div.innerHTML = val\n  return div.textContent || div.innerText\n}\n\n/**\n * Merges two objects, giving the last one precedence\n * @param {Object} target\n * @param {(Object|Array)} source\n * @returns {Object}\n */\nexport function objectMerge(target, source) {\n  if (typeof target !== 'object') {\n    target = {}\n  }\n  if (Array.isArray(source)) {\n    return source.slice()\n  }\n  Object.keys(source).forEach(property => {\n    const sourceProperty = source[property]\n    if (typeof sourceProperty === 'object') {\n      target[property] = objectMerge(target[property], sourceProperty)\n    } else {\n      target[property] = sourceProperty\n    }\n  })\n  return target\n}\n\n/**\n * @param {HTMLElement} element\n * @param {string} className\n */\nexport function toggleClass(element, className) {\n  if (!element || !className) {\n    return\n  }\n  let classString = element.className\n  const nameIndex = classString.indexOf(className)\n  if (nameIndex === -1) {\n    classString += '' + className\n  } else {\n    classString =\n      classString.substr(0, nameIndex) +\n      classString.substr(nameIndex + className.length)\n  }\n  element.className = classString\n}\n\n/**\n * @param {string} type\n * @returns {Date}\n */\nexport function getTime(type) {\n  if (type === 'start') {\n    return new Date().getTime() - 3600 * 1000 * 24 * 90\n  } else {\n    return new Date(new Date().toDateString())\n  }\n}\n\n/**\n * @param {Function} func\n * @param {number} wait\n * @param {boolean} immediate\n * @return {*}\n */\nexport function debounce(func, wait, immediate) {\n  let timeout, args, context, timestamp, result\n\n  const later = function() {\n    // 据上一次触发时间间隔\n    const last = +new Date() - timestamp\n\n    // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait\n    if (last < wait && last > 0) {\n      timeout = setTimeout(later, wait - last)\n    } else {\n      timeout = null\n      // 如果设定为immediate===true，因为开始边界已经调用过了此处无需调用\n      if (!immediate) {\n        result = func.apply(context, args)\n        if (!timeout) context = args = null\n      }\n    }\n  }\n\n  return function(...args) {\n    context = this\n    timestamp = +new Date()\n    const callNow = immediate && !timeout\n    // 如果延时不存在，重新设定延时\n    if (!timeout) timeout = setTimeout(later, wait)\n    if (callNow) {\n      result = func.apply(context, args)\n      context = args = null\n    }\n\n    return result\n  }\n}\n\n/**\n * This is just a simple version of deep copy\n * Has a lot of edge cases bug\n * If you want to use a perfect deep copy, use lodash's _.cloneDeep\n * @param {Object} source\n * @returns {Object}\n */\nexport function deepClone(source) {\n  if (!source && typeof source !== 'object') {\n    throw new Error('error arguments', 'deepClone')\n  }\n  const targetObj = source.constructor === Array ? [] : {}\n  Object.keys(source).forEach(keys => {\n    if (source[keys] && typeof source[keys] === 'object') {\n      targetObj[keys] = deepClone(source[keys])\n    } else {\n      targetObj[keys] = source[keys]\n    }\n  })\n  return targetObj\n}\n\n/**\n * @param {Array} arr\n * @returns {Array}\n */\nexport function uniqueArr(arr) {\n  return Array.from(new Set(arr))\n}\n\n/**\n * @returns {string}\n */\nexport function createUniqueString() {\n  const timestamp = +new Date() + ''\n  const randomNum = parseInt((1 + Math.random()) * 65536) + ''\n  return (+(randomNum + timestamp)).toString(32)\n}\n\n/**\n * Check if an element has a class\n * @param {HTMLElement} elm\n * @param {string} cls\n * @returns {boolean}\n */\nexport function hasClass(ele, cls) {\n  return !!ele.className.match(new RegExp('(\\\\s|^)' + cls + '(\\\\s|$)'))\n}\n\n/**\n * Add class to element\n * @param {HTMLElement} elm\n * @param {string} cls\n */\nexport function addClass(ele, cls) {\n  if (!hasClass(ele, cls)) ele.className += ' ' + cls\n}\n\n/**\n * Remove class from element\n * @param {HTMLElement} elm\n * @param {string} cls\n */\nexport function removeClass(ele, cls) {\n  if (hasClass(ele, cls)) {\n    const reg = new RegExp('(\\\\s|^)' + cls + '(\\\\s|$)')\n    ele.className = ele.className.replace(reg, ' ')\n  }\n}\n\nexport function makeMap(str, expectsLowerCase) {\n  const map = Object.create(null)\n  const list = str.split(',')\n  for (let i = 0; i < list.length; i++) {\n    map[list[i]] = true\n  }\n  return expectsLowerCase\n    ? val => map[val.toLowerCase()]\n    : val => map[val]\n}\n\nexport const exportDefault = 'export default '\n\nexport const beautifierConf = {\n  html: {\n    indent_size: '2',\n    indent_char: ' ',\n    max_preserve_newlines: '-1',\n    preserve_newlines: false,\n    keep_array_indentation: false,\n    break_chained_methods: false,\n    indent_scripts: 'separate',\n    brace_style: 'end-expand',\n    space_before_conditional: true,\n    unescape_strings: false,\n    jslint_happy: false,\n    end_with_newline: true,\n    wrap_line_length: '110',\n    indent_inner_html: true,\n    comma_first: false,\n    e4x: true,\n    indent_empty_lines: true\n  },\n  js: {\n    indent_size: '2',\n    indent_char: ' ',\n    max_preserve_newlines: '-1',\n    preserve_newlines: false,\n    keep_array_indentation: false,\n    break_chained_methods: false,\n    indent_scripts: 'normal',\n    brace_style: 'end-expand',\n    space_before_conditional: true,\n    unescape_strings: false,\n    jslint_happy: true,\n    end_with_newline: true,\n    wrap_line_length: '110',\n    indent_inner_html: true,\n    comma_first: false,\n    e4x: true,\n    indent_empty_lines: true\n  }\n}\n\n// 首字母大小\nexport function titleCase(str) {\n  return str.replace(/( |^)[a-z]/g, L => L.toUpperCase())\n}\n\n// 下划转驼峰\nexport function camelCase(str) {\n  return str.replace(/_[a-z]/g, str1 => str1.substr(-1).toUpperCase())\n}\n\nexport function isNumberStr(str) {\n  return /^[+-]?(0|([1-9]\\d*))(\\.\\d+)?$/g.test(str)\n}\n\n"
  },
  {
    "path": "ruoyi-ui/src/utils/jsencrypt.js",
    "content": "import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'\n\n// 密钥对生成 http://web.chacuo.net/netrsakeypair\n\nconst publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\\n' +\n  'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='\n\nconst privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\\n' +\n  '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\\n' +\n  'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\\n' +\n  'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\\n' +\n  'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\\n' +\n  'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\\n' +\n  'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\\n' +\n  'UP8iWi1Qw0Y='\n\n// 加密\nexport function encrypt(txt) {\n  const encryptor = new JSEncrypt()\n  encryptor.setPublicKey(publicKey) // 设置公钥\n  return encryptor.encrypt(txt) // 对数据进行加密\n}\n\n// 解密\nexport function decrypt(txt) {\n  const encryptor = new JSEncrypt()\n  encryptor.setPrivateKey(privateKey) // 设置私钥\n  return encryptor.decrypt(txt) // 对数据进行解密\n}\n\n"
  },
  {
    "path": "ruoyi-ui/src/utils/permission.js",
    "content": "import store from '@/store'\n\n/**\n * 字符权限校验\n * @param {Array} value 校验值\n * @returns {Boolean}\n */\nexport function checkPermi(value) {\n  if (value && value instanceof Array && value.length > 0) {\n    const permissions = store.getters && store.getters.permissions\n    const permissionDatas = value\n    const all_permission = \"*:*:*\";\n\n    const hasPermission = permissions.some(permission => {\n      return all_permission === permission || permissionDatas.includes(permission)\n    })\n\n    if (!hasPermission) {\n      return false\n    }\n    return true\n  } else {\n    console.error(`need roles! Like checkPermi=\"['system:user:add','system:user:edit']\"`)\n    return false\n  }\n}\n\n/**\n * 角色权限校验\n * @param {Array} value 校验值\n * @returns {Boolean}\n */\nexport function checkRole(value) {\n  if (value && value instanceof Array && value.length > 0) {\n    const roles = store.getters && store.getters.roles\n    const permissionRoles = value\n    const super_admin = \"admin\";\n\n    const hasRole = roles.some(role => {\n      return super_admin === role || permissionRoles.includes(role)\n    })\n\n    if (!hasRole) {\n      return false\n    }\n    return true\n  } else {\n    console.error(`need roles! Like checkRole=\"['admin','editor']\"`)\n    return false\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/src/utils/request.js",
    "content": "import axios from 'axios'\nimport { Notification, MessageBox, Message, Loading } from 'element-ui'\nimport store from '@/store'\nimport { getToken } from '@/utils/auth'\nimport errorCode from '@/utils/errorCode'\nimport { tansParams, blobValidate } from \"@/utils/ruoyi\";\nimport cache from '@/plugins/cache'\nimport { saveAs } from 'file-saver'\n\nlet downloadLoadingInstance;\n// 是否显示重新登录\nexport let isRelogin = { show: false };\n\naxios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'\n// 对应国际化资源文件后缀\naxios.defaults.headers['Content-Language'] = 'zh_CN'\n// 创建axios实例\nconst service = axios.create({\n  // axios中请求配置有baseURL选项，表示请求URL公共部分\n  baseURL: process.env.VUE_APP_BASE_API,\n  // 超时\n  timeout: 10000\n})\n\n// request拦截器\nservice.interceptors.request.use(config => {\n  // 是否需要设置 token\n  const isToken = (config.headers || {}).isToken === false\n  // 是否需要防止数据重复提交\n  const isRepeatSubmit = (config.headers || {}).repeatSubmit === false\n  if (getToken() && !isToken) {\n    config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改\n  }\n  // get请求映射params参数\n  if (config.method === 'get' && config.params) {\n    let url = config.url + '?' + tansParams(config.params);\n    url = url.slice(0, -1);\n    config.params = {};\n    config.url = url;\n  }\n  if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {\n    const requestObj = {\n      url: config.url,\n      data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,\n      time: new Date().getTime()\n    }\n    const sessionObj = cache.session.getJSON('sessionObj')\n    if (sessionObj === undefined || sessionObj === null || sessionObj === '') {\n      cache.session.setJSON('sessionObj', requestObj)\n    } else {\n      const s_url = sessionObj.url;                  // 请求地址\n      const s_data = sessionObj.data;                // 请求数据\n      const s_time = sessionObj.time;                // 请求时间\n      const interval = 1000;                         // 间隔时间(ms)，小于此时间视为重复提交\n      if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {\n        const message = '数据正在处理，请勿重复提交';\n        console.warn(`[${s_url}]: ` + message)\n        return Promise.reject(new Error(message))\n      } else {\n        cache.session.setJSON('sessionObj', requestObj)\n      }\n    }\n  }\n  return config\n}, error => {\n    console.log(error)\n    Promise.reject(error)\n})\n\n// 响应拦截器\nservice.interceptors.response.use(res => {\n    // 未设置状态码则默认成功状态\n    const code = res.data.code || 200;\n    // 获取错误信息\n    const msg = errorCode[code] || res.data.msg || errorCode['default']\n    // 二进制数据则直接返回\n    if (res.request.responseType ===  'blob' || res.request.responseType ===  'arraybuffer') {\n      return res.data\n    }\n    if (code === 401) {\n      if (!isRelogin.show) {\n        isRelogin.show = true;\n        MessageBox.confirm('登录状态已过期，您可以继续留在该页面，或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {\n          isRelogin.show = false;\n          store.dispatch('LogOut').then(() => {\n            location.href = process.env.VUE_APP_CONTEXT_PATH + \"index\";\n          })\n      }).catch(() => {\n        isRelogin.show = false;\n      });\n    }\n      return Promise.reject('无效的会话，或者会话已过期，请重新登录。')\n    } else if (code === 500) {\n      Message({ message: msg, type: 'error' })\n      return Promise.reject(new Error(msg))\n    } else if (code === 601) {\n      Message({ message: msg, type: 'warning' })\n      return Promise.reject('error')\n    } else if (code !== 200) {\n      Notification.error({ title: msg })\n      return Promise.reject('error')\n    } else {\n      return res.data\n    }\n  },\n  error => {\n    console.log('err' + error)\n    let { message } = error;\n    if (message == \"Network Error\") {\n      message = \"后端接口连接异常\";\n      Message({ message: message, type: 'error', duration: 5 * 1000 })\n      return Promise.reject(error)\n    } else if (message.includes(\"timeout\")) {\n      message = \"系统接口请求超时\";\n      Message({ message: message, type: 'error', duration: 5 * 1000 })\n      return Promise.reject(error)\n    } else if (message.includes(\"Request failed with status code\")) {\n      message = \"系统接口\" + message.substr(message.length - 3) + \"异常\";\n      //TODO 这种情况先不处理\n    }\n\n  }\n)\n\n// 通用下载方法\nexport function download(url, params, filename, config) {\n  downloadLoadingInstance = Loading.service({ text: \"正在下载数据，请稍候\", spinner: \"el-icon-loading\", background: \"rgba(0, 0, 0, 0.7)\", })\n  return service.post(url, params, {\n    transformRequest: [(params) => { return tansParams(params) }],\n    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n    responseType: 'blob',\n    ...config\n  }).then(async (data) => {\n    const isBlob = blobValidate(data);\n    if (isBlob) {\n      const blob = new Blob([data])\n      saveAs(blob, filename)\n    } else {\n      const resText = await data.text();\n      const rspObj = JSON.parse(resText);\n      const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']\n      Message.error(errMsg);\n    }\n    downloadLoadingInstance.close();\n  }).catch((r) => {\n    console.error(r)\n    Message.error('下载文件出现错误，请联系管理员！')\n    downloadLoadingInstance.close();\n  })\n}\n\nexport default service\n"
  },
  {
    "path": "ruoyi-ui/src/utils/ruoyi.js",
    "content": "\n\n/**\n * 通用js方法封装处理\n * Copyright (c) 2019 ruoyi\n */\n\n// 日期格式化\nexport function parseTime(time, pattern) {\n  if (arguments.length === 0 || !time) {\n    return null\n  }\n  const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'\n  let date\n  if (typeof time === 'object') {\n    date = time\n  } else {\n    if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {\n      time = parseInt(time)\n    } else if (typeof time === 'string') {\n      time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\\.[\\d]{3}/gm), '');\n    }\n    if ((typeof time === 'number') && (time.toString().length === 10)) {\n      time = time * 1000\n    }\n    date = new Date(time)\n  }\n  const formatObj = {\n    y: date.getFullYear(),\n    m: date.getMonth() + 1,\n    d: date.getDate(),\n    h: date.getHours(),\n    i: date.getMinutes(),\n    s: date.getSeconds(),\n    a: date.getDay()\n  }\n  const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {\n    let value = formatObj[key]\n    // Note: getDay() returns 0 on Sunday\n    if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }\n    if (result.length > 0 && value < 10) {\n      value = '0' + value\n    }\n    return value || 0\n  })\n  return time_str\n}\n\n// 表单重置\nexport function resetForm(refName) {\n  if (this.$refs[refName]) {\n    this.$refs[refName].resetFields();\n  }\n}\n\n// 添加日期范围\nexport function addDateRange(params, dateRange, propName) {\n  let search = params;\n  search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};\n  dateRange = Array.isArray(dateRange) ? dateRange : [];\n  if (typeof (propName) === 'undefined') {\n    search.params['beginTime'] = dateRange[0];\n    search.params['endTime'] = dateRange[1];\n  } else {\n    search.params['begin' + propName] = dateRange[0];\n    search.params['end' + propName] = dateRange[1];\n  }\n  return search;\n}\n\n// 回显数据字典\nexport function selectDictLabel(datas, value) {\n  if (value === undefined) {\n    return \"\";\n  }\n  var actions = [];\n  Object.keys(datas).some((key) => {\n    if (datas[key].value == ('' + value)) {\n      actions.push(datas[key].label);\n      return true;\n    }\n  })\n  if (actions.length === 0) {\n    actions.push(value);\n  }\n  return actions.join('');\n}\n\n// 回显数据字典（字符串、数组）\nexport function selectDictLabels(datas, value, separator) {\n  if (value === undefined || value.length ===0) {\n    return \"\";\n  }\n  if (Array.isArray(value)) {\n    value = value.join(\",\");\n  }\n  var actions = [];\n  var currentSeparator = undefined === separator ? \",\" : separator;\n  var temp = value.split(currentSeparator);\n  Object.keys(value.split(currentSeparator)).some((val) => {\n    var match = false;\n    Object.keys(datas).some((key) => {\n      if (datas[key].value == ('' + temp[val])) {\n        actions.push(datas[key].label + currentSeparator);\n        match = true;\n      }\n    })\n    if (!match) {\n      actions.push(temp[val] + currentSeparator);\n    }\n  })\n  return actions.join('').substring(0, actions.join('').length - 1);\n}\n\n// 字符串格式化(%s )\nexport function sprintf(str) {\n  var args = arguments, flag = true, i = 1;\n  str = str.replace(/%s/g, function () {\n    var arg = args[i++];\n    if (typeof arg === 'undefined') {\n      flag = false;\n      return '';\n    }\n    return arg;\n  });\n  return flag ? str : '';\n}\n\n// 转换字符串，undefined,null等转化为\"\"\nexport function parseStrEmpty(str) {\n  if (!str || str == \"undefined\" || str == \"null\") {\n    return \"\";\n  }\n  return str;\n}\n\n// 数据合并\nexport function mergeRecursive(source, target) {\n  for (var p in target) {\n    try {\n      if (target[p].constructor == Object) {\n        source[p] = mergeRecursive(source[p], target[p]);\n      } else {\n        source[p] = target[p];\n      }\n    } catch (e) {\n      source[p] = target[p];\n    }\n  }\n  return source;\n};\n\n/**\n * 构造树型结构数据\n * @param {*} data 数据源\n * @param {*} id id字段 默认 'id'\n * @param {*} parentId 父节点字段 默认 'parentId'\n * @param {*} children 孩子节点字段 默认 'children'\n */\nexport function handleTree(data, id, parentId, children) {\n  let config = {\n    id: id || 'id',\n    parentId: parentId || 'parentId',\n    childrenList: children || 'children'\n  };\n\n  var childrenListMap = {};\n  var nodeIds = {};\n  var tree = [];\n\n  for (let d of data) {\n    let parentId = d[config.parentId];\n    if (childrenListMap[parentId] == null) {\n      childrenListMap[parentId] = [];\n    }\n    nodeIds[d[config.id]] = d;\n    childrenListMap[parentId].push(d);\n  }\n\n  for (let d of data) {\n    let parentId = d[config.parentId];\n    if (nodeIds[parentId] == null) {\n      tree.push(d);\n    }\n  }\n\n  for (let t of tree) {\n    adaptToChildrenList(t);\n  }\n\n  function adaptToChildrenList(o) {\n    if (childrenListMap[o[config.id]] !== null) {\n      o[config.childrenList] = childrenListMap[o[config.id]];\n    }\n    if (o[config.childrenList]) {\n      for (let c of o[config.childrenList]) {\n        adaptToChildrenList(c);\n      }\n    }\n  }\n  return tree;\n}\n\n/**\n* 参数处理\n* @param {*} params  参数\n*/\nexport function tansParams(params) {\n  let result = ''\n  for (const propName of Object.keys(params)) {\n    const value = params[propName];\n    var part = encodeURIComponent(propName) + \"=\";\n    if (value !== null && value !== \"\" && typeof (value) !== \"undefined\") {\n      if (typeof value === 'object') {\n        for (const key of Object.keys(value)) {\n          if (value[key] !== null && value[key] !== \"\" && typeof (value[key]) !== 'undefined') {\n            let params = propName + '[' + key + ']';\n            var subPart = encodeURIComponent(params) + \"=\";\n            result += subPart + encodeURIComponent(value[key]) + \"&\";\n          }\n        }\n      } else {\n        result += part + encodeURIComponent(value) + \"&\";\n      }\n    }\n  }\n  return result\n}\n\n// 验证是否为blob格式\nexport function blobValidate(data) {\n  return data.type !== 'application/json'\n}\n"
  },
  {
    "path": "ruoyi-ui/src/utils/scroll-to.js",
    "content": "Math.easeInOutQuad = function(t, b, c, d) {\n  t /= d / 2\n  if (t < 1) {\n    return c / 2 * t * t + b\n  }\n  t--\n  return -c / 2 * (t * (t - 2) - 1) + b\n}\n\n// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts\nvar requestAnimFrame = (function() {\n  return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }\n})()\n\n/**\n * Because it's so fucking difficult to detect the scrolling element, just move them all\n * @param {number} amount\n */\nfunction move(amount) {\n  document.documentElement.scrollTop = amount\n  document.body.parentNode.scrollTop = amount\n  document.body.scrollTop = amount\n}\n\nfunction position() {\n  return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop\n}\n\n/**\n * @param {number} to\n * @param {number} duration\n * @param {Function} callback\n */\nexport function scrollTo(to, duration, callback) {\n  const start = position()\n  const change = to - start\n  const increment = 20\n  let currentTime = 0\n  duration = (typeof (duration) === 'undefined') ? 500 : duration\n  var animateScroll = function() {\n    // increment the time\n    currentTime += increment\n    // find the value with the quadratic in-out easing function\n    var val = Math.easeInOutQuad(currentTime, start, change, duration)\n    // move the document.body\n    move(val)\n    // do the animation unless its over\n    if (currentTime < duration) {\n      requestAnimFrame(animateScroll)\n    } else {\n      if (callback && typeof (callback) === 'function') {\n        // the animation is done so lets callback\n        callback()\n      }\n    }\n  }\n  animateScroll()\n}\n"
  },
  {
    "path": "ruoyi-ui/src/utils/validate.js",
    "content": "/**\n * @param {string} path\n * @returns {Boolean}\n */\nexport function isExternal(path) {\n  return /^(https?:|mailto:|tel:)/.test(path)\n}\n\n/**\n * @param {string} str\n * @returns {Boolean}\n */\nexport function validUsername(str) {\n  const valid_map = ['admin', 'editor']\n  return valid_map.indexOf(str.trim()) >= 0\n}\n\n/**\n * @param {string} url\n * @returns {Boolean}\n */\nexport function validURL(url) {\n  const reg = /^(https?|ftp):\\/\\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\\.)*[a-zA-Z0-9-]+\\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\\/($|[a-zA-Z0-9.,?'\\\\+&%$#=~_-]+))*$/\n  return reg.test(url)\n}\n\n/**\n * @param {string} str\n * @returns {Boolean}\n */\nexport function validLowerCase(str) {\n  const reg = /^[a-z]+$/\n  return reg.test(str)\n}\n\n/**\n * @param {string} str\n * @returns {Boolean}\n */\nexport function validUpperCase(str) {\n  const reg = /^[A-Z]+$/\n  return reg.test(str)\n}\n\n/**\n * @param {string} str\n * @returns {Boolean}\n */\nexport function validAlphabets(str) {\n  const reg = /^[A-Za-z]+$/\n  return reg.test(str)\n}\n\n/**\n * @param {string} email\n * @returns {Boolean}\n */\nexport function validEmail(email) {\n  const reg = /^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/\n  return reg.test(email)\n}\n\n/**\n * @param {string} str\n * @returns {Boolean}\n */\nexport function isString(str) {\n  if (typeof str === 'string' || str instanceof String) {\n    return true\n  }\n  return false\n}\n\n/**\n * @param {Array} arg\n * @returns {Boolean}\n */\nexport function isArray(arg) {\n  if (typeof Array.isArray === 'undefined') {\n    return Object.prototype.toString.call(arg) === '[object Array]'\n  }\n  return Array.isArray(arg)\n}\n"
  },
  {
    "path": "ruoyi-ui/src/views/components/icons/element-icons.js",
    "content": "const elementIcons = ['platform-eleme', 'eleme', 'delete-solid', 'delete', 's-tools', 'setting', 'user-solid', 'user', 'phone', 'phone-outline', 'more', 'more-outline', 'star-on', 'star-off', 's-goods', 'goods', 'warning', 'warning-outline', 'question', 'info', 'remove', 'circle-plus', 'success', 'error', 'zoom-in', 'zoom-out', 'remove-outline', 'circle-plus-outline', 'circle-check', 'circle-close', 's-help', 'help', 'minus', 'plus', 'check', 'close', 'picture', 'picture-outline', 'picture-outline-round', 'upload', 'upload2', 'download', 'camera-solid', 'camera', 'video-camera-solid', 'video-camera', 'message-solid', 'bell', 's-cooperation', 's-order', 's-platform', 's-fold', 's-unfold', 's-operation', 's-promotion', 's-home', 's-release', 's-ticket', 's-management', 's-open', 's-shop', 's-marketing', 's-flag', 's-comment', 's-finance', 's-claim', 's-custom', 's-opportunity', 's-data', 's-check', 's-grid', 'menu', 'share', 'd-caret', 'caret-left', 'caret-right', 'caret-bottom', 'caret-top', 'bottom-left', 'bottom-right', 'back', 'right', 'bottom', 'top', 'top-left', 'top-right', 'arrow-left', 'arrow-right', 'arrow-down', 'arrow-up', 'd-arrow-left', 'd-arrow-right', 'video-pause', 'video-play', 'refresh', 'refresh-right', 'refresh-left', 'finished', 'sort', 'sort-up', 'sort-down', 'rank', 'loading', 'view', 'c-scale-to-original', 'date', 'edit', 'edit-outline', 'folder', 'folder-opened', 'folder-add', 'folder-remove', 'folder-delete', 'folder-checked', 'tickets', 'document-remove', 'document-delete', 'document-copy', 'document-checked', 'document', 'document-add', 'printer', 'paperclip', 'takeaway-box', 'search', 'monitor', 'attract', 'mobile', 'scissors', 'umbrella', 'headset', 'brush', 'mouse', 'coordinate', 'magic-stick', 'reading', 'data-line', 'data-board', 'pie-chart', 'data-analysis', 'collection-tag', 'film', 'suitcase', 'suitcase-1', 'receiving', 'collection', 'files', 'notebook-1', 'notebook-2', 'toilet-paper', 'office-building', 'school', 'table-lamp', 'house', 'no-smoking', 'smoking', 'shopping-cart-full', 'shopping-cart-1', 'shopping-cart-2', 'shopping-bag-1', 'shopping-bag-2', 'sold-out', 'sell', 'present', 'box', 'bank-card', 'money', 'coin', 'wallet', 'discount', 'price-tag', 'news', 'guide', 'male', 'female', 'thumb', 'cpu', 'link', 'connection', 'open', 'turn-off', 'set-up', 'chat-round', 'chat-line-round', 'chat-square', 'chat-dot-round', 'chat-dot-square', 'chat-line-square', 'message', 'postcard', 'position', 'turn-off-microphone', 'microphone', 'close-notification', 'bangzhu', 'time', 'odometer', 'crop', 'aim', 'switch-button', 'full-screen', 'copy-document', 'mic', 'stopwatch', 'medal-1', 'medal', 'trophy', 'trophy-1', 'first-aid-kit', 'discover', 'place', 'location', 'location-outline', 'location-information', 'add-location', 'delete-location', 'map-location', 'alarm-clock', 'timer', 'watch-1', 'watch', 'lock', 'unlock', 'key', 'service', 'mobile-phone', 'bicycle', 'truck', 'ship', 'basketball', 'football', 'soccer', 'baseball', 'wind-power', 'light-rain', 'lightning', 'heavy-rain', 'sunrise', 'sunrise-1', 'sunset', 'sunny', 'cloudy', 'partly-cloudy', 'cloudy-and-sunny', 'moon', 'moon-night', 'dish', 'dish-1', 'food', 'chicken', 'fork-spoon', 'knife-fork', 'burger', 'tableware', 'sugar', 'dessert', 'ice-cream', 'hot-water', 'water-cup', 'coffee-cup', 'cold-drink', 'goblet', 'goblet-full', 'goblet-square', 'goblet-square-full', 'refrigerator', 'grape', 'watermelon', 'cherry', 'apple', 'pear', 'orange', 'coffee', 'ice-tea', 'ice-drink', 'milk-tea', 'potato-strips', 'lollipop', 'ice-cream-square', 'ice-cream-round']\n\nexport default elementIcons\n"
  },
  {
    "path": "ruoyi-ui/src/views/components/icons/index.vue",
    "content": "<template>\n  <div class=\"icons-container\">\n    <aside>\n      <a href=\"#\" target=\"_blank\">Add and use\n      </a>\n    </aside>\n    <el-tabs type=\"border-card\">\n      <el-tab-pane label=\"Icons\">\n        <div v-for=\"item of svgIcons\" :key=\"item\">\n          <el-tooltip placement=\"top\">\n            <div slot=\"content\">\n              {{ generateIconCode(item) }}\n            </div>\n            <div class=\"icon-item\">\n              <svg-icon :icon-class=\"item\" class-name=\"disabled\" />\n              <span>{{ item }}</span>\n            </div>\n          </el-tooltip>\n        </div>\n      </el-tab-pane>\n      <el-tab-pane label=\"Element-UI Icons\">\n        <div v-for=\"item of elementIcons\" :key=\"item\">\n          <el-tooltip placement=\"top\">\n            <div slot=\"content\">\n              {{ generateElementIconCode(item) }}\n            </div>\n            <div class=\"icon-item\">\n              <i :class=\"'el-icon-' + item\" />\n              <span>{{ item }}</span>\n            </div>\n          </el-tooltip>\n        </div>\n      </el-tab-pane>\n    </el-tabs>\n  </div>\n</template>\n\n<script>\nimport svgIcons from './svg-icons'\nimport elementIcons from './element-icons'\n\nexport default {\n  name: 'Icons',\n  data() {\n    return {\n      svgIcons,\n      elementIcons\n    }\n  },\n  methods: {\n    generateIconCode(symbol) {\n      return `<svg-icon icon-class=\"${symbol}\" />`\n    },\n    generateElementIconCode(symbol) {\n      return `<i class=\"el-icon-${symbol}\" />`\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.icons-container {\n  margin: 10px 20px 0;\n  overflow: hidden;\n\n  .icon-item {\n    margin: 20px;\n    height: 85px;\n    text-align: center;\n    width: 100px;\n    float: left;\n    font-size: 30px;\n    color: #24292e;\n    cursor: pointer;\n  }\n\n  span {\n    display: block;\n    font-size: 16px;\n    margin-top: 10px;\n  }\n\n  .disabled {\n    pointer-events: none;\n  }\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/views/components/icons/svg-icons.js",
    "content": "const req = require.context('../../../assets/icons/svg', false, /\\.svg$/)\nconst requireAll = requireContext => requireContext.keys()\n\nconst re = /\\.\\/(.*)\\.svg/\n\nconst svgIcons = requireAll(req).map(i => {\n  return i.match(re)[1]\n})\n\nexport default svgIcons\n"
  },
  {
    "path": "ruoyi-ui/src/views/dashboard/BarChart.vue",
    "content": "<template>\n  <div :class=\"className\" :style=\"{height:height,width:width}\" />\n</template>\n\n<script>\nimport * as echarts from 'echarts'\nrequire('echarts/theme/macarons') // echarts theme\nimport resize from './mixins/resize'\n\nconst animationDuration = 6000\n\nexport default {\n  mixins: [resize],\n  props: {\n    className: {\n      type: String,\n      default: 'chart'\n    },\n    width: {\n      type: String,\n      default: '100%'\n    },\n    height: {\n      type: String,\n      default: '300px'\n    }\n  },\n  data() {\n    return {\n      chart: null\n    }\n  },\n  mounted() {\n    this.$nextTick(() => {\n      this.initChart()\n    })\n  },\n  beforeDestroy() {\n    if (!this.chart) {\n      return\n    }\n    this.chart.dispose()\n    this.chart = null\n  },\n  methods: {\n    initChart() {\n      this.chart = echarts.init(this.$el, 'macarons')\n\n      this.chart.setOption({\n        tooltip: {\n          trigger: 'axis',\n          axisPointer: { // 坐标轴指示器，坐标轴触发有效\n            type: 'shadow' // 默认为直线，可选为：'line' | 'shadow'\n          }\n        },\n        grid: {\n          top: 10,\n          left: '2%',\n          right: '2%',\n          bottom: '3%',\n          containLabel: true\n        },\n        xAxis: [{\n          type: 'category',\n          data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],\n          axisTick: {\n            alignWithLabel: true\n          }\n        }],\n        yAxis: [{\n          type: 'value',\n          axisTick: {\n            show: false\n          }\n        }],\n        series: [{\n          name: 'pageA',\n          type: 'bar',\n          stack: 'vistors',\n          barWidth: '60%',\n          data: [79, 52, 200, 334, 390, 330, 220],\n          animationDuration\n        }, {\n          name: 'pageB',\n          type: 'bar',\n          stack: 'vistors',\n          barWidth: '60%',\n          data: [80, 52, 200, 334, 390, 330, 220],\n          animationDuration\n        }, {\n          name: 'pageC',\n          type: 'bar',\n          stack: 'vistors',\n          barWidth: '60%',\n          data: [30, 52, 200, 334, 390, 330, 220],\n          animationDuration\n        }]\n      })\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/dashboard/LineChart.vue",
    "content": "<template>\n  <div :class=\"className\" :style=\"{height:height,width:width}\" />\n</template>\n\n<script>\nimport * as echarts from 'echarts'\nrequire('echarts/theme/macarons') // echarts theme\nimport resize from './mixins/resize'\n\nexport default {\n  mixins: [resize],\n  props: {\n    className: {\n      type: String,\n      default: 'chart'\n    },\n    width: {\n      type: String,\n      default: '100%'\n    },\n    height: {\n      type: String,\n      default: '350px'\n    },\n    autoResize: {\n      type: Boolean,\n      default: true\n    },\n    chartData: {\n      type: Object,\n      required: true\n    }\n  },\n  data() {\n    return {\n      chart: null\n    }\n  },\n  watch: {\n    chartData: {\n      deep: true,\n      handler(val) {\n        this.setOptions(val)\n      }\n    }\n  },\n  mounted() {\n    this.$nextTick(() => {\n      this.initChart()\n    })\n  },\n  beforeDestroy() {\n    if (!this.chart) {\n      return\n    }\n    this.chart.dispose()\n    this.chart = null\n  },\n  methods: {\n    initChart() {\n      this.chart = echarts.init(this.$el, 'macarons')\n      this.setOptions(this.chartData)\n    },\n    setOptions({ expectedData, actualData } = {}) {\n      this.chart.setOption({\n        xAxis: {\n          data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],\n          boundaryGap: false,\n          axisTick: {\n            show: false\n          }\n        },\n        grid: {\n          left: 10,\n          right: 10,\n          bottom: 20,\n          top: 30,\n          containLabel: true\n        },\n        tooltip: {\n          trigger: 'axis',\n          axisPointer: {\n            type: 'cross'\n          },\n          padding: [5, 10]\n        },\n        yAxis: {\n          axisTick: {\n            show: false\n          }\n        },\n        legend: {\n          data: ['expected', 'actual']\n        },\n        series: [{\n          name: 'expected', itemStyle: {\n            normal: {\n              color: '#FF005A',\n              lineStyle: {\n                color: '#FF005A',\n                width: 2\n              }\n            }\n          },\n          smooth: true,\n          type: 'line',\n          data: expectedData,\n          animationDuration: 2800,\n          animationEasing: 'cubicInOut'\n        },\n        {\n          name: 'actual',\n          smooth: true,\n          type: 'line',\n          itemStyle: {\n            normal: {\n              color: '#3888fa',\n              lineStyle: {\n                color: '#3888fa',\n                width: 2\n              },\n              areaStyle: {\n                color: '#f3f8ff'\n              }\n            }\n          },\n          data: actualData,\n          animationDuration: 2800,\n          animationEasing: 'quadraticOut'\n        }]\n      })\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/dashboard/PanelGroup.vue",
    "content": "<template>\n  <el-row :gutter=\"40\" class=\"panel-group\">\n    <el-col :xs=\"12\" :sm=\"12\" :lg=\"6\" class=\"card-panel-col\">\n      <div class=\"card-panel\" @click=\"handleSetLineChartData('newVisitis')\">\n        <div class=\"card-panel-icon-wrapper icon-people\">\n          <svg-icon icon-class=\"peoples\" class-name=\"card-panel-icon\" />\n        </div>\n        <div class=\"card-panel-description\">\n          <div class=\"card-panel-text\">\n            访客\n          </div>\n          <count-to :start-val=\"0\" :end-val=\"102400\" :duration=\"2600\" class=\"card-panel-num\" />\n        </div>\n      </div>\n    </el-col>\n    <el-col :xs=\"12\" :sm=\"12\" :lg=\"6\" class=\"card-panel-col\">\n      <div class=\"card-panel\" @click=\"handleSetLineChartData('messages')\">\n        <div class=\"card-panel-icon-wrapper icon-message\">\n          <svg-icon icon-class=\"message\" class-name=\"card-panel-icon\" />\n        </div>\n        <div class=\"card-panel-description\">\n          <div class=\"card-panel-text\">\n            消息\n          </div>\n          <count-to :start-val=\"0\" :end-val=\"81212\" :duration=\"3000\" class=\"card-panel-num\" />\n        </div>\n      </div>\n    </el-col>\n    <el-col :xs=\"12\" :sm=\"12\" :lg=\"6\" class=\"card-panel-col\">\n      <div class=\"card-panel\" @click=\"handleSetLineChartData('purchases')\">\n        <div class=\"card-panel-icon-wrapper icon-money\">\n          <svg-icon icon-class=\"money\" class-name=\"card-panel-icon\" />\n        </div>\n        <div class=\"card-panel-description\">\n          <div class=\"card-panel-text\">\n            金额\n          </div>\n          <count-to :start-val=\"0\" :end-val=\"9280\" :duration=\"3200\" class=\"card-panel-num\" />\n        </div>\n      </div>\n    </el-col>\n    <el-col :xs=\"12\" :sm=\"12\" :lg=\"6\" class=\"card-panel-col\">\n      <div class=\"card-panel\" @click=\"handleSetLineChartData('shoppings')\">\n        <div class=\"card-panel-icon-wrapper icon-shopping\">\n          <svg-icon icon-class=\"shopping\" class-name=\"card-panel-icon\" />\n        </div>\n        <div class=\"card-panel-description\">\n          <div class=\"card-panel-text\">\n            订单\n          </div>\n          <count-to :start-val=\"0\" :end-val=\"13600\" :duration=\"3600\" class=\"card-panel-num\" />\n        </div>\n      </div>\n    </el-col>\n  </el-row>\n</template>\n\n<script>\nimport CountTo from 'vue-count-to'\n\nexport default {\n  components: {\n    CountTo\n  },\n  methods: {\n    handleSetLineChartData(type) {\n      this.$emit('handleSetLineChartData', type)\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.panel-group {\n  margin-top: 18px;\n\n  .card-panel-col {\n    margin-bottom: 32px;\n  }\n\n  .card-panel {\n    height: 108px;\n    cursor: pointer;\n    font-size: 12px;\n    position: relative;\n    overflow: hidden;\n    color: #666;\n    background: #fff;\n    box-shadow: 4px 4px 40px rgba(0, 0, 0, .05);\n    border-color: rgba(0, 0, 0, .05);\n\n    &:hover {\n      .card-panel-icon-wrapper {\n        color: #fff;\n      }\n\n      .icon-people {\n        background: #40c9c6;\n      }\n\n      .icon-message {\n        background: #36a3f7;\n      }\n\n      .icon-money {\n        background: #f4516c;\n      }\n\n      .icon-shopping {\n        background: #34bfa3\n      }\n    }\n\n    .icon-people {\n      color: #40c9c6;\n    }\n\n    .icon-message {\n      color: #36a3f7;\n    }\n\n    .icon-money {\n      color: #f4516c;\n    }\n\n    .icon-shopping {\n      color: #34bfa3\n    }\n\n    .card-panel-icon-wrapper {\n      float: left;\n      margin: 14px 0 0 14px;\n      padding: 16px;\n      transition: all 0.38s ease-out;\n      border-radius: 6px;\n    }\n\n    .card-panel-icon {\n      float: left;\n      font-size: 48px;\n    }\n\n    .card-panel-description {\n      float: right;\n      font-weight: bold;\n      margin: 26px;\n      margin-left: 0px;\n\n      .card-panel-text {\n        line-height: 18px;\n        color: rgba(0, 0, 0, 0.45);\n        font-size: 16px;\n        margin-bottom: 12px;\n      }\n\n      .card-panel-num {\n        font-size: 20px;\n      }\n    }\n  }\n}\n\n@media (max-width:550px) {\n  .card-panel-description {\n    display: none;\n  }\n\n  .card-panel-icon-wrapper {\n    float: none !important;\n    width: 100%;\n    height: 100%;\n    margin: 0 !important;\n\n    .svg-icon {\n      display: block;\n      margin: 14px auto !important;\n      float: none !important;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/views/dashboard/PieChart.vue",
    "content": "<template>\n  <div :class=\"className\" :style=\"{height:height,width:width}\" />\n</template>\n\n<script>\nimport * as echarts from 'echarts'\nrequire('echarts/theme/macarons') // echarts theme\nimport resize from './mixins/resize'\n\nexport default {\n  mixins: [resize],\n  props: {\n    className: {\n      type: String,\n      default: 'chart'\n    },\n    width: {\n      type: String,\n      default: '100%'\n    },\n    height: {\n      type: String,\n      default: '300px'\n    }\n  },\n  data() {\n    return {\n      chart: null\n    }\n  },\n  mounted() {\n    this.$nextTick(() => {\n      this.initChart()\n    })\n  },\n  beforeDestroy() {\n    if (!this.chart) {\n      return\n    }\n    this.chart.dispose()\n    this.chart = null\n  },\n  methods: {\n    initChart() {\n      this.chart = echarts.init(this.$el, 'macarons')\n\n      this.chart.setOption({\n        tooltip: {\n          trigger: 'item',\n          formatter: '{a} <br/>{b} : {c} ({d}%)'\n        },\n        legend: {\n          left: 'center',\n          bottom: '10',\n          data: ['Industries', 'Technology', 'Forex', 'Gold', 'Forecasts']\n        },\n        series: [\n          {\n            name: 'WEEKLY WRITE ARTICLES',\n            type: 'pie',\n            roseType: 'radius',\n            radius: [15, 95],\n            center: ['50%', '38%'],\n            data: [\n              { value: 320, name: 'Industries' },\n              { value: 240, name: 'Technology' },\n              { value: 149, name: 'Forex' },\n              { value: 100, name: 'Gold' },\n              { value: 59, name: 'Forecasts' }\n            ],\n            animationEasing: 'cubicInOut',\n            animationDuration: 2600\n          }\n        ]\n      })\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/dashboard/RaddarChart.vue",
    "content": "<template>\n  <div :class=\"className\" :style=\"{height:height,width:width}\" />\n</template>\n\n<script>\nimport * as echarts from 'echarts'\nrequire('echarts/theme/macarons') // echarts theme\nimport resize from './mixins/resize'\n\nconst animationDuration = 3000\n\nexport default {\n  mixins: [resize],\n  props: {\n    className: {\n      type: String,\n      default: 'chart'\n    },\n    width: {\n      type: String,\n      default: '100%'\n    },\n    height: {\n      type: String,\n      default: '300px'\n    }\n  },\n  data() {\n    return {\n      chart: null\n    }\n  },\n  mounted() {\n    this.$nextTick(() => {\n      this.initChart()\n    })\n  },\n  beforeDestroy() {\n    if (!this.chart) {\n      return\n    }\n    this.chart.dispose()\n    this.chart = null\n  },\n  methods: {\n    initChart() {\n      this.chart = echarts.init(this.$el, 'macarons')\n\n      this.chart.setOption({\n        tooltip: {\n          trigger: 'axis',\n          axisPointer: { // 坐标轴指示器，坐标轴触发有效\n            type: 'shadow' // 默认为直线，可选为：'line' | 'shadow'\n          }\n        },\n        radar: {\n          radius: '66%',\n          center: ['50%', '42%'],\n          splitNumber: 8,\n          splitArea: {\n            areaStyle: {\n              color: 'rgba(127,95,132,.3)',\n              opacity: 1,\n              shadowBlur: 45,\n              shadowColor: 'rgba(0,0,0,.5)',\n              shadowOffsetX: 0,\n              shadowOffsetY: 15\n            }\n          },\n          indicator: [\n            { name: 'Sales', max: 10000 },\n            { name: 'Administration', max: 20000 },\n            { name: 'Information Techology', max: 20000 },\n            { name: 'Customer Support', max: 20000 },\n            { name: 'Development', max: 20000 },\n            { name: 'Marketing', max: 20000 }\n          ]\n        },\n        legend: {\n          left: 'center',\n          bottom: '10',\n          data: ['Allocated Budget', 'Expected Spending', 'Actual Spending']\n        },\n        series: [{\n          type: 'radar',\n          symbolSize: 0,\n          areaStyle: {\n            normal: {\n              shadowBlur: 13,\n              shadowColor: 'rgba(0,0,0,.2)',\n              shadowOffsetX: 0,\n              shadowOffsetY: 10,\n              opacity: 1\n            }\n          },\n          data: [\n            {\n              value: [5000, 7000, 12000, 11000, 15000, 14000],\n              name: 'Allocated Budget'\n            },\n            {\n              value: [4000, 9000, 15000, 15000, 13000, 11000],\n              name: 'Expected Spending'\n            },\n            {\n              value: [5500, 11000, 12000, 15000, 12000, 12000],\n              name: 'Actual Spending'\n            }\n          ],\n          animationDuration: animationDuration\n        }]\n      })\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/dashboard/mixins/resize.js",
    "content": "import { debounce } from '@/utils'\n\nexport default {\n  data() {\n    return {\n      $_sidebarElm: null,\n      $_resizeHandler: null\n    }\n  },\n  mounted() {\n    this.initListener()\n  },\n  activated() {\n    if (!this.$_resizeHandler) {\n      // avoid duplication init\n      this.initListener()\n    }\n\n    // when keep-alive chart activated, auto resize\n    this.resize()\n  },\n  beforeDestroy() {\n    this.destroyListener()\n  },\n  deactivated() {\n    this.destroyListener()\n  },\n  methods: {\n    // use $_ for mixins properties\n    // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential\n    $_sidebarResizeHandler(e) {\n      if (e.propertyName === 'width') {\n        this.$_resizeHandler()\n      }\n    },\n    initListener() {\n      this.$_resizeHandler = debounce(() => {\n        this.resize()\n      }, 100)\n      window.addEventListener('resize', this.$_resizeHandler)\n\n      this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]\n      this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)\n    },\n    destroyListener() {\n      window.removeEventListener('resize', this.$_resizeHandler)\n      this.$_resizeHandler = null\n\n      this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)\n    },\n    resize() {\n      const { chart } = this\n      chart && chart.resize()\n    }\n  }\n}\n"
  },
  {
    "path": "ruoyi-ui/src/views/demo/demo/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"key键\" prop=\"testKey\">\n        <el-input\n          v-model=\"queryParams.testKey\"\n          placeholder=\"请输入key键\"\n          clearable\n          size=\"small\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"值\" prop=\"value\">\n        <el-input\n          v-model=\"queryParams.value\"\n          placeholder=\"请输入值\"\n          clearable\n          size=\"small\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"创建时间\">\n        <el-date-picker\n          v-model=\"daterangeCreateTime\"\n          size=\"small\"\n          style=\"width: 240px\"\n          value-format=\"yyyy-MM-dd HH:mm:ss\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"['00:00:00', '23:59:59']\"\n        ></el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handlePage\">搜索(自定义分页接口)</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['demo:demo:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['demo:demo:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['demo:demo:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"info\"\n          plain\n          icon=\"el-icon-upload2\"\n          size=\"mini\"\n          @click=\"handleImport\"\n          v-hasPermi=\"['demo:demo:import']\"\n        >导入(校验)</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['demo:demo:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"demoList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"主键\" align=\"center\" prop=\"id\" v-if=\"false\"/>\n      <el-table-column label=\"部门id\" align=\"center\" prop=\"deptId\" />\n      <el-table-column label=\"用户id\" align=\"center\" prop=\"userId\" />\n      <el-table-column label=\"排序号\" align=\"center\" prop=\"orderNum\" />\n      <el-table-column label=\"key键\" align=\"center\" prop=\"testKey\" />\n      <el-table-column label=\"值\" align=\"center\" prop=\"value\" />\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建人\" align=\"center\" prop=\"createBy\" />\n      <el-table-column label=\"更新时间\" align=\"center\" prop=\"updateTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"更新人\" align=\"center\" prop=\"updateBy\" />\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['demo:demo:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['demo:demo:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改测试单表对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"部门id\" prop=\"deptId\">\n          <el-input v-model=\"form.deptId\" placeholder=\"请输入部门id\" />\n        </el-form-item>\n        <el-form-item label=\"用户id\" prop=\"userId\">\n          <el-input v-model=\"form.userId\" placeholder=\"请输入用户id\" />\n        </el-form-item>\n        <el-form-item label=\"排序号\" prop=\"orderNum\">\n          <el-input v-model=\"form.orderNum\" placeholder=\"请输入排序号\" />\n        </el-form-item>\n        <el-form-item label=\"key键\" prop=\"testKey\">\n          <el-input v-model=\"form.testKey\" placeholder=\"请输入key键\" />\n        </el-form-item>\n        <el-form-item label=\"值\" prop=\"value\">\n          <el-input v-model=\"form.value\" placeholder=\"请输入值\" />\n        </el-form-item>\n        <el-form-item label=\"创建时间\" prop=\"createTime\">\n          <el-date-picker clearable size=\"small\"\n                          v-model=\"form.createTime\"\n                          type=\"datetime\"\n                          value-format=\"yyyy-MM-dd HH:mm:ss\"\n                          placeholder=\"选择创建时间\">\n          </el-date-picker>\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n    <!-- 用户导入对话框 -->\n    <el-dialog :title=\"upload.title\" :visible.sync=\"upload.open\" width=\"400px\" append-to-body>\n      <el-upload\n        ref=\"upload\"\n        :limit=\"1\"\n        accept=\".xlsx, .xls\"\n        :headers=\"upload.headers\"\n        :action=\"upload.url + '?updateSupport=' + upload.updateSupport\"\n        :disabled=\"upload.isUploading\"\n        :on-progress=\"handleFileUploadProgress\"\n        :on-success=\"handleFileSuccess\"\n        :auto-upload=\"false\"\n        drag\n      >\n        <i class=\"el-icon-upload\"></i>\n        <div class=\"el-upload__text\">将文件拖到此处，或<em>点击上传</em></div>\n      </el-upload>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitFileForm\">确 定</el-button>\n        <el-button @click=\"upload.open = false\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listDemo, pageDemo, getDemo, delDemo, addDemo, updateDemo } from \"@/api/demo/demo\";\nimport {getToken} from \"@/utils/auth\";\n\nexport default {\n  name: \"Demo\",\n  components: {\n  },\n  data() {\n    return {\n      //按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 测试单表表格数据\n      demoList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 创建时间时间范围\n      daterangeCreateTime: [],\n      // 用户导入参数\n      upload: {\n        // 是否显示弹出层（用户导入）\n        open: false,\n        // 弹出层标题（用户导入）\n        title: \"\",\n        // 是否禁用上传\n        isUploading: false,\n        // 设置上传的请求头部\n        headers: { Authorization: \"Bearer \" + getToken() },\n        // 上传的地址\n        url: process.env.VUE_APP_BASE_API + \"/demo/demo/importData\"\n      },\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        testKey: undefined,\n        value: undefined,\n        createTime: undefined,\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        testKey: [\n          { required: true, message: \"key键不能为空\", trigger: \"blur\" }\n        ],\n        value: [\n          { required: true, message: \"值不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询测试单表列表 */\n    getList() {\n      this.loading = true;\n      this.queryParams.params = {};\n      if (null != this.daterangeCreateTime && '' != this.daterangeCreateTime) {\n        this.queryParams.params[\"beginCreateTime\"] = this.daterangeCreateTime[0];\n        this.queryParams.params[\"endCreateTime\"] = this.daterangeCreateTime[1];\n      }\n      listDemo(this.queryParams).then(response => {\n        this.demoList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    /** 自定义分页查询 */\n    getPage() {\n      this.loading = true;\n      this.queryParams.params = {};\n      if (null != this.daterangeCreateTime && '' != this.daterangeCreateTime) {\n        this.queryParams.params[\"beginCreateTime\"] = this.daterangeCreateTime[0];\n        this.queryParams.params[\"endCreateTime\"] = this.daterangeCreateTime[1];\n      }\n      pageDemo(this.queryParams).then(response => {\n        this.demoList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        id: undefined,\n        deptId: undefined,\n        userId: undefined,\n        orderNum: undefined,\n        testKey: undefined,\n        value: undefined,\n        version: undefined,\n        createTime: undefined,\n        createBy: undefined,\n        updateTime: undefined,\n        updateBy: undefined,\n        delFlag: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 搜索按钮操作 */\n    handlePage() {\n      this.queryParams.pageNum = 1;\n      this.getPage();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.daterangeCreateTime = [];\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.id)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加测试单表\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const id = row.id || this.ids\n      getDemo(id).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改测试单表\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.id != null) {\n            updateDemo(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addDemo(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const ids = row.id || this.ids;\n      this.$modal.confirm('是否确认删除测试单表编号为\"' + ids + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delDemo(ids);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导入按钮操作 */\n    handleImport() {\n      this.upload.title = \"用户导入\";\n      this.upload.open = true;\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('demo/demo/export', {\n        ...this.queryParams\n      }, `demo_${new Date().getTime()}.xlsx`)\n    },\n    // 文件上传中处理\n    handleFileUploadProgress(event, file, fileList) {\n      this.upload.isUploading = true;\n    },\n    // 文件上传成功处理\n    handleFileSuccess(response, file, fileList) {\n      this.upload.open = false;\n      this.upload.isUploading = false;\n      this.$refs.upload.clearFiles();\n      this.$alert(response.msg, \"导入结果\", { dangerouslyUseHTMLString: true });\n      this.getList();\n    },\n    // 提交上传文件\n    submitFileForm() {\n      this.$refs.upload.submit();\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/demo/tree/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"树节点名\" prop=\"treeName\">\n        <el-input\n          v-model=\"queryParams.treeName\"\n          placeholder=\"请输入树节点名\"\n          clearable\n          size=\"small\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"创建时间\">\n        <el-date-picker\n          v-model=\"daterangeCreateTime\"\n          size=\"small\"\n          style=\"width: 240px\"\n          value-format=\"yyyy-MM-dd HH:mm:ss\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"['00:00:00', '23:59:59']\"\n        ></el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['demo:tree:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"info\"\n          plain\n          icon=\"el-icon-sort\"\n          size=\"mini\"\n          @click=\"toggleExpandAll\"\n        >展开/折叠</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table\n      v-if=\"refreshTable\"\n      v-loading=\"loading\"\n      :data=\"treeList\"\n      row-key=\"id\"\n      :default-expand-all=\"isExpandAll\"\n      :tree-props=\"{children: 'children', hasChildren: 'hasChildren'}\"\n    >\n      <el-table-column label=\"父id\" prop=\"parentId\" />\n      <el-table-column label=\"部门id\" align=\"center\" prop=\"deptId\" />\n      <el-table-column label=\"用户id\" align=\"center\" prop=\"userId\" />\n      <el-table-column label=\"树节点名\" align=\"center\" prop=\"treeName\" />\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template #default=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template #default=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['demo:tree:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-plus\"\n            @click=\"handleAdd(scope.row)\"\n            v-hasPermi=\"['demo:tree:add']\"\n          >新增</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['demo:tree:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <!-- 添加或修改测试树表对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"父id\" prop=\"parentId\">\n          <treeselect v-model=\"form.parentId\" :options=\"treeOptions\" :normalizer=\"normalizer\" placeholder=\"请选择父id\" />\n        </el-form-item>\n        <el-form-item label=\"部门id\" prop=\"deptId\">\n          <el-input v-model=\"form.deptId\" placeholder=\"请输入部门id\" />\n        </el-form-item>\n        <el-form-item label=\"用户id\" prop=\"userId\">\n          <el-input v-model=\"form.userId\" placeholder=\"请输入用户id\" />\n        </el-form-item>\n        <el-form-item label=\"树节点名\" prop=\"treeName\">\n          <el-input v-model=\"form.treeName\" placeholder=\"请输入树节点名\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listTree, getTree, delTree, addTree, updateTree } from \"@/api/demo/tree\";\nimport Treeselect from \"@riophae/vue-treeselect\";\nimport \"@riophae/vue-treeselect/dist/vue-treeselect.css\";\n\nexport default {\n  name: \"Tree\",\n  components: {\n    Treeselect\n  },\n  data() {\n    return {\n      //按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 测试树表表格数据\n      treeList: [],\n      // 测试树表树选项\n      treeOptions: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 是否展开，默认全部展开\n      isExpandAll: true,\n      // 重新渲染表格状态\n      refreshTable: true,\n      // 创建时间时间范围\n      daterangeCreateTime: [],\n      // 查询参数\n      queryParams: {\n        treeName: null,\n        createTime: null,\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        treeName: [\n          { required: true, message: \"树节点名不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询测试树表列表 */\n    getList() {\n      this.loading = true;\n      this.queryParams.params = {};\n      if (null != this.daterangeCreateTime && '' != this.daterangeCreateTime) {\n        this.queryParams.params[\"beginCreateTime\"] = this.daterangeCreateTime[0];\n        this.queryParams.params[\"endCreateTime\"] = this.daterangeCreateTime[1];\n      }\n      listTree(this.queryParams).then(response => {\n        this.treeList = this.handleTree(response.data, \"id\", \"parentId\");\n        this.loading = false;\n      });\n    },\n    /** 转换测试树表数据结构 */\n    normalizer(node) {\n      if (node.children && !node.children.length) {\n        delete node.children;\n      }\n      return {\n        id: node.id,\n        label: node.treeName,\n        children: node.children\n      };\n    },\n    /** 查询测试树表下拉树结构 */\n    getTreeselect() {\n      listTree().then(response => {\n        this.treeOptions = [];\n        const data = { id: 0, treeName: '顶级节点', children: [] };\n        data.children = this.handleTree(response.data, \"id\", \"parentId\");\n        this.treeOptions.push(data);\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        id: null,\n        parentId: null,\n        deptId: null,\n        userId: null,\n        treeName: null,\n        version: null,\n        createTime: null,\n        createBy: null,\n        updateTime: null,\n        updateBy: null,\n        delFlag: null\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.daterangeCreateTime = [];\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 新增按钮操作 */\n    handleAdd(row) {\n      this.reset();\n      this.getTreeselect();\n      if (row != null && row.id) {\n        this.form.parentId = row.id;\n      } else {\n        this.form.parentId = 0;\n      }\n      this.open = true;\n      this.title = \"添加测试树表\";\n    },\n    /** 展开/折叠操作 */\n    toggleExpandAll() {\n      this.refreshTable = false;\n      this.isExpandAll = !this.isExpandAll;\n      this.$nextTick(() => {\n        this.refreshTable = true;\n      });\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      this.getTreeselect();\n      if (row != null) {\n        this.form.parentId = row.id;\n      }\n      getTree(row.id).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改测试树表\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.id != null) {\n            updateTree(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addTree(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      this.$modal.confirm('是否确认删除测试树表编号为\"' + row.id + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delTree(row.id);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).finally(() => {\n        this.loading = false;\n      });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/error/401.vue",
    "content": "<template>\n  <div class=\"errPage-container\">\n    <el-button icon=\"arrow-left\" class=\"pan-back-btn\" @click=\"back\">\n      返回\n    </el-button>\n    <el-row>\n      <el-col :span=\"12\">\n        <h1 class=\"text-jumbo text-ginormous\">\n          401错误!\n        </h1>\n        <h2>您没有访问权限！</h2>\n        <h6>对不起，您没有访问权限，请不要进行非法操作！您可以返回主页面</h6>\n        <ul class=\"list-unstyled\">\n          <li class=\"link-type\">\n            <router-link to=\"/\">\n              回首页\n            </router-link>\n          </li>\n        </ul>\n      </el-col>\n      <el-col :span=\"12\">\n        <img :src=\"errGif\" width=\"313\" height=\"428\" alt=\"Girl has dropped her ice cream.\">\n      </el-col>\n    </el-row>\n  </div>\n</template>\n\n<script>\nimport errGif from '@/assets/401_images/401.gif'\n\nexport default {\n  name: 'Page401',\n  data() {\n    return {\n      errGif: errGif + '?' + +new Date()\n    }\n  },\n  methods: {\n    back() {\n      if (this.$route.query.noGoBack) {\n        this.$router.push({ path: '/' })\n      } else {\n        this.$router.go(-1)\n      }\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n  .errPage-container {\n    width: 800px;\n    max-width: 100%;\n    margin: 100px auto;\n    .pan-back-btn {\n      background: #008489;\n      color: #fff;\n      border: none!important;\n    }\n    .pan-gif {\n      margin: 0 auto;\n      display: block;\n    }\n    .pan-img {\n      display: block;\n      margin: 0 auto;\n      width: 100%;\n    }\n    .text-jumbo {\n      font-size: 60px;\n      font-weight: 700;\n      color: #484848;\n    }\n    .list-unstyled {\n      font-size: 14px;\n      li {\n        padding-bottom: 5px;\n      }\n      a {\n        color: #008489;\n        text-decoration: none;\n        &:hover {\n          text-decoration: underline;\n        }\n      }\n    }\n  }\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/views/error/404.vue",
    "content": "<template>\n  <div class=\"wscn-http404-container\">\n    <div class=\"wscn-http404\">\n      <div class=\"pic-404\">\n        <img class=\"pic-404__parent\" src=\"@/assets/404_images/404.png\" alt=\"404\">\n        <img class=\"pic-404__child left\" src=\"@/assets/404_images/404_cloud.png\" alt=\"404\">\n        <img class=\"pic-404__child mid\" src=\"@/assets/404_images/404_cloud.png\" alt=\"404\">\n        <img class=\"pic-404__child right\" src=\"@/assets/404_images/404_cloud.png\" alt=\"404\">\n      </div>\n      <div class=\"bullshit\">\n        <div class=\"bullshit__oops\">\n          404错误!\n        </div>\n        <div class=\"bullshit__headline\">\n          {{ message }}\n        </div>\n        <div class=\"bullshit__info\">\n          对不起，您正在寻找的页面不存在。尝试检查URL的错误，然后按浏览器上的刷新按钮或尝试在我们的应用程序中找到其他内容。\n        </div>\n        <router-link to=\"/\" class=\"bullshit__return-home\">\n          返回首页\n        </router-link>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\n\nexport default {\n  name: 'Page404',\n  computed: {\n    message() {\n      return '找不到网页！'\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.wscn-http404-container{\n  transform: translate(-50%,-50%);\n  position: absolute;\n  top: 40%;\n  left: 50%;\n}\n.wscn-http404 {\n  position: relative;\n  width: 1200px;\n  padding: 0 50px;\n  overflow: hidden;\n  .pic-404 {\n    position: relative;\n    float: left;\n    width: 600px;\n    overflow: hidden;\n    &__parent {\n      width: 100%;\n    }\n    &__child {\n      position: absolute;\n      &.left {\n        width: 80px;\n        top: 17px;\n        left: 220px;\n        opacity: 0;\n        animation-name: cloudLeft;\n        animation-duration: 2s;\n        animation-timing-function: linear;\n        animation-fill-mode: forwards;\n        animation-delay: 1s;\n      }\n      &.mid {\n        width: 46px;\n        top: 10px;\n        left: 420px;\n        opacity: 0;\n        animation-name: cloudMid;\n        animation-duration: 2s;\n        animation-timing-function: linear;\n        animation-fill-mode: forwards;\n        animation-delay: 1.2s;\n      }\n      &.right {\n        width: 62px;\n        top: 100px;\n        left: 500px;\n        opacity: 0;\n        animation-name: cloudRight;\n        animation-duration: 2s;\n        animation-timing-function: linear;\n        animation-fill-mode: forwards;\n        animation-delay: 1s;\n      }\n      @keyframes cloudLeft {\n        0% {\n          top: 17px;\n          left: 220px;\n          opacity: 0;\n        }\n        20% {\n          top: 33px;\n          left: 188px;\n          opacity: 1;\n        }\n        80% {\n          top: 81px;\n          left: 92px;\n          opacity: 1;\n        }\n        100% {\n          top: 97px;\n          left: 60px;\n          opacity: 0;\n        }\n      }\n      @keyframes cloudMid {\n        0% {\n          top: 10px;\n          left: 420px;\n          opacity: 0;\n        }\n        20% {\n          top: 40px;\n          left: 360px;\n          opacity: 1;\n        }\n        70% {\n          top: 130px;\n          left: 180px;\n          opacity: 1;\n        }\n        100% {\n          top: 160px;\n          left: 120px;\n          opacity: 0;\n        }\n      }\n      @keyframes cloudRight {\n        0% {\n          top: 100px;\n          left: 500px;\n          opacity: 0;\n        }\n        20% {\n          top: 120px;\n          left: 460px;\n          opacity: 1;\n        }\n        80% {\n          top: 180px;\n          left: 340px;\n          opacity: 1;\n        }\n        100% {\n          top: 200px;\n          left: 300px;\n          opacity: 0;\n        }\n      }\n    }\n  }\n  .bullshit {\n    position: relative;\n    float: left;\n    width: 300px;\n    padding: 30px 0;\n    overflow: hidden;\n    &__oops {\n      font-size: 32px;\n      font-weight: bold;\n      line-height: 40px;\n      color: #1482f0;\n      opacity: 0;\n      margin-bottom: 20px;\n      animation-name: slideUp;\n      animation-duration: 0.5s;\n      animation-fill-mode: forwards;\n    }\n    &__headline {\n      font-size: 20px;\n      line-height: 24px;\n      color: #222;\n      font-weight: bold;\n      opacity: 0;\n      margin-bottom: 10px;\n      animation-name: slideUp;\n      animation-duration: 0.5s;\n      animation-delay: 0.1s;\n      animation-fill-mode: forwards;\n    }\n    &__info {\n      font-size: 13px;\n      line-height: 21px;\n      color: grey;\n      opacity: 0;\n      margin-bottom: 30px;\n      animation-name: slideUp;\n      animation-duration: 0.5s;\n      animation-delay: 0.2s;\n      animation-fill-mode: forwards;\n    }\n    &__return-home {\n      display: block;\n      float: left;\n      width: 110px;\n      height: 36px;\n      background: #1482f0;\n      border-radius: 100px;\n      text-align: center;\n      color: #ffffff;\n      opacity: 0;\n      font-size: 14px;\n      line-height: 36px;\n      cursor: pointer;\n      animation-name: slideUp;\n      animation-duration: 0.5s;\n      animation-delay: 0.3s;\n      animation-fill-mode: forwards;\n    }\n    @keyframes slideUp {\n      0% {\n        transform: translateY(60px);\n        opacity: 0;\n      }\n      100% {\n        transform: translateY(0);\n        opacity: 1;\n      }\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/views/index.vue",
    "content": "<template>\n  <div class=\"app-container home\">\n    <el-row :gutter=\"20\">\n      <el-col :sm=\"24\" :lg=\"12\" style=\"padding-left: 20px\">\n        <h2>派之城后台管理系统架构选型</h2>\n        <p>\n          <br/>\n          * 前端开发框架 Vue、Element UI<br/>\n          * 后端开发框架 Spring Boot<br/>\n          * 容器框架 Undertow 基于 XNIO 的高性能容器<br/>\n          * 权限认证框架 Sa-Token、Jwt 支持多终端认证系统<br/>\n          * 关系数据库 MySQL 适配 8.X 最低 5.7<br/>\n          * 关系数据库 Oracle 适配 11g 12c<br/>\n          * 关系数据库 PostgreSQL 适配 13 14<br/>\n          * 关系数据库 SQLServer 适配 2017 2019<br/>\n          * 缓存数据库 Redis 适配 6.X 最低 4.X<br/>\n          * 数据库框架 Mybatis-Plus 快速 CRUD 增加开发效率<br/>\n          * 数据库框架 p6spy 更强劲的 SQL 分析<br/>\n          * 多数据源框架 dynamic-datasource 支持主从与多种类数据库异构<br/>\n          * 序列化框架 Jackson 统一使用 jackson 高效可靠<br/>\n          * Redis客户端 Redisson 性能强劲、API丰富<br/>\n          * 分布式限流 Redisson 全局、请求IP、集群ID 多种限流<br/>\n          * 分布式锁 Lock4j 注解锁、工具锁 多种多样<br/>\n          * 分布式幂等 Redisson 拦截重复提交<br/>\n          * 分布式链路追踪 SkyWalking 支持链路追踪、网格分析、度量聚合、可视化<br/>\n          * 分布式任务调度 Xxl-Job 高性能 高可靠 易扩展<br/>\n          * 分布式文件存储 Minio 本地存储<br/>\n          * 分布式云存储 七牛、阿里、腾讯 云存储<br/>\n          * 监控框架 SpringBoot-Admin 全方位服务监控<br/>\n          * 校验框架 Validation 增强接口安全性 严谨性<br/>\n          * Excel框架 Alibaba EasyExcel 性能优异 扩展性强<br/>\n          * 文档框架 SpringDoc、javadoc 无注解零入侵基于java注释<br/>\n          * 工具类框架 Hutool、Lombok 减少代码冗余 增加安全性<br/>\n          * 代码生成器 适配MP、SpringDoc规范化代码 一键生成前后端代码<br/>\n          * 部署方式 Docker 容器编排 一键部署业务集群<br/>\n          * 国际化 SpringMessage Spring标准国际化方案<br/>\n        </p>\n\n      </el-col>\n\n      <el-col :sm=\"24\" :lg=\"12\" style=\"padding-left: 50px\">\n        <el-row>\n          <el-col :span=\"12\">\n            <h2>技术选型</h2>\n          </el-col>\n        </el-row>\n        <el-row>\n          <el-col :span=\"6\">\n            <h4>后端技术</h4>\n            <ul>\n              <li>SpringBoot</li>\n              <li>Sa-Token</li>\n              <li>JWT</li>\n              <li>MyBatis</li>\n              <li>Druid</li>\n              <li>Jackson</li>\n              <li>...</li>\n            </ul>\n          </el-col>\n          <el-col :span=\"6\">\n            <h4>前端技术</h4>\n            <ul>\n              <li>Vue</li>\n              <li>Vuex</li>\n              <li>Element-ui</li>\n              <li>Axios</li>\n              <li>Sass</li>\n              <li>Quill</li>\n              <li>...</li>\n            </ul>\n          </el-col>\n        </el-row>\n      </el-col>\n    </el-row>\n    <el-divider />\n  </div>\n</template>\n\n<script>\nexport default {\n  name: \"Index\",\n  data() {\n    return {\n      // 版本号\n      version: \"4.7.0\",\n    };\n  },\n  methods: {\n    goTarget(href) {\n      window.open(href, \"_blank\");\n    },\n  },\n};\n</script>\n\n<style scoped lang=\"scss\">\n.home {\n  blockquote {\n    padding: 10px 20px;\n    margin: 0 0 20px;\n    font-size: 17.5px;\n    border-left: 5px solid #eee;\n  }\n  hr {\n    margin-top: 20px;\n    margin-bottom: 20px;\n    border: 0;\n    border-top: 1px solid #eee;\n  }\n  .col-item {\n    margin-bottom: 20px;\n  }\n\n  ul {\n    padding: 0;\n    margin: 0;\n  }\n\n  font-family: \"open sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 13px;\n  color: #676a6c;\n  overflow-x: hidden;\n\n  ul {\n    list-style-type: none;\n  }\n\n  h4 {\n    margin-top: 0px;\n  }\n\n  h2 {\n    margin-top: 10px;\n    font-size: 26px;\n    font-weight: 100;\n  }\n\n  p {\n    margin-top: 10px;\n\n    b {\n      font-weight: 700;\n    }\n  }\n\n  .update-log {\n    ol {\n      display: block;\n      list-style-type: decimal;\n      margin-block-start: 1em;\n      margin-block-end: 1em;\n      margin-inline-start: 0;\n      margin-inline-end: 0;\n      padding-inline-start: 40px;\n    }\n  }\n}\n</style>\n\n"
  },
  {
    "path": "ruoyi-ui/src/views/index_v1.vue",
    "content": "<template>\n  <div class=\"dashboard-editor-container\">\n\n    <panel-group @handleSetLineChartData=\"handleSetLineChartData\" />\n\n    <el-row style=\"background:#fff;padding:16px 16px 0;margin-bottom:32px;\">\n      <line-chart :chart-data=\"lineChartData\" />\n    </el-row>\n\n    <el-row :gutter=\"32\">\n      <el-col :xs=\"24\" :sm=\"24\" :lg=\"8\">\n        <div class=\"chart-wrapper\">\n          <raddar-chart />\n        </div>\n      </el-col>\n      <el-col :xs=\"24\" :sm=\"24\" :lg=\"8\">\n        <div class=\"chart-wrapper\">\n          <pie-chart />\n        </div>\n      </el-col>\n      <el-col :xs=\"24\" :sm=\"24\" :lg=\"8\">\n        <div class=\"chart-wrapper\">\n          <bar-chart />\n        </div>\n      </el-col>\n    </el-row>\n\n    \n  </div>\n</template>\n\n<script>\nimport PanelGroup from './dashboard/PanelGroup'\nimport LineChart from './dashboard/LineChart'\nimport RaddarChart from './dashboard/RaddarChart'\nimport PieChart from './dashboard/PieChart'\nimport BarChart from './dashboard/BarChart'\n\nconst lineChartData = {\n  newVisitis: {\n    expectedData: [100, 120, 161, 134, 105, 160, 165],\n    actualData: [120, 82, 91, 154, 162, 140, 145]\n  },\n  messages: {\n    expectedData: [200, 192, 120, 144, 160, 130, 140],\n    actualData: [180, 160, 151, 106, 145, 150, 130]\n  },\n  purchases: {\n    expectedData: [80, 100, 121, 104, 105, 90, 100],\n    actualData: [120, 90, 100, 138, 142, 130, 130]\n  },\n  shoppings: {\n    expectedData: [130, 140, 141, 142, 145, 150, 160],\n    actualData: [120, 82, 91, 154, 162, 140, 130]\n  }\n}\n\nexport default {\n  name: 'Index',\n  components: {\n    PanelGroup,\n    LineChart,\n    RaddarChart,\n    PieChart,\n    BarChart\n  },\n  data() {\n    return {\n      lineChartData: lineChartData.newVisitis\n    }\n  },\n  methods: {\n    handleSetLineChartData(type) {\n      this.lineChartData = lineChartData[type]\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.dashboard-editor-container {\n  padding: 32px;\n  background-color: rgb(240, 242, 245);\n  position: relative;\n\n  .chart-wrapper {\n    background: #fff;\n    padding: 16px 16px 0;\n    margin-bottom: 32px;\n  }\n}\n\n@media (max-width:1024px) {\n  .chart-wrapper {\n    padding: 8px;\n  }\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/views/login.vue",
    "content": "<template>\n  <div class=\"login\">\n    <el-form ref=\"loginForm\" :model=\"loginForm\" :rules=\"loginRules\" class=\"login-form\">\n      <h3 class=\"title\">派之城后台管理系统</h3>\n      <el-form-item prop=\"username\">\n        <el-input\n          v-model=\"loginForm.username\"\n          type=\"text\"\n          auto-complete=\"off\"\n          placeholder=\"账号\"\n        >\n          <svg-icon slot=\"prefix\" icon-class=\"user\" class=\"el-input__icon input-icon\" />\n        </el-input>\n      </el-form-item>\n      <el-form-item prop=\"password\">\n        <el-input\n          v-model=\"loginForm.password\"\n          type=\"password\"\n          auto-complete=\"off\"\n          placeholder=\"密码\"\n          @keyup.enter.native=\"handleLogin\"\n        >\n          <svg-icon slot=\"prefix\" icon-class=\"password\" class=\"el-input__icon input-icon\" />\n        </el-input>\n      </el-form-item>\n      <el-form-item prop=\"code\" v-if=\"captchaEnabled\">\n        <el-input\n          v-model=\"loginForm.code\"\n          auto-complete=\"off\"\n          placeholder=\"验证码\"\n          style=\"width: 63%\"\n          @keyup.enter.native=\"handleLogin\"\n        >\n          <svg-icon slot=\"prefix\" icon-class=\"validCode\" class=\"el-input__icon input-icon\" />\n        </el-input>\n        <div class=\"login-code\">\n          <img :src=\"codeUrl\" @click=\"getCode\" class=\"login-code-img\"/>\n        </div>\n      </el-form-item>\n      <el-checkbox v-model=\"loginForm.rememberMe\" style=\"margin:0px 0px 25px 0px;\">记住密码</el-checkbox>\n      <el-form-item style=\"width:100%;\">\n        <el-button\n          :loading=\"loading\"\n          size=\"medium\"\n          type=\"primary\"\n          style=\"width:100%;\"\n          @click.native.prevent=\"handleLogin\"\n        >\n          <span v-if=\"!loading\">登 录</span>\n          <span v-else>登 录 中...</span>\n        </el-button>\n        <div style=\"float: right;\" v-if=\"register\">\n          <router-link class=\"link-type\" :to=\"'/register'\">立即注册</router-link>\n        </div>\n      </el-form-item>\n    </el-form>\n    <!--  底部  -->\n    <div class=\"el-login-footer\">\n      <span>Copyright © 2018-2023 疯狂的狮子Li All Rights Reserved.</span>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { getCodeImg } from \"@/api/login\";\nimport Cookies from \"js-cookie\";\nimport { encrypt, decrypt } from '@/utils/jsencrypt'\n\nexport default {\n  name: \"Login\",\n  data() {\n    return {\n      codeUrl: \"\",\n      loginForm: {\n        username: \"admin\",\n        password: \"admin123\",\n        rememberMe: false,\n        code: \"\",\n        uuid: \"\"\n      },\n      loginRules: {\n        username: [\n          { required: true, trigger: \"blur\", message: \"请输入您的账号\" }\n        ],\n        password: [\n          { required: true, trigger: \"blur\", message: \"请输入您的密码\" }\n        ],\n        code: [{ required: true, trigger: \"change\", message: \"请输入验证码\" }]\n      },\n      loading: false,\n      // 验证码开关\n      captchaEnabled: true,\n      // 注册开关\n      register: false,\n      redirect: undefined\n    };\n  },\n  watch: {\n    $route: {\n      handler: function(route) {\n        this.redirect = route.query && route.query.redirect;\n      },\n      immediate: true\n    }\n  },\n  created() {\n    this.getCode();\n    this.getCookie();\n  },\n  methods: {\n    getCode() {\n      getCodeImg().then(res => {\n        this.captchaEnabled = res.data.captchaEnabled === undefined ? true : res.data.captchaEnabled;\n        if (this.captchaEnabled) {\n          this.codeUrl = \"data:image/gif;base64,\" + res.data.img;\n          this.loginForm.uuid = res.data.uuid;\n        }\n      });\n    },\n    getCookie() {\n      const username = Cookies.get(\"username\");\n      const password = Cookies.get(\"password\");\n      const rememberMe = Cookies.get('rememberMe')\n      this.loginForm = {\n        username: username === undefined ? this.loginForm.username : username,\n        password: password === undefined ? this.loginForm.password : decrypt(password),\n        rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)\n      };\n    },\n    handleLogin() {\n      this.$refs.loginForm.validate(valid => {\n        if (valid) {\n          this.loading = true;\n          if (this.loginForm.rememberMe) {\n            Cookies.set(\"username\", this.loginForm.username, { expires: 30 });\n            Cookies.set(\"password\", encrypt(this.loginForm.password), { expires: 30 });\n            Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 });\n          } else {\n            Cookies.remove(\"username\");\n            Cookies.remove(\"password\");\n            Cookies.remove('rememberMe');\n          }\n          this.$store.dispatch(\"Login\", this.loginForm).then(() => {\n            this.$router.push({ path: this.redirect || \"/\" }).catch(()=>{});\n          }).catch(() => {\n            this.loading = false;\n            if (this.captchaEnabled) {\n              this.getCode();\n            }\n          });\n        }\n      });\n    }\n  }\n};\n</script>\n\n<style rel=\"stylesheet/scss\" lang=\"scss\">\n.login {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  height: 100%;\n  background-image: url(\"../assets/images/login-background.jpg\");\n  background-size: cover;\n}\n.title {\n  margin: 0px auto 30px auto;\n  text-align: center;\n  color: #707070;\n}\n\n.login-form {\n  border-radius: 6px;\n  background: #ffffff;\n  width: 400px;\n  padding: 25px 25px 5px 25px;\n  .el-input {\n    height: 38px;\n    input {\n      height: 38px;\n    }\n  }\n  .input-icon {\n    height: 39px;\n    width: 14px;\n    margin-left: 2px;\n  }\n}\n.login-tip {\n  font-size: 13px;\n  text-align: center;\n  color: #bfbfbf;\n}\n.login-code {\n  width: 33%;\n  height: 38px;\n  float: right;\n  img {\n    cursor: pointer;\n    vertical-align: middle;\n  }\n}\n.el-login-footer {\n  height: 40px;\n  line-height: 40px;\n  position: fixed;\n  bottom: 0;\n  width: 100%;\n  text-align: center;\n  color: #fff;\n  font-family: Arial;\n  font-size: 12px;\n  letter-spacing: 1px;\n}\n.login-code-img {\n  height: 38px;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/views/monitor/admin/index.vue",
    "content": "<template>\n  <i-frame :src=\"url\" />\n</template>\n<script>\nimport iFrame from \"@/components/iFrame/index\";\nexport default {\n  name: \"Admin\",\n  components: { iFrame },\n  data() {\n    return {\n      url: process.env.VUE_APP_MONITRO_ADMIN\n    };\n  },\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/monitor/cache/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-row>\n      <el-col :span=\"24\" class=\"card-box\">\n        <el-card>\n          <div slot=\"header\"><span><i class=\"el-icon-monitor\"></i> 基本信息</span></div>\n          <div class=\"el-table el-table--enable-row-hover el-table--medium\">\n            <table cellspacing=\"0\" style=\"width: 100%\">\n              <tbody>\n                <tr>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\">Redis版本</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\" v-if=\"cache.info\">{{ cache.info.redis_version }}</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\">运行模式</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\" v-if=\"cache.info\">{{ cache.info.redis_mode == \"standalone\" ? \"单机\" : \"集群\" }}</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\">端口</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\" v-if=\"cache.info\">{{ cache.info.tcp_port }}</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\">客户端数</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\" v-if=\"cache.info\">{{ cache.info.connected_clients }}</div></td>\n                </tr>\n                <tr>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\">运行时间(天)</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\" v-if=\"cache.info\">{{ cache.info.uptime_in_days }}</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\">使用内存</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\" v-if=\"cache.info\">{{ cache.info.used_memory_human }}</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\">使用CPU</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\" v-if=\"cache.info\">{{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }}</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\">内存配置</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\" v-if=\"cache.info\">{{ cache.info.maxmemory_human }}</div></td>\n                </tr>\n                <tr>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\">AOF是否开启</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\" v-if=\"cache.info\">{{ cache.info.aof_enabled == \"0\" ? \"否\" : \"是\" }}</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\">RDB是否成功</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\" v-if=\"cache.info\">{{ cache.info.rdb_last_bgsave_status }}</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\">Key数量</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\" v-if=\"cache.dbSize\">{{ cache.dbSize }} </div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\">网络入口/出口</div></td>\n                  <td class=\"el-table__cell is-leaf\"><div class=\"cell\" v-if=\"cache.info\">{{ cache.info.instantaneous_input_kbps }}kps/{{cache.info.instantaneous_output_kbps}}kps</div></td>\n                </tr>\n              </tbody>\n            </table>\n          </div>\n        </el-card>\n      </el-col>\n\n      <el-col :span=\"12\" class=\"card-box\">\n        <el-card>\n          <div slot=\"header\"><span><i class=\"el-icon-pie-chart\"></i> 命令统计</span></div>\n          <div class=\"el-table el-table--enable-row-hover el-table--medium\">\n            <div ref=\"commandstats\" style=\"height: 420px\" />\n          </div>\n        </el-card>\n      </el-col>\n\n      <el-col :span=\"12\" class=\"card-box\">\n        <el-card>\n          <div slot=\"header\"><span><i class=\"el-icon-odometer\"></i> 内存信息</span></div>\n          <div class=\"el-table el-table--enable-row-hover el-table--medium\">\n            <div ref=\"usedmemory\" style=\"height: 420px\" />\n          </div>\n        </el-card>\n      </el-col>\n    </el-row>\n  </div>\n</template>\n\n<script>\nimport { getCache } from \"@/api/monitor/cache\";\nimport * as echarts from \"echarts\";\n\nexport default {\n  name: \"Cache\",\n  data() {\n    return {\n      // 统计命令信息\n      commandstats: null,\n      // 使用内存\n      usedmemory: null,\n      // cache信息\n      cache: []\n    }\n  },\n  created() {\n    this.getList();\n    this.openLoading();\n  },\n  methods: {\n    /** 查缓存询信息 */\n    getList() {\n      getCache().then((response) => {\n        this.cache = response.data;\n        this.$modal.closeLoading();\n\n        this.commandstats = echarts.init(this.$refs.commandstats, \"macarons\");\n        this.commandstats.setOption({\n          tooltip: {\n            trigger: \"item\",\n            formatter: \"{a} <br/>{b} : {c} ({d}%)\",\n          },\n          series: [\n            {\n              name: \"命令\",\n              type: \"pie\",\n              roseType: \"radius\",\n              radius: [15, 95],\n              center: [\"50%\", \"38%\"],\n              data: response.data.commandStats,\n              animationEasing: \"cubicInOut\",\n              animationDuration: 1000,\n            }\n          ]\n        });\n        this.usedmemory = echarts.init(this.$refs.usedmemory, \"macarons\");\n        this.usedmemory.setOption({\n          tooltip: {\n            formatter: \"{b} <br/>{a} : \" + this.cache.info.used_memory_human,\n          },\n          series: [\n            {\n              name: \"峰值\",\n              type: \"gauge\",\n              min: 0,\n              max: 1000,\n              detail: {\n                formatter: this.cache.info.used_memory_human,\n              },\n              data: [\n                {\n                  value: parseFloat(this.cache.info.used_memory_human),\n                  name: \"内存消耗\",\n                }\n              ]\n            }\n          ]\n        });\n      });\n    },\n    // 打开加载层\n    openLoading() {\n      this.$modal.loading(\"正在加载缓存监控数据，请稍候！\");\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/monitor/cache/list.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-row :gutter=\"10\">\n      <el-col :span=\"8\">\n        <el-card style=\"height: calc(100vh - 125px)\">\n          <div slot=\"header\">\n            <span><i class=\"el-icon-collection\"></i> 缓存列表</span>\n            <el-button\n              style=\"float: right; padding: 3px 0\"\n              type=\"text\"\n              icon=\"el-icon-refresh-right\"\n              @click=\"refreshCacheNames()\"\n            ></el-button>\n          </div>\n          <el-table\n            v-loading=\"loading\"\n            :data=\"cacheNames\"\n            :height=\"tableHeight\"\n            highlight-current-row\n            @row-click=\"getCacheKeys\"\n            style=\"width: 100%\"\n          >\n            <el-table-column\n              label=\"序号\"\n              width=\"60\"\n              type=\"index\"\n            ></el-table-column>\n\n            <el-table-column\n              label=\"缓存名称\"\n              align=\"center\"\n              prop=\"cacheName\"\n              :show-overflow-tooltip=\"true\"\n              :formatter=\"nameFormatter\"\n            ></el-table-column>\n\n            <el-table-column\n              label=\"备注\"\n              align=\"center\"\n              prop=\"remark\"\n              :show-overflow-tooltip=\"true\"\n            />\n            <el-table-column\n              label=\"操作\"\n              width=\"60\"\n              align=\"center\"\n              class-name=\"small-padding fixed-width\"\n            >\n              <template slot-scope=\"scope\">\n                <el-button\n                  size=\"mini\"\n                  type=\"text\"\n                  icon=\"el-icon-delete\"\n                  @click=\"handleClearCacheName(scope.row)\"\n                ></el-button>\n              </template>\n            </el-table-column>\n          </el-table>\n        </el-card>\n      </el-col>\n\n      <el-col :span=\"8\">\n        <el-card style=\"height: calc(100vh - 125px)\">\n          <div slot=\"header\">\n            <span><i class=\"el-icon-key\"></i> 键名列表</span>\n            <el-button\n              style=\"float: right; padding: 3px 0\"\n              type=\"text\"\n              icon=\"el-icon-refresh-right\"\n              @click=\"refreshCacheKeys()\"\n            ></el-button>\n          </div>\n          <el-table\n            v-loading=\"subLoading\"\n            :data=\"cacheKeys\"\n            :height=\"tableHeight\"\n            highlight-current-row\n            @row-click=\"handleCacheValue\"\n            style=\"width: 100%\"\n          >\n            <el-table-column\n              label=\"序号\"\n              width=\"60\"\n              type=\"index\"\n            ></el-table-column>\n            <el-table-column\n              label=\"缓存键名\"\n              align=\"center\"\n              :show-overflow-tooltip=\"true\"\n              :formatter=\"keyFormatter\"\n            >\n            </el-table-column>\n            <el-table-column\n              label=\"操作\"\n              width=\"60\"\n              align=\"center\"\n              class-name=\"small-padding fixed-width\"\n            >\n              <template slot-scope=\"scope\">\n                <el-button\n                  size=\"mini\"\n                  type=\"text\"\n                  icon=\"el-icon-delete\"\n                  @click=\"handleClearCacheKey(scope.row)\"\n                ></el-button>\n              </template>\n            </el-table-column>\n          </el-table>\n        </el-card>\n      </el-col>\n\n      <el-col :span=\"8\">\n        <el-card :bordered=\"false\" style=\"height: calc(100vh - 125px)\">\n          <div slot=\"header\">\n            <span><i class=\"el-icon-document\"></i> 缓存内容</span>\n            <el-button\n              style=\"float: right; padding: 3px 0\"\n              type=\"text\"\n              icon=\"el-icon-refresh-right\"\n              @click=\"handleClearCacheAll()\"\n              >清理全部</el-button\n            >\n          </div>\n          <el-form :model=\"cacheForm\">\n            <el-row :gutter=\"32\">\n              <el-col :offset=\"1\" :span=\"22\">\n                <el-form-item label=\"缓存名称:\" prop=\"cacheName\">\n                  <el-input v-model=\"cacheForm.cacheName\" :readOnly=\"true\" />\n                </el-form-item>\n              </el-col>\n              <el-col :offset=\"1\" :span=\"22\">\n                <el-form-item label=\"缓存键名:\" prop=\"cacheKey\">\n                  <el-input v-model=\"cacheForm.cacheKey\" :readOnly=\"true\" />\n                </el-form-item>\n              </el-col>\n              <el-col :offset=\"1\" :span=\"22\">\n                <el-form-item label=\"缓存内容:\" prop=\"cacheValue\">\n                  <el-input\n                    v-model=\"cacheForm.cacheValue\"\n                    type=\"textarea\"\n                    :rows=\"8\"\n                    :readOnly=\"true\"\n                  />\n                </el-form-item>\n              </el-col>\n            </el-row>\n          </el-form>\n        </el-card>\n      </el-col>\n    </el-row>\n  </div>\n</template>\n\n<script>\nimport { listCacheName, listCacheKey, getCacheValue, clearCacheName, clearCacheKey, clearCacheAll } from \"@/api/monitor/cache\";\n\nexport default {\n  name: \"CacheList\",\n  data() {\n    return {\n      cacheNames: [],\n      cacheKeys: [],\n      cacheForm: {},\n      loading: true,\n      subLoading: false,\n      nowCacheName: \"\",\n      tableHeight: window.innerHeight - 200\n    };\n  },\n  created() {\n    this.getCacheNames();\n  },\n  methods: {\n    /** 查询缓存名称列表 */\n    getCacheNames() {\n      this.loading = true;\n      listCacheName().then(response => {\n        this.cacheNames = response.data;\n        this.loading = false;\n      });\n    },\n    /** 刷新缓存名称列表 */\n    refreshCacheNames() {\n      this.getCacheNames();\n      this.$modal.msgSuccess(\"刷新缓存列表成功\");\n    },\n    /** 清理指定名称缓存 */\n    handleClearCacheName(row) {\n      clearCacheName(row.cacheName).then(response => {\n        this.$modal.msgSuccess(\"清理缓存名称[\" + row.cacheName + \"]成功\");\n        this.getCacheKeys();\n      });\n    },\n    /** 查询缓存键名列表 */\n    getCacheKeys(row) {\n      const cacheName = row !== undefined ? row.cacheName : this.nowCacheName;\n      if (cacheName === \"\") {\n        return;\n      }\n      this.subLoading = true;\n      listCacheKey(cacheName).then(response => {\n        this.cacheKeys = response.data;\n        this.subLoading = false;\n        this.nowCacheName = cacheName;\n      });\n    },\n    /** 刷新缓存键名列表 */\n    refreshCacheKeys() {\n      this.getCacheKeys();\n      this.$modal.msgSuccess(\"刷新键名列表成功\");\n    },\n    /** 清理指定键名缓存 */\n    handleClearCacheKey(cacheKey) {\n      clearCacheKey(this.nowCacheName, cacheKey).then(response => {\n        this.$modal.msgSuccess(\"清理缓存键名[\" + cacheKey + \"]成功\");\n        this.getCacheKeys();\n      });\n    },\n    /** 列表前缀去除 */\n    nameFormatter(row) {\n      return row.cacheName.replace(\":\", \"\");\n    },\n    /** 键名前缀去除 */\n    keyFormatter(cacheKey) {\n      return cacheKey.replace(this.nowCacheName, \"\");\n    },\n    /** 查询缓存内容详细 */\n    handleCacheValue(cacheKey) {\n      getCacheValue(this.nowCacheName, cacheKey).then(response => {\n        this.cacheForm = response.data;\n      });\n    },\n    /** 清理全部缓存 */\n    handleClearCacheAll() {\n      clearCacheAll().then(response => {\n        this.$modal.msgSuccess(\"清理全部缓存成功\");\n      });\n    }\n  },\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/monitor/logininfor/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"登录地址\" prop=\"ipaddr\">\n        <el-input\n          v-model=\"queryParams.ipaddr\"\n          placeholder=\"请输入登录地址\"\n          clearable\n          style=\"width: 240px;\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"用户名称\" prop=\"userName\">\n        <el-input\n          v-model=\"queryParams.userName\"\n          placeholder=\"请输入用户名称\"\n          clearable\n          style=\"width: 240px;\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          v-model=\"queryParams.status\"\n          placeholder=\"登录状态\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <el-option\n            v-for=\"dict in dict.type.sys_common_status\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"登录时间\">\n        <el-date-picker\n          v-model=\"dateRange\"\n          style=\"width: 240px\"\n          value-format=\"yyyy-MM-dd HH:mm:ss\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"['00:00:00', '23:59:59']\"\n        ></el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['monitor:logininfor:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          @click=\"handleClean\"\n          v-hasPermi=\"['monitor:logininfor:remove']\"\n        >清空</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-unlock\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUnlock\"\n          v-hasPermi=\"['monitor:logininfor:unlock']\"\n        >解锁</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['monitor:logininfor:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table ref=\"tables\" v-loading=\"loading\" :data=\"list\" @selection-change=\"handleSelectionChange\" :default-sort=\"defaultSort\" @sort-change=\"handleSortChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"访问编号\" align=\"center\" prop=\"infoId\" />\n      <el-table-column label=\"用户名称\" align=\"center\" prop=\"userName\" :show-overflow-tooltip=\"true\" sortable=\"custom\" :sort-orders=\"['descending', 'ascending']\" />\n      <el-table-column label=\"登录地址\" align=\"center\" prop=\"ipaddr\" width=\"130\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"登录地点\" align=\"center\" prop=\"loginLocation\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"浏览器\" align=\"center\" prop=\"browser\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"操作系统\" align=\"center\" prop=\"os\" />\n      <el-table-column label=\"登录状态\" align=\"center\" prop=\"status\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.sys_common_status\" :value=\"scope.row.status\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作信息\" align=\"center\" prop=\"msg\" />\n      <el-table-column label=\"登录日期\" align=\"center\" prop=\"loginTime\" sortable=\"custom\" :sort-orders=\"['descending', 'ascending']\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.loginTime) }}</span>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n  </div>\n</template>\n\n<script>\nimport { list, delLogininfor, cleanLogininfor, unlockLogininfor } from \"@/api/monitor/logininfor\";\n\nexport default {\n  name: \"Logininfor\",\n  dicts: ['sys_common_status'],\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 选择用户名\n      selectName: \"\",\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 表格数据\n      list: [],\n      // 日期范围\n      dateRange: [],\n      // 默认排序\n      defaultSort: {prop: 'loginTime', order: 'descending'},\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        ipaddr: undefined,\n        userName: undefined,\n        status: undefined\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询登录日志列表 */\n    getList() {\n      this.loading = true;\n      list(this.addDateRange(this.queryParams, this.dateRange)).then(response => {\n          this.list = response.rows;\n          this.total = response.total;\n          this.loading = false;\n        }\n      );\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.dateRange = [];\n      this.resetForm(\"queryForm\");\n      this.queryParams.pageNum = 1;\n      this.$refs.tables.sort(this.defaultSort.prop, this.defaultSort.order)\n    },\n    /** 多选框选中数据 */\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.infoId)\n      this.single = selection.length!=1\n      this.multiple = !selection.length\n      this.selectName = selection.map(item => item.userName);\n    },\n    /** 排序触发事件 */\n    handleSortChange(column, prop, order) {\n      this.queryParams.orderByColumn = column.prop;\n      this.queryParams.isAsc = column.order;\n      this.getList();\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const infoIds = row.infoId || this.ids;\n      this.$modal.confirm('是否确认删除访问编号为\"' + infoIds + '\"的数据项？').then(function() {\n        return delLogininfor(infoIds);\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {});\n    },\n    /** 清空按钮操作 */\n    handleClean() {\n      this.$modal.confirm('是否确认清空所有登录日志数据项？').then(function() {\n        return cleanLogininfor();\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"清空成功\");\n      }).catch(() => {});\n    },\n    /** 解锁按钮操作 */\n    handleUnlock() {\n      const username = this.selectName;\n      this.$modal.confirm('是否确认解锁用户\"' + username + '\"数据项?').then(function() {\n        return unlockLogininfor(username);\n      }).then(() => {\n        this.$modal.msgSuccess(\"用户\" + username + \"解锁成功\");\n      }).catch(() => {});\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('monitor/logininfor/export', {\n        ...this.queryParams\n      }, `logininfor_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n\n"
  },
  {
    "path": "ruoyi-ui/src/views/monitor/online/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" label-width=\"68px\">\n      <el-form-item label=\"登录地址\" prop=\"ipaddr\">\n        <el-input\n          v-model=\"queryParams.ipaddr\"\n          placeholder=\"请输入登录地址\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"用户名称\" prop=\"userName\">\n        <el-input\n          v-model=\"queryParams.userName\"\n          placeholder=\"请输入用户名称\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n\n    </el-form>\n    <el-table\n      v-loading=\"loading\"\n      :data=\"list.slice((pageNum-1)*pageSize,pageNum*pageSize)\"\n      style=\"width: 100%;\"\n    >\n      <el-table-column label=\"序号\" type=\"index\" align=\"center\">\n        <template slot-scope=\"scope\">\n          <span>{{(pageNum - 1) * pageSize + scope.$index + 1}}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"会话编号\" align=\"center\" prop=\"tokenId\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"登录名称\" align=\"center\" prop=\"userName\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"部门名称\" align=\"center\" prop=\"deptName\" />\n      <el-table-column label=\"主机\" align=\"center\" prop=\"ipaddr\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"登录地点\" align=\"center\" prop=\"loginLocation\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"浏览器\" align=\"center\" prop=\"browser\" />\n      <el-table-column label=\"操作系统\" align=\"center\" prop=\"os\" />\n      <el-table-column label=\"登录时间\" align=\"center\" prop=\"loginTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.loginTime) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleForceLogout(scope.row)\"\n            v-hasPermi=\"['monitor:online:forceLogout']\"\n          >强退</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination v-show=\"total>0\" :total=\"total\" :page.sync=\"pageNum\" :limit.sync=\"pageSize\" />\n  </div>\n</template>\n\n<script>\nimport { list, forceLogout } from \"@/api/monitor/online\";\n\nexport default {\n  name: \"Online\",\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 总条数\n      total: 0,\n      // 表格数据\n      list: [],\n      pageNum: 1,\n      pageSize: 10,\n      // 查询参数\n      queryParams: {\n        ipaddr: undefined,\n        userName: undefined\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询登录日志列表 */\n    getList() {\n      this.loading = true;\n      list(this.queryParams).then(response => {\n        this.list = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 强退按钮操作 */\n    handleForceLogout(row) {\n      this.$modal.confirm('是否确认强退名称为\"' + row.userName + '\"的用户？').then(function() {\n        return forceLogout(row.tokenId);\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"强退成功\");\n      }).catch(() => {});\n    }\n  }\n};\n</script>\n\n"
  },
  {
    "path": "ruoyi-ui/src/views/monitor/operlog/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"系统模块\" prop=\"title\">\n        <el-input\n          v-model=\"queryParams.title\"\n          placeholder=\"请输入系统模块\"\n          clearable\n          style=\"width: 240px;\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"操作人员\" prop=\"operName\">\n        <el-input\n          v-model=\"queryParams.operName\"\n          placeholder=\"请输入操作人员\"\n          clearable\n          style=\"width: 240px;\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"类型\" prop=\"businessType\">\n        <el-select\n          v-model=\"queryParams.businessType\"\n          placeholder=\"操作类型\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <el-option\n            v-for=\"dict in dict.type.sys_oper_type\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          v-model=\"queryParams.status\"\n          placeholder=\"操作状态\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <el-option\n            v-for=\"dict in dict.type.sys_common_status\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"操作时间\">\n        <el-date-picker\n          v-model=\"dateRange\"\n          style=\"width: 240px\"\n          value-format=\"yyyy-MM-dd HH:mm:ss\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"['00:00:00', '23:59:59']\"\n        ></el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['monitor:operlog:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          @click=\"handleClean\"\n          v-hasPermi=\"['monitor:operlog:remove']\"\n        >清空</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['monitor:operlog:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table ref=\"tables\" v-loading=\"loading\" :data=\"list\" @selection-change=\"handleSelectionChange\" :default-sort=\"defaultSort\" @sort-change=\"handleSortChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"日志编号\" align=\"center\" prop=\"operId\" />\n      <el-table-column label=\"系统模块\" align=\"center\" prop=\"title\" />\n      <el-table-column label=\"操作类型\" align=\"center\" prop=\"businessType\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.sys_oper_type\" :value=\"scope.row.businessType\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"请求方式\" align=\"center\" prop=\"requestMethod\" />\n      <el-table-column label=\"操作人员\" align=\"center\" prop=\"operName\" width=\"100\" :show-overflow-tooltip=\"true\" sortable=\"custom\" :sort-orders=\"['descending', 'ascending']\" />\n      <el-table-column label=\"操作地址\" align=\"center\" prop=\"operIp\" width=\"130\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"操作地点\" align=\"center\" prop=\"operLocation\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"操作状态\" align=\"center\" prop=\"status\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.sys_common_status\" :value=\"scope.row.status\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作日期\" align=\"center\" prop=\"operTime\" sortable=\"custom\" :sort-orders=\"['descending', 'ascending']\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.operTime) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-view\"\n            @click=\"handleView(scope.row,scope.index)\"\n            v-hasPermi=\"['monitor:operlog:query']\"\n          >详细</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 操作日志详细 -->\n    <el-dialog title=\"操作日志详细\" :visible.sync=\"open\" width=\"700px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" label-width=\"100px\" size=\"mini\">\n        <el-row>\n          <el-col :span=\"12\">\n            <el-form-item label=\"操作模块：\">{{ form.title }} / {{ typeFormat(form) }}</el-form-item>\n            <el-form-item\n              label=\"登录信息：\"\n            >{{ form.operName }} / {{ form.operIp }} / {{ form.operLocation }}</el-form-item>\n          </el-col>\n          <el-col :span=\"12\">\n            <el-form-item label=\"请求地址：\">{{ form.operUrl }}</el-form-item>\n            <el-form-item label=\"请求方式：\">{{ form.requestMethod }}</el-form-item>\n          </el-col>\n          <el-col :span=\"24\">\n            <el-form-item label=\"操作方法：\">{{ form.method }}</el-form-item>\n          </el-col>\n          <el-col :span=\"24\">\n            <el-form-item label=\"请求参数：\">{{ form.operParam }}</el-form-item>\n          </el-col>\n          <el-col :span=\"24\">\n            <el-form-item label=\"返回参数：\">{{ form.jsonResult }}</el-form-item>\n          </el-col>\n          <el-col :span=\"12\">\n            <el-form-item label=\"操作状态：\">\n              <div v-if=\"form.status === 0\">正常</div>\n              <div v-else-if=\"form.status === 1\">失败</div>\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\">\n            <el-form-item label=\"操作时间：\">{{ parseTime(form.operTime) }}</el-form-item>\n          </el-col>\n          <el-col :span=\"24\">\n            <el-form-item label=\"异常信息：\" v-if=\"form.status === 1\">{{ form.errorMsg }}</el-form-item>\n          </el-col>\n        </el-row>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button @click=\"open = false\">关 闭</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { list, delOperlog, cleanOperlog } from \"@/api/monitor/operlog\";\n\nexport default {\n  name: \"Operlog\",\n  dicts: ['sys_oper_type', 'sys_common_status'],\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 表格数据\n      list: [],\n      // 是否显示弹出层\n      open: false,\n      // 日期范围\n      dateRange: [],\n      // 默认排序\n      defaultSort: {prop: 'operTime', order: 'descending'},\n      // 表单参数\n      form: {},\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        title: undefined,\n        operName: undefined,\n        businessType: undefined,\n        status: undefined\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询登录日志 */\n    getList() {\n      this.loading = true;\n      list(this.addDateRange(this.queryParams, this.dateRange)).then( response => {\n          this.list = response.rows;\n          this.total = response.total;\n          this.loading = false;\n        }\n      );\n    },\n    // 操作日志类型字典翻译\n    typeFormat(row, column) {\n      return this.selectDictLabel(this.dict.type.sys_oper_type, row.businessType);\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.dateRange = [];\n      this.resetForm(\"queryForm\");\n      this.queryParams.pageNum = 1;\n      this.$refs.tables.sort(this.defaultSort.prop, this.defaultSort.order)\n    },\n    /** 多选框选中数据 */\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.operId)\n      this.multiple = !selection.length\n    },\n    /** 排序触发事件 */\n    handleSortChange(column, prop, order) {\n      this.queryParams.orderByColumn = column.prop;\n      this.queryParams.isAsc = column.order;\n      this.getList();\n    },\n    /** 详细按钮操作 */\n    handleView(row) {\n      this.open = true;\n      this.form = row;\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const operIds = row.operId || this.ids;\n      this.$modal.confirm('是否确认删除日志编号为\"' + operIds + '\"的数据项？').then(function() {\n        return delOperlog(operIds);\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {});\n    },\n    /** 清空按钮操作 */\n    handleClean() {\n      this.$modal.confirm('是否确认清空所有操作日志数据项？').then(function() {\n        return cleanOperlog();\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"清空成功\");\n      }).catch(() => {});\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('monitor/operlog/export', {\n        ...this.queryParams\n      }, `operlog_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n\n"
  },
  {
    "path": "ruoyi-ui/src/views/monitor/xxljob/index.vue",
    "content": "<template>\n  <i-frame :src=\"url\" />\n</template>\n<script>\nimport iFrame from \"@/components/iFrame/index\";\nexport default {\n  name: \"XxlJob\",\n  components: { iFrame },\n  data() {\n    return {\n      url: process.env.VUE_APP_XXL_JOB_ADMIN\n    };\n  },\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/redirect.vue",
    "content": "<script>\nexport default {\n  created() {\n    const { params, query } = this.$route\n    const { path } = params\n    this.$router.replace({ path: '/' + path, query })\n  },\n  render: function(h) {\n    return h() // avoid warning message\n  }\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/register.vue",
    "content": "<template>\n  <div class=\"register\">\n    <el-form ref=\"registerForm\" :model=\"registerForm\" :rules=\"registerRules\" class=\"register-form\">\n      <h3 class=\"title\">派之城后台管理系统</h3>\n      <el-form-item prop=\"username\">\n        <el-input v-model=\"registerForm.username\" type=\"text\" auto-complete=\"off\" placeholder=\"账号\">\n          <svg-icon slot=\"prefix\" icon-class=\"user\" class=\"el-input__icon input-icon\" />\n        </el-input>\n      </el-form-item>\n      <el-form-item prop=\"password\">\n        <el-input\n          v-model=\"registerForm.password\"\n          type=\"password\"\n          auto-complete=\"off\"\n          placeholder=\"密码\"\n          @keyup.enter.native=\"handleRegister\"\n        >\n          <svg-icon slot=\"prefix\" icon-class=\"password\" class=\"el-input__icon input-icon\" />\n        </el-input>\n      </el-form-item>\n      <el-form-item prop=\"confirmPassword\">\n        <el-input\n          v-model=\"registerForm.confirmPassword\"\n          type=\"password\"\n          auto-complete=\"off\"\n          placeholder=\"确认密码\"\n          @keyup.enter.native=\"handleRegister\"\n        >\n          <svg-icon slot=\"prefix\" icon-class=\"password\" class=\"el-input__icon input-icon\" />\n        </el-input>\n      </el-form-item>\n      <el-form-item prop=\"code\" v-if=\"captchaEnabled\">\n        <el-input\n          v-model=\"registerForm.code\"\n          auto-complete=\"off\"\n          placeholder=\"验证码\"\n          style=\"width: 63%\"\n          @keyup.enter.native=\"handleRegister\"\n        >\n          <svg-icon slot=\"prefix\" icon-class=\"validCode\" class=\"el-input__icon input-icon\" />\n        </el-input>\n        <div class=\"register-code\">\n          <img :src=\"codeUrl\" @click=\"getCode\" class=\"register-code-img\"/>\n        </div>\n      </el-form-item>\n      <el-form-item style=\"width:100%;\">\n        <el-button\n          :loading=\"loading\"\n          size=\"medium\"\n          type=\"primary\"\n          style=\"width:100%;\"\n          @click.native.prevent=\"handleRegister\"\n        >\n          <span v-if=\"!loading\">注 册</span>\n          <span v-else>注 册 中...</span>\n        </el-button>\n        <div style=\"float: right;\">\n          <router-link class=\"link-type\" :to=\"'/login'\">使用已有账户登录</router-link>\n        </div>\n      </el-form-item>\n    </el-form>\n    <!--  底部  -->\n    <div class=\"el-register-footer\">\n      <span>Copyright © 2018-2023 疯狂的狮子Li All Rights Reserved.</span>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { getCodeImg, register } from \"@/api/login\";\n\nexport default {\n  name: \"Register\",\n  data() {\n    const equalToPassword = (rule, value, callback) => {\n      if (this.registerForm.password !== value) {\n        callback(new Error(\"两次输入的密码不一致\"));\n      } else {\n        callback();\n      }\n    };\n    return {\n      codeUrl: \"\",\n      registerForm: {\n        username: \"\",\n        password: \"\",\n        confirmPassword: \"\",\n        code: \"\",\n        uuid: \"\",\n        userType: \"sys_user\"\n      },\n      registerRules: {\n        username: [\n          { required: true, trigger: \"blur\", message: \"请输入您的账号\" },\n          { min: 2, max: 20, message: '用户账号长度必须介于 2 和 20 之间', trigger: 'blur' }\n        ],\n        password: [\n          { required: true, trigger: \"blur\", message: \"请输入您的密码\" },\n          { min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' }\n        ],\n        confirmPassword: [\n          { required: true, trigger: \"blur\", message: \"请再次输入您的密码\" },\n          { required: true, validator: equalToPassword, trigger: \"blur\" }\n        ],\n        code: [{ required: true, trigger: \"change\", message: \"请输入验证码\" }]\n      },\n      loading: false,\n      captchaEnabled: true\n    };\n  },\n  created() {\n    this.getCode();\n  },\n  methods: {\n    getCode() {\n      getCodeImg().then(res => {\n        this.captchaEnabled = res.data.captchaEnabled === undefined ? true : res.data.captchaEnabled;\n        if (this.captchaEnabled) {\n          this.codeUrl = \"data:image/gif;base64,\" + res.data.img;\n          this.registerForm.uuid = res.data.uuid;\n        }\n      });\n    },\n    handleRegister() {\n      this.$refs.registerForm.validate(valid => {\n        if (valid) {\n          this.loading = true;\n          let registerForm = this.registerForm;\n          registerForm.userType = \"sys_user\"\n          register(registerForm).then(res => {\n            const username = this.registerForm.username;\n            this.$alert(\"<font color='red'>恭喜你，您的账号 \" + username + \" 注册成功！</font>\", '系统提示', {\n              dangerouslyUseHTMLString: true,\n              type: 'success'\n            }).then(() => {\n              this.$router.push(\"/login\");\n            }).catch(() => {});\n          }).catch(() => {\n            this.loading = false;\n            if (this.captchaEnabled) {\n              this.getCode();\n            }\n          })\n        }\n      });\n    }\n  }\n};\n</script>\n\n<style rel=\"stylesheet/scss\" lang=\"scss\">\n.register {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  height: 100%;\n  background-image: url(\"../assets/images/login-background.jpg\");\n  background-size: cover;\n}\n.title {\n  margin: 0px auto 30px auto;\n  text-align: center;\n  color: #707070;\n}\n\n.register-form {\n  border-radius: 6px;\n  background: #ffffff;\n  width: 400px;\n  padding: 25px 25px 5px 25px;\n  .el-input {\n    height: 38px;\n    input {\n      height: 38px;\n    }\n  }\n  .input-icon {\n    height: 39px;\n    width: 14px;\n    margin-left: 2px;\n  }\n}\n.register-tip {\n  font-size: 13px;\n  text-align: center;\n  color: #bfbfbf;\n}\n.register-code {\n  width: 33%;\n  height: 38px;\n  float: right;\n  img {\n    cursor: pointer;\n    vertical-align: middle;\n  }\n}\n.el-register-footer {\n  height: 40px;\n  line-height: 40px;\n  position: fixed;\n  bottom: 0;\n  width: 100%;\n  text-align: center;\n  color: #fff;\n  font-family: Arial;\n  font-size: 12px;\n  letter-spacing: 1px;\n}\n.register-code-img {\n  height: 38px;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/activity/add/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" label-width=\"120px\" :rules=\"rules\">\n      <el-form-item label=\"活动标题\" prop=\"title\">\n        <el-input\n          v-model=\"queryParams.title\"\n          placeholder=\"请输入活动标题\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"地址\" prop=\"address\">\n        <el-input\n          v-model=\"queryParams.address\"\n          placeholder=\"请输入地址\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <!--form-item 放在一列-->\n      <div class=\"flex\">\n        <el-form-item label=\"所在城市\" prop=\"regionId\">\n          <el-select filterable v-model=\"queryParams.regionId\" placeholder=\"请选择\">\n            <el-option\n              v-for=\"item in addressList\"\n              :key=\"item.regionId\"\n              :label=\"item.name\"\n              :value=\"item.regionId\">\n            </el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"分类\" prop=\"classify\">\n          <el-select v-model=\"queryParams.classify\" placeholder=\"请选择\">\n            <el-option\n              v-for=\"item in classifyList\"\n              :key=\"item.value\"\n              :label=\"item.label\"\n              :value=\"item.value\">\n            </el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"地区\" prop=\"classify\">\n          <el-select filterable v-model=\"queryParams.region\" placeholder=\"请选择\">\n            <el-option\n              v-for=\"item in region\"\n              :key=\"item.value\"\n              :label=\"item.label\"\n              :value=\"item.value\">\n            </el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"艺人\" prop=\"artistList\">\n          <el-select filterable v-model=\"queryParams.artistList\" placeholder=\"请选择\" multiple>\n            <el-option\n              v-for=\"item in listArtist\"\n              :key=\"item.artistId\"\n              :label=\"item.name\"\n              :value=\"item.artistId\">\n            </el-option>\n          </el-select>\n        </el-form-item>\n      </div>\n      <div class=\"flex\">\n        <el-form-item label=\"开始时间\" prop=\"startTime\">\n          <el-date-picker\n            v-model=\"queryParams.startTime\"\n            type=\"datetime\"\n            placeholder=\"选择日期\"\n            value-format=\"yyyy-MM-dd HH:mm:ss\"\n          >\n          </el-date-picker>\n        </el-form-item>\n        <el-form-item label=\"结束时间\" prop=\"endDate\">\n          <el-date-picker\n            v-model=\"queryParams.endDate\"\n            type=\"datetime\"\n            placeholder=\"选择日期\"\n            value-format=\"yyyy-MM-dd HH:mm:ss\"\n          ></el-date-picker>\n        </el-form-item>\n        <!--        <el-form-item label=\"售票结束时间\" prop=\"saleEndTime\">-->\n        <!--          <el-date-picker-->\n        <!--            v-model=\"queryParams.saleEndTime\"-->\n        <!--            type=\"datetime\"-->\n        <!--            placeholder=\"选择日期\"-->\n        <!--            value-format=\"yyyy-MM-dd HH:mm:ss\"-->\n        <!--          >-->\n        <!--          </el-date-picker>-->\n        <!--        </el-form-item>-->\n        <el-form-item label=\"展示时间\" prop=\"showTime\">\n          <el-date-picker\n            v-model=\"queryParams.showTime\"\n            type=\"datetime\"\n            placeholder=\"选择日期\"\n            value-format=\"yyyy-MM-dd HH:mm:ss\"\n          >\n          </el-date-picker>\n        </el-form-item>\n      </div>\n      <el-form-item label=\"活动主办方\" prop=\"organizerLists\">\n        <el-select filterable v-model=\"queryParams.organizerLists\" placeholder=\"请选择\">\n          <el-option\n            v-for=\"item in listOrganizer\"\n            :key=\"item.organizerId\"\n            :label=\"item.name\"\n            :value=\"item.organizerId\">\n          </el-option>\n        </el-select>\n      </el-form-item>\n      <el-form-item\n        v-for=\"(item, index) in queryParams.organizerTickets\"\n        :label=\"'票务' + (index + 1)\"\n        :key=\"index\"\n      >\n        <label>名称：</label>\n        <el-input v-model=\"item.name\" class=\"input\"></el-input>\n        <label>购票二维码：</label>\n        <image-upload v-model=\"item.qrImage\"/>\n        <label>Logo：</label>\n        <image-upload v-model=\"item.logoImage\"/>\n        <el-button @click.prevent=\"deleteTicket(index)\">删除</el-button>\n      </el-form-item>\n      <el-form-item>\n        <el-button @click=\"addTicket\">新增票务</el-button>\n      </el-form-item>\n      <div class=\"flex\">\n        <el-form-item label=\"舞台介绍\" prop=\"introList\">\n          <el-select filterable v-model=\"queryParams.stageList\" placeholder=\"请选择\" multiple>\n            <el-option\n              v-for=\"item in wtIntro\"\n              :key=\"item.introId\"\n              :label=\"item.title\"\n              :value=\"item.introId\">\n            </el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"更多介绍\" prop=\"introList\">\n          <el-select filterable v-model=\"queryParams.introList\" placeholder=\"请选择\" multiple>\n            <el-option\n              v-for=\"item in listIntro\"\n              :key=\"item.introId\"\n              :label=\"item.title\"\n              :value=\"item.introId\">\n            </el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"活动标签\" prop=\"tagList\">\n          <el-select v-model=\"queryParams.tagList\" placeholder=\"请选择\" multiple>\n            <el-option\n              v-for=\"item in listTag\"\n              :key=\"item.tagId\"\n              :label=\"item.name\"\n              :value=\"item.tagId\">\n            </el-option>\n          </el-select>\n        </el-form-item>\n      </div>\n      <el-form-item label=\"活动详情主图\" prop=\"innerImage\">\n        <image-upload v-model=\"queryParams.innerImage\"/>\n      </el-form-item>\n      <el-form-item label=\"活动封面\" prop=\"coverImage\">\n        <image-upload v-model=\"queryParams.coverImage\"/>\n      </el-form-item>\n      <el-form-item label=\"活动海报\" prop=\"shareImage\">\n        <image-upload v-model=\"queryParams.shareImage\"/>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" @click=\"onSubmit\" :loading=\"buttonLoading\">{{ submitText }}</el-button>\n        <el-button>取消</el-button>\n      </el-form-item>\n    </el-form>\n  </div>\n</template>\n\n<script>\nimport {addActivity, getActivity, getAddressList, updateActivity} from \"@/api/system/activity\";\nimport {listArtist} from \"@/api/system/artist\";\nimport {listIntro} from \"@/api/system/intro\";\nimport {listTag} from \"@/api/system/tag\";\nimport {listOrganizer} from \"@/api/system/organizer\";\nimport dayjs from \"dayjs\";\n\nexport default {\n  data() {\n    return {\n      // 提交按钮文案\n      submitText: '立即创建',\n      // 分类列表\n      classifyList: [\n        {value: 0, label: '电音节'},\n        {value: 1, label: '派对'},\n      ],\n      region: [\n        {value: 0, label: \"国际\"},\n        {value: 1, label: '国内'}\n      ],\n      // 地址列表\n      addressList: [],\n      // 艺人列表\n      listArtist: [],\n      // 活动介绍列表\n      listIntro: [],\n      wtIntro: [],\n      // 标签列表\n      listTag: [],\n      // 主办方列表\n      listOrganizer: [],\n      // 提交按钮loading\n      buttonLoading: false,\n      // 查询参数\n      queryParams: {\n        artistList: [],\n        introList: [],\n        tagList: [],\n        organizerLists: null,\n        organizerTickets: [\n          {name: '', qrImage: '', logoImage: ''}\n        ],\n        stageList: [],\n        address: undefined,\n        region: undefined,\n        regionId: undefined,\n        classify: undefined,\n        saleEndTime: '2023/8/4 12:00:00',\n        title: undefined,\n        startTime: undefined,\n        endDate: undefined,\n        innerImage: undefined,\n        showTime: undefined,\n        coverImage: undefined,\n        shareImage: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n      },\n      // 表单校验\n      rules: {\n        address: [\n          {required: true, message: \"地址不能为空\", trigger: \"blur\"}\n        ],\n        regionId: [\n          {required: true, message: \"城市ID不能为空\", trigger: \"blur\"}\n        ],\n        title: [\n          {required: true, message: \"活动标题不能为空\", trigger: \"blur\"}\n        ],\n        classify: [\n          {required: true, message: \"分类不能为空\", trigger: \"blur\"}\n        ],\n        region: [\n          {required: true, message: \"地区不能为空\", trigger: \"blur\"}\n        ],\n        artistList: [\n          {required: true, message: \"艺人不能为空\", trigger: \"blur\"}\n        ],\n        // saleEndTime: [\n        //   {required: true, message: \"售票结束时间不能为空\", trigger: \"blur\"}\n        // ],\n        startTime: [\n          {required: true, message: \"开始时间不能为空\", trigger: \"blur\"}\n        ],\n        endDate: [\n          {required: true, message: \"结束时间不能为空\", trigger: \"blur\"}\n        ],\n        innerImage: [\n          {required: true, message: \"活动详情主图不能为空\", trigger: \"blur\"}\n        ],\n        showTime: [\n          {required: true, message: \"展示时间不能为空\", trigger: \"blur\"}\n        ],\n        coverImage: [\n          {required: true, message: \"封面图片不能为空\", trigger: \"blur\"}\n        ],\n        organizerLists: [\n          {required: true, message: \"主办方不能为空\", trigger: \"blur\"}\n        ],\n        introList: [\n          {required: true, message: \"活动介绍不能为空\", trigger: \"blur\"}\n        ],\n        tagList: [\n          {required: true, message: \"活动标签不能为空\", trigger: \"blur\"}\n        ],\n        organizerTickets: [\n          {required: true, message: \"主办方票务不能为空\", trigger: \"blur\"}\n        ],\n        shareImage: [\n          {required: true, message: \"活动海报不能为空\", trigger: \"blur\"}\n        ],\n      }\n    }\n  },\n  methods: {\n    // 删除票务\n    deleteTicket(index) {\n      this.queryParams.organizerTickets.splice(index, 1)\n    },\n    // 添加票务\n    addTicket() {\n      this.queryParams.organizerTickets.push({\n        name: \"\",\n        qrImage: \"\",\n        logoImage: \"\",\n      })\n    },\n    /*获取详情数据*/\n    async getDetail(activityId) {\n      if (activityId === undefined) return\n      const data = await getActivity(activityId)\n      this.queryParams = {\n        ...data.data,\n        organizerLists: data.data.organizerList.organizerId,\n        organizerTickets: data.data.organizerList.organizerTickets,\n        tagList: data.data.tagList.map(item => item.tagId),\n        introList: data.data.introList.map(item => item.introId),\n        stageList: data.data.stageList.map(item => item.introId),\n        artistList: data.data.artistList.map(item => item.artistId),\n\n      }\n      this.submitText = '立即修改'\n    },\n    /*查询地址列表*/\n    async handleQuery() {\n      const data = await getAddressList()\n      this.addressList = data.data['全部']\n    },\n    /*查询艺人列表*/\n    async queryArtistList() {\n      const data = await listArtist()\n      this.listArtist = data.rows\n    },\n    /*查询活动介绍列表*/\n    async queryListIntro() {\n      const data = await listIntro()\n      data.rows.forEach(item => {\n        if (item.type === 0) {\n          this.wtIntro.push(item)\n        }\n        if (item.type === 1) {\n          this.listIntro.push(item)\n        }\n      })\n      // this.listIntro = data.rows\n    },\n    /*查询标签列表*/\n    async queryListTag() {\n      const data = await listTag()\n      this.listTag = data.rows\n    },\n    /*查询活动主办方*/\n    async queryListOrganizer() {\n      const data = await listOrganizer()\n      this.listOrganizer = data.rows\n    },\n    /*创建活动*/\n    onSubmit() {\n      const now = dayjs().format('YYYY-MM-DD HH:mm:ss')\n\n      this.$refs[\"queryForm\"].validate(valid => {\n        this.queryParams.artistList = this.queryParams.artistList.map(item => ({artistId: item}))\n        this.queryParams.introList = this.queryParams.introList.map(item => ({introId: item}))\n        this.queryParams.tagList = this.queryParams.tagList.map(item => ({tagId: item}))\n        this.queryParams.stageList = this.queryParams.stageList.map(item => ({introId: item}))\n        this.buttonLoading = true;\n        const organizerId = this.queryParams.organizerLists\n        const organizer = this.listOrganizer.find(item => item.organizerId === organizerId)\n        delete organizer.organizerId\n        const organizerList = {\n          ...organizer,\n          organizerId: organizerId,\n          organizerTickets: this.queryParams.organizerTickets\n        }\n\n        if (this.queryParams.activityId) {\n          updateActivity({...this.queryParams, updateTime: now, organizerList}).then(response => {\n            this.$modal.msgSuccess(\"修改成功\");\n            //跳转到活动列表页面 并刷新活动列表\n            this.$router.push({path: '/pzcActivity/activity'})\n          })\n            .finally(() => {\n              this.buttonLoading = false;\n            });\n        } else {\n          addActivity({...this.queryParams, createTime: now, organizerList}).then(response => {\n            this.$modal.msgSuccess(\"新增成功\");\n            //跳转到活动列表页面 并刷新活动列表\n            this.$router.push({path: '/pzcActivity/activity'})\n\n\n          }).finally(() => {\n            this.buttonLoading = false;\n          });\n        }\n      })\n    },\n  },\n  created() {\n    this.handleQuery()\n    this.queryArtistList()\n    this.queryListIntro()\n    this.queryListTag()\n    this.queryListOrganizer()\n    const activityId = this.$route.query.activityId\n    this.queryParams.activityId = activityId\n    this.getDetail(activityId)\n  }\n}\n</script>\n\n<style>\n.flex {\n  display: flex;\n  align-items: center;\n  flex-wrap: wrap;\n}\n\n.input {\n  display: block;\n  width: 120px;\n  margin-right: 10px;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/activity/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"地址\" prop=\"address\">\n        <el-input\n          v-model=\"queryParams.address\"\n          placeholder=\"请输入地址\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"城市ID\" prop=\"regionId\">\n        <el-input\n          v-model=\"queryParams.regionId\"\n          placeholder=\"请输入城市ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"活动标题\" prop=\"title\">\n        <el-input\n          v-model=\"queryParams.title\"\n          placeholder=\"请输入活动标题\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"开始时间\" prop=\"startTime\">\n        <el-input\n          v-model=\"queryParams.startTime\"\n          placeholder=\"请输入开始时间\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"结束时间\" prop=\"endDate\">\n        <el-input\n          v-model=\"queryParams.endDate\"\n          placeholder=\"请输入结束时间\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"活动详情主图\" prop=\"innerImage\">\n        <el-input\n          v-model=\"queryParams.innerImage\"\n          placeholder=\"请输入活动详情主图\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"展示时间\" prop=\"showTime\">\n        <el-input\n          v-model=\"queryParams.showTime\"\n          placeholder=\"请输入展示时间\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"创建时间\">\n        <el-date-picker\n          v-model=\"daterangeCreateTime\"\n          style=\"width: 240px\"\n          value-format=\"yyyy-MM-dd HH:mm:ss\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"['00:00:00', '23:59:59']\"\n        ></el-date-picker>\n      </el-form-item>\n      <el-form-item label=\"更新时间\">\n        <el-date-picker\n          v-model=\"daterangeUpdateTime\"\n          style=\"width: 240px\"\n          value-format=\"yyyy-MM-dd HH:mm:ss\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"['00:00:00', '23:59:59']\"\n        ></el-date-picker>\n      </el-form-item>\n      <el-form-item label=\"删除状态，默认为1表示正常状态\" prop=\"state\">\n        <el-input\n          v-model=\"queryParams.state\"\n          placeholder=\"请输入删除状态，默认为1表示正常状态\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:activity:add']\"\n        >新增\n        </el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:activity:edit']\"\n        >修改\n        </el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:activity:remove']\"\n        >删除\n        </el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:activity:export']\"\n        >导出\n        </el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"activityList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\"/>\n      <el-table-column label=\"活动id\" align=\"center\" prop=\"activityId\" v-if=\"true\"/>\n      <el-table-column label=\"活动标题\" align=\"center\" prop=\"title\"/>\n      <el-table-column label=\"活动类型\" align=\"center\" prop=\"classify\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.activity_type\" :value=\"scope.row.classify\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"地址\" align=\"center\" prop=\"address\"/>\n      <el-table-column label=\"城市ID\" align=\"center\" prop=\"regionId\"/>\n      <el-table-column label=\"开始时间\" align=\"center\" prop=\"startTime\"/>\n      <el-table-column label=\"结束时间\" align=\"center\" prop=\"endDate\"/>\n      <el-table-column label=\"活动详情主图\" align=\"center\" prop=\"innerImage\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <image-preview :src=\"scope.row.innerImage\" :width=\"50\" :height=\"50\"/>\n        </template>\n      </el-table-column>\n\n      <el-table-column label=\"封面图片\" align=\"center\" prop=\"coverImage\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <image-preview :src=\"scope.row.coverImage\" :width=\"50\" :height=\"50\"/>\n        </template>\n      </el-table-column>\n\n      <el-table-column label=\"海报图片\" align=\"center\" prop=\"shareImage\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <image-preview :src=\"scope.row.shareImage\" :width=\"50\" :height=\"50\"/>\n        </template>\n      </el-table-column>\n\n      <el-table-column label=\"展示时间\" align=\"center\" prop=\"showTime\"/>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\"/>\n      <el-table-column label=\"更新时间\" align=\"center\" prop=\"updateTime\"/>\n\n      <!--      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">-->\n      <!--        <template slot-scope=\"scope\">-->\n      <!--          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>-->\n      <!--        </template>-->\n      <!--      </el-table-column>-->\n      <!--      <el-table-column label=\"更新时间\" align=\"center\" prop=\"updateTime\" width=\"180\">-->\n      <!--        <template slot-scope=\"scope\">-->\n      <!--          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>-->\n      <!--        </template>-->\n      <!--      </el-table-column>-->\n      <!--      <el-table-column label=\"删除状态，默认为1表示正常状态\" align=\"center\" prop=\"state\"/>-->\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:activity:edit']\"\n          >修改\n          </el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:activity:remove']\"\n          >删除\n          </el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改活动对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"600px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"活动地址\" prop=\"address\">\n          <el-input v-model=\"form.address\" placeholder=\"请选择活动地址\"/>\n        </el-form-item>\n        <el-form-item label=\"城市ID\" prop=\"regionId\">\n          <el-input v-model=\"form.regionId\" placeholder=\"请输入城市ID\"/>\n        </el-form-item>\n        <el-form-item label=\"活动标题\" prop=\"title\">\n          <el-input v-model=\"form.title\" placeholder=\"请输入活动标题\"/>\n        </el-form-item>\n        <el-form-item label=\"活动起止时间\" prop=\"startTime\">\n          <!--          <el-input v-model=\"form.startTime\" placeholder=\"请输入开始时间\"/>-->\n\n          <el-date-picker\n            v-model=\"form.rangeTime\"\n            type=\"datetimerange\"\n            range-separator=\"至\"\n            start-placeholder=\"活动开始时间\"\n            end-placeholder=\"活动结束时间\"\n            value-format=\"yyyy-MM-dd HH:mm:ss\"\n          >\n          </el-date-picker>\n        </el-form-item>\n        <!--        <el-form-item label=\"结束时间\" prop=\"endDate\">-->\n        <!--          <el-input v-model=\"form.endDate\" placeholder=\"请输入结束时间\"/>-->\n        <!--        </el-form-item>-->\n        <el-form-item label=\"活动详情主图\" prop=\"innerImage\">\n          <el-input v-model=\"form.innerImage\" placeholder=\"请输入活动详情主图\"/>\n        </el-form-item>\n        <el-form-item label=\"展示时间\" prop=\"showTime\">\n          <el-input v-model=\"form.showTime\" placeholder=\"请输入展示时间\"/>\n        </el-form-item>\n        <el-form-item label=\"封面图片\" prop=\"coverImage\">\n          <image-upload v-model=\"form.coverImage\"/>\n        </el-form-item>\n        <!--        <el-form-item label=\"删除状态，默认为1表示正常状态\" prop=\"state\">-->\n        <!--          <el-input v-model=\"form.state\" placeholder=\"请输入删除状态，默认为1表示正常状态\"/>-->\n        <!--        </el-form-item>-->\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport {listActivity, getActivity, delActivity, addActivity, updateActivity} from \"@/api/system/activity\";\n\nexport default {\n  name: \"Activity\",\n  dicts: ['activity_type'],\n  data() { //这里data()定义为函数的原因是，如果是对象的话，会导致多个组件共享一个data，从而导致数据错乱\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 活动表格数据\n      activityList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 删除状态，默认为1表示正常状态时间范围\n      daterangeCreateTime: [],\n      // 删除状态，默认为1表示正常状态时间范围\n      daterangeUpdateTime: [],\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        address: undefined,\n        regionId: undefined,\n        title: undefined,\n        classify: undefined,\n        rangeTime: [],\n        startTime: undefined,\n        endDate: undefined,\n        innerImage: undefined,\n        showTime: undefined,\n        coverImage: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n        state: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        activityId: [\n          {required: true, message: \"活动id不能为空\", trigger: \"blur\"}\n        ],\n        address: [\n          {required: true, message: \"地址不能为空\", trigger: \"blur\"}\n        ],\n        regionId: [\n          {required: true, message: \"城市ID不能为空\", trigger: \"blur\"}\n        ],\n        title: [\n          {required: true, message: \"活动标题不能为空\", trigger: \"blur\"}\n        ],\n        startTime: [\n          {required: true, message: \"开始时间不能为空\", trigger: \"blur\"}\n        ],\n        endDate: [\n          {required: true, message: \"结束时间不能为空\", trigger: \"blur\"}\n        ],\n        innerImage: [\n          {required: true, message: \"活动详情主图不能为空\", trigger: \"blur\"}\n        ],\n        showTime: [\n          {required: true, message: \"展示时间不能为空\", trigger: \"blur\"}\n        ],\n        coverImage: [\n          {required: true, message: \"封面图片不能为空\", trigger: \"blur\"}\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询活动列表 */\n    getList() {\n      this.loading = true;\n      this.queryParams.params = {};\n      if (null != this.daterangeCreateTime && '' != this.daterangeCreateTime) {\n        this.queryParams.params[\"beginCreateTime\"] = this.daterangeCreateTime[0];\n        this.queryParams.params[\"endCreateTime\"] = this.daterangeCreateTime[1];\n      }\n      if (null != this.daterangeUpdateTime && '' != this.daterangeUpdateTime) {\n        this.queryParams.params[\"beginUpdateTime\"] = this.daterangeUpdateTime[0];\n        this.queryParams.params[\"endUpdateTime\"] = this.daterangeUpdateTime[1];\n      }\n      listActivity(this.queryParams).then(response => {\n        this.activityList = response.rows;\n        console.log(this.activityList)\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        activityId: undefined,\n        address: undefined,\n        regionId: undefined,\n        title: undefined,\n        classify: undefined,\n        startTime: undefined,\n        endDate: undefined,\n        innerImage: undefined,\n        showTime: undefined,\n        coverImage: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n        state: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.daterangeCreateTime = [];\n      this.daterangeUpdateTime = [];\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.activityId)\n      this.single = selection.length !== 1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.$router.push({path: \"/pzcActivity/add\"})\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.$router.push({path: \"/pzcActivity/add\", query: {activityId: row.activityId}})\n      // this.loading = true;\n      // this.reset();\n      // const activityId = row.activityId || this.ids\n      // getActivity(activityId).then(response => {\n      //   this.loading = false;\n      //   this.form = response.data;\n      //   this.open = true;\n      //   this.title = \"修改活动\";\n      // });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.activityId != null) {\n            console.log(\"活动起止时间是： \" + this.form.rangeTime)\n            this.form.startTime = this.form.rangeTime[0];\n            this.form.endDate = this.form.rangeTime[1];\n            updateActivity(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addActivity(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const activityIds = row.activityId || this.ids;\n      this.$modal.confirm('是否确认删除活动编号为\"' + activityIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delActivity(activityIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/activity/export', {\n        ...this.queryParams\n      }, `activity_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n\n\n<style>\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/activityConnArtist/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"活动ID\" prop=\"activityId\">\n        <el-input\n          v-model=\"queryParams.activityId\"\n          placeholder=\"请输入活动ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"艺人ID\" prop=\"artistId\">\n        <el-input\n          v-model=\"queryParams.artistId\"\n          placeholder=\"请输入艺人ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.createTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择创建时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item label=\"更新时间\" prop=\"updateTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.updateTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择更新时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:activityConnArtist:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:activityConnArtist:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:activityConnArtist:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:activityConnArtist:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"activityConnArtistList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"ID\" align=\"center\" prop=\"activityConnArtistId\" v-if=\"true\"/>\n      <el-table-column label=\"活动ID\" align=\"center\" prop=\"activityId\" />\n      <el-table-column label=\"艺人ID\" align=\"center\" prop=\"artistId\" />\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"更新时间\" align=\"center\" prop=\"updateTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"删除状态，默认为1表示正常状态\" align=\"center\" prop=\"state\" />\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:activityConnArtist:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:activityConnArtist:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改活动关联艺人对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"活动ID\" prop=\"activityId\">\n          <el-input v-model=\"form.activityId\" placeholder=\"请输入活动ID\" />\n        </el-form-item>\n        <el-form-item label=\"艺人ID\" prop=\"artistId\">\n          <el-input v-model=\"form.artistId\" placeholder=\"请输入艺人ID\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listActivityConnArtist, getActivityConnArtist, delActivityConnArtist, addActivityConnArtist, updateActivityConnArtist } from \"@/api/system/activityConnArtist\";\n\nexport default {\n  name: \"ActivityConnArtist\",\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 活动关联艺人表格数据\n      activityConnArtistList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        activityId: undefined,\n        artistId: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        activityConnArtistId: [\n          { required: true, message: \"ID不能为空\", trigger: \"blur\" }\n        ],\n        activityId: [\n          { required: true, message: \"活动ID不能为空\", trigger: \"blur\" }\n        ],\n        artistId: [\n          { required: true, message: \"艺人ID不能为空\", trigger: \"blur\" }\n        ],\n        createTime: [\n          { required: true, message: \"创建时间不能为空\", trigger: \"blur\" }\n        ],\n        updateTime: [\n          { required: true, message: \"更新时间不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询活动关联艺人列表 */\n    getList() {\n      this.loading = true;\n      listActivityConnArtist(this.queryParams).then(response => {\n        this.activityConnArtistList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        activityConnArtistId: undefined,\n        activityId: undefined,\n        artistId: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n        state: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.activityConnArtistId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加活动关联艺人\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const activityConnArtistId = row.activityConnArtistId || this.ids\n      getActivityConnArtist(activityConnArtistId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改活动关联艺人\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.activityConnArtistId != null) {\n            updateActivityConnArtist(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addActivityConnArtist(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const activityConnArtistIds = row.activityConnArtistId || this.ids;\n      this.$modal.confirm('是否确认删除活动关联艺人编号为\"' + activityConnArtistIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delActivityConnArtist(activityConnArtistIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/activityConnArtist/export', {\n        ...this.queryParams\n      }, `activityConnArtist_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/activityConnIntro/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"ID\" prop=\"activityConnIntroId\">\n        <el-input\n          v-model=\"queryParams.activityConnIntroId\"\n          placeholder=\"请输入ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"活动ID\" prop=\"activityId\">\n        <el-input\n          v-model=\"queryParams.activityId\"\n          placeholder=\"请输入活动ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"活动介绍ID\" prop=\"introId\">\n        <el-input\n          v-model=\"queryParams.introId\"\n          placeholder=\"请输入活动介绍ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:activityConnIntro:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:activityConnIntro:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:activityConnIntro:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:activityConnIntro:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"activityConnIntroList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"ID\" align=\"center\" prop=\"activityConnIntroId\" v-if=\"true\"/>\n      <el-table-column label=\"活动ID\" align=\"center\" prop=\"activityId\" />\n      <el-table-column label=\"活动介绍ID\" align=\"center\" prop=\"introId\" />\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"更新时间\" align=\"center\" prop=\"updateTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:activityConnIntro:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:activityConnIntro:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改活动介绍与活动关联对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"活动ID\" prop=\"activityId\">\n          <el-input v-model=\"form.activityId\" placeholder=\"请输入活动ID\" />\n        </el-form-item>\n        <el-form-item label=\"活动介绍ID\" prop=\"introId\">\n          <el-input v-model=\"form.introId\" placeholder=\"请输入活动介绍ID\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listActivityConnIntro, getActivityConnIntro, delActivityConnIntro, addActivityConnIntro, updateActivityConnIntro } from \"@/api/system/activityConnIntro\";\n\nexport default {\n  name: \"ActivityConnIntro\",\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 活动介绍与活动关联表格数据\n      activityConnIntroList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        activityConnIntroId: undefined,\n        activityId: undefined,\n        introId: undefined,\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        activityConnIntroId: [\n          { required: true, message: \"ID不能为空\", trigger: \"blur\" }\n        ],\n        activityId: [\n          { required: true, message: \"活动ID不能为空\", trigger: \"blur\" }\n        ],\n        introId: [\n          { required: true, message: \"活动介绍ID不能为空\", trigger: \"blur\" }\n        ],\n        createTime: [\n          { required: true, message: \"创建时间不能为空\", trigger: \"blur\" }\n        ],\n        updateTime: [\n          { required: true, message: \"更新时间不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询活动介绍与活动关联列表 */\n    getList() {\n      this.loading = true;\n      listActivityConnIntro(this.queryParams).then(response => {\n        this.activityConnIntroList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        activityConnIntroId: undefined,\n        activityId: undefined,\n        introId: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n        state: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.activityConnIntroId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加活动介绍与活动关联\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const activityConnIntroId = row.activityConnIntroId || this.ids\n      getActivityConnIntro(activityConnIntroId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改活动介绍与活动关联\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.activityConnIntroId != null) {\n            updateActivityConnIntro(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addActivityConnIntro(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const activityConnIntroIds = row.activityConnIntroId || this.ids;\n      this.$modal.confirm('是否确认删除活动介绍与活动关联编号为\"' + activityConnIntroIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delActivityConnIntro(activityConnIntroIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/activityConnIntro/export', {\n        ...this.queryParams\n      }, `activityConnIntro_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/activityConnTag/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"ID\" prop=\"activityConnTagId\">\n        <el-input\n          v-model=\"queryParams.activityConnTagId\"\n          placeholder=\"请输入ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"活动ID\" prop=\"activityId\">\n        <el-input\n          v-model=\"queryParams.activityId\"\n          placeholder=\"请输入活动ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"活动标签ID\" prop=\"tagId\">\n        <el-input\n          v-model=\"queryParams.tagId\"\n          placeholder=\"请输入活动标签ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.createTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择创建时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item label=\"更新时间\" prop=\"updateTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.updateTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择更新时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:activityConnTag:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:activityConnTag:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:activityConnTag:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:activityConnTag:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"activityConnTagList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"ID\" align=\"center\" prop=\"activityConnTagId\" v-if=\"true\"/>\n      <el-table-column label=\"活动ID\" align=\"center\" prop=\"activityId\" />\n      <el-table-column label=\"活动标签ID\" align=\"center\" prop=\"tagId\" />\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"更新时间\" align=\"center\" prop=\"updateTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:activityConnTag:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:activityConnTag:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改活动标签与活动关联对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"活动ID\" prop=\"activityId\">\n          <el-input v-model=\"form.activityId\" placeholder=\"请输入活动ID\" />\n        </el-form-item>\n        <el-form-item label=\"活动标签ID\" prop=\"tagId\">\n          <el-input v-model=\"form.tagId\" placeholder=\"请输入活动标签ID\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listActivityConnTag, getActivityConnTag, delActivityConnTag, addActivityConnTag, updateActivityConnTag } from \"@/api/system/activityConnTag\";\n\nexport default {\n  name: \"ActivityConnTag\",\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 活动标签与活动关联表格数据\n      activityConnTagList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        activityConnTagId: undefined,\n        activityId: undefined,\n        tagId: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        activityConnTagId: [\n          { required: true, message: \"ID不能为空\", trigger: \"blur\" }\n        ],\n        activityId: [\n          { required: true, message: \"活动ID不能为空\", trigger: \"blur\" }\n        ],\n        tagId: [\n          { required: true, message: \"活动标签ID不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询活动标签与活动关联列表 */\n    getList() {\n      this.loading = true;\n      listActivityConnTag(this.queryParams).then(response => {\n        this.activityConnTagList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        activityConnTagId: undefined,\n        activityId: undefined,\n        tagId: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n        state: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.activityConnTagId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加活动标签与活动关联\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const activityConnTagId = row.activityConnTagId || this.ids\n      getActivityConnTag(activityConnTagId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改活动标签与活动关联\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.activityConnTagId != null) {\n            updateActivityConnTag(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addActivityConnTag(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const activityConnTagIds = row.activityConnTagId || this.ids;\n      this.$modal.confirm('是否确认删除活动标签与活动关联编号为\"' + activityConnTagIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delActivityConnTag(activityConnTagIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/activityConnTag/export', {\n        ...this.queryParams\n      }, `activityConnTag_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/activityGroup/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"活动ID\" prop=\"activityId\">\n        <el-input\n          v-model=\"queryParams.activityId\"\n          placeholder=\"请输入活动ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"活动组队发起人ID\" prop=\"userId\">\n        <el-input\n          v-model=\"queryParams.userId\"\n          placeholder=\"请输入活动组队发起人ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"活动主题\" prop=\"title\">\n        <el-input\n          v-model=\"queryParams.title\"\n          placeholder=\"请输入活动主题\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"活动组队所缴纳的保证金\" prop=\"money\">\n        <el-input\n          v-model=\"queryParams.money\"\n          placeholder=\"请输入活动组队所缴纳的保证金\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"买单方式\" prop=\"groupType\">\n        <el-select v-model=\"queryParams.groupType\" placeholder=\"请选择买单方式\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.group_pay_type\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"活动地址\" prop=\"address\">\n        <el-input\n          v-model=\"queryParams.address\"\n          placeholder=\"请输入活动地址\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"一起约定好的时间\" prop=\"activityTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.activityTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择一起约定好的时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item label=\"权限\" prop=\"auth\">\n        <el-input\n          v-model=\"queryParams.auth\"\n          placeholder=\"请输入权限\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.createTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择创建时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item label=\"更新时间\" prop=\"updateTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.updateTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择更新时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:activityGroup:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:activityGroup:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:activityGroup:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:activityGroup:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"activityGroupList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"组队ID\" align=\"center\" prop=\"groupId\" v-if=\"true\"/>\n      <el-table-column label=\"活动ID\" align=\"center\" prop=\"activityId\" />\n      <el-table-column label=\"活动组队发起人ID\" align=\"center\" prop=\"userId\" />\n      <el-table-column label=\"活动主题\" align=\"center\" prop=\"title\" />\n      <el-table-column label=\"活动组队所缴纳的保证金\" align=\"center\" prop=\"money\" />\n      <el-table-column label=\"买单方式\" align=\"center\" prop=\"groupType\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.group_pay_type\" :value=\"scope.row.groupType\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"活动地址\" align=\"center\" prop=\"address\" />\n      <el-table-column label=\"一起约定好的时间\" align=\"center\" prop=\"activityTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.activityTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"权限\" align=\"center\" prop=\"auth\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.group_auth\" :value=\"scope.row.auth\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"更新时间\" align=\"center\" prop=\"updateTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:activityGroup:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:activityGroup:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改活动组队对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"活动ID\" prop=\"activityId\">\n          <el-input v-model=\"form.activityId\" placeholder=\"请输入活动ID\" />\n        </el-form-item>\n        <el-form-item label=\"活动组队发起人ID\" prop=\"userId\">\n          <el-input v-model=\"form.userId\" placeholder=\"请输入活动组队发起人ID\" />\n        </el-form-item>\n        <el-form-item label=\"活动主题\" prop=\"title\">\n          <el-input v-model=\"form.title\" placeholder=\"请输入活动主题\" />\n        </el-form-item>\n        <el-form-item label=\"活动组队所缴纳的保证金\" prop=\"money\">\n          <el-input v-model=\"form.money\" placeholder=\"请输入活动组队所缴纳的保证金\" />\n        </el-form-item>\n        <el-form-item label=\"买单方式\" prop=\"groupType\">\n          <el-select v-model=\"form.groupType\" placeholder=\"请选择买单方式\">\n            <el-option\n              v-for=\"dict in dict.type.group_pay_type\"\n              :key=\"dict.value\"\n              :label=\"dict.label\"\n              :value=\"parseInt(dict.value)\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"活动地址\" prop=\"address\">\n          <el-input v-model=\"form.address\" placeholder=\"请输入活动地址\" />\n        </el-form-item>\n        <el-form-item label=\"一起约定好的时间\" prop=\"activityTime\">\n          <el-date-picker clearable\n            v-model=\"form.activityTime\"\n            type=\"datetime\"\n            value-format=\"yyyy-MM-dd HH:mm:ss\"\n            placeholder=\"请选择一起约定好的时间\">\n          </el-date-picker>\n        </el-form-item>\n        <el-form-item label=\"权限\" prop=\"auth\">\n          <el-input v-model=\"form.auth\" placeholder=\"请输入权限\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listActivityGroup, getActivityGroup, delActivityGroup, addActivityGroup, updateActivityGroup } from \"@/api/system/activityGroup\";\n\nexport default {\n  name: \"ActivityGroup\",\n  dicts: ['group_pay_type'],\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 活动组队表格数据\n      activityGroupList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        activityId: undefined,\n        userId: undefined,\n        title: undefined,\n        money: undefined,\n        groupType: undefined,\n        address: undefined,\n        activityTime: undefined,\n        auth: undefined,\n        createTime: undefined,\n        updateTime: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        groupId: [\n          { required: true, message: \"组队ID不能为空\", trigger: \"blur\" }\n        ],\n        activityId: [\n          { required: true, message: \"活动ID不能为空\", trigger: \"blur\" }\n        ],\n        userId: [\n          { required: true, message: \"活动组队发起人ID不能为空\", trigger: \"blur\" }\n        ],\n        title: [\n          { required: true, message: \"活动主题不能为空\", trigger: \"blur\" }\n        ],\n        money: [\n          { required: true, message: \"活动组队所缴纳的保证金不能为空\", trigger: \"blur\" }\n        ],\n        groupType: [\n          { required: true, message: \"买单方式不能为空\", trigger: \"change\" }\n        ],\n        address: [\n          { required: true, message: \"活动地址不能为空\", trigger: \"blur\" }\n        ],\n        activityTime: [\n          { required: true, message: \"一起约定好的时间不能为空\", trigger: \"blur\" }\n        ],\n        auth: [\n          { required: true, message: \"权限不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询活动组队列表 */\n    getList() {\n      this.loading = true;\n      listActivityGroup(this.queryParams).then(response => {\n        this.activityGroupList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        groupId: undefined,\n        activityId: undefined,\n        userId: undefined,\n        title: undefined,\n        money: undefined,\n        groupType: undefined,\n        address: undefined,\n        activityTime: undefined,\n        auth: undefined,\n        createTime: undefined,\n        updateTime: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.groupId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加活动组队\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const groupId = row.groupId || this.ids\n      getActivityGroup(groupId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改活动组队\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.groupId != null) {\n            updateActivityGroup(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addActivityGroup(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const groupIds = row.groupId || this.ids;\n      this.$modal.confirm('是否确认删除活动组队编号为\"' + groupIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delActivityGroup(groupIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/activityGroup/export', {\n        ...this.queryParams\n      }, `activityGroup_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/activityGroupApply/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"申请ID\" prop=\"applyId\">\n        <el-input\n          v-model=\"queryParams.applyId\"\n          placeholder=\"请输入申请ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"申请人ID\" prop=\"userId\">\n        <el-input\n          v-model=\"queryParams.userId\"\n          placeholder=\"请输入申请人ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"申请的活动ID\" prop=\"activityId\">\n        <el-input\n          v-model=\"queryParams.activityId\"\n          placeholder=\"请输入申请的活动ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"申请加入的组ID\" prop=\"groupId\">\n        <el-input\n          v-model=\"queryParams.groupId\"\n          placeholder=\"请输入申请加入的组ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"买单类型\" prop=\"groupType\">\n        <el-select v-model=\"queryParams.groupType\" placeholder=\"买单类型\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.group_pay_type\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"活动保证金\" prop=\"money\">\n        <el-input\n          v-model=\"queryParams.money\"\n          placeholder=\"请输入活动保证金\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"留言内容\" prop=\"message\">\n        <el-input\n          v-model=\"queryParams.message\"\n          placeholder=\"请输入留言内容\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"\" prop=\"createTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.createTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item label=\"\" prop=\"updateTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.updateTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:activityGroupApply:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:activityGroupApply:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:activityGroupApply:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:activityGroupApply:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"activityGroupApplyList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"申请ID\" align=\"center\" prop=\"applyId\" v-if=\"true\"/>\n      <el-table-column label=\"申请人ID\" align=\"center\" prop=\"userId\" />\n      <el-table-column label=\"申请的活动ID\" align=\"center\" prop=\"activityId\" />\n      <el-table-column label=\"申请加入的组ID\" align=\"center\" prop=\"groupId\" />\n      <el-table-column label=\"买单类型\" align=\"center\" prop=\"groupType\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.group_pay_type\" :value=\"scope.row.groupType\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"活动保证金\" align=\"center\" prop=\"money\" />\n      <el-table-column label=\"留言内容\" align=\"center\" prop=\"message\" />\n      <el-table-column label=\"\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"\" align=\"center\" prop=\"updateTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:activityGroupApply:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:activityGroupApply:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改活动组队申请列对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"申请人ID\" prop=\"userId\">\n          <el-input v-model=\"form.userId\" placeholder=\"请输入申请人ID\" />\n        </el-form-item>\n        <el-form-item label=\"申请的活动ID\" prop=\"activityId\">\n          <el-input v-model=\"form.activityId\" placeholder=\"请输入申请的活动ID\" />\n        </el-form-item>\n        <el-form-item label=\"申请加入的组ID\" prop=\"groupId\">\n          <el-input v-model=\"form.groupId\" placeholder=\"请输入申请加入的组ID\" />\n        </el-form-item>\n        <el-form-item label=\"买单类型\" prop=\"groupType\">\n          <el-select v-model=\"form.groupType\" placeholder=\"买单类型\">\n            <el-option\n              v-for=\"dict in dict.type.group_pay_type\"\n              :key=\"dict.value\"\n              :label=\"dict.label\"\n              :value=\"parseInt(dict.value)\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"活动保证金\" prop=\"money\">\n          <el-input v-model=\"form.money\" placeholder=\"请输入活动保证金\" />\n        </el-form-item>\n        <el-form-item label=\"留言内容\" prop=\"message\">\n          <el-input v-model=\"form.message\" placeholder=\"请输入留言内容\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listActivityGroupApply, getActivityGroupApply, delActivityGroupApply, addActivityGroupApply, updateActivityGroupApply } from \"@/api/system/activityGroupApply\";\n\nexport default {\n  name: \"ActivityGroupApply\",\n  dicts: ['group_pay_type'],\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 活动组队申请列表格数据\n      activityGroupApplyList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        applyId: undefined,\n        userId: undefined,\n        activityId: undefined,\n        groupId: undefined,\n        groupType: undefined,\n        money: undefined,\n        message: undefined,\n        createTime: undefined,\n        updateTime: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        applyId: [\n          { required: true, message: \"申请ID不能为空\", trigger: \"blur\" }\n        ],\n        userId: [\n          { required: true, message: \"申请人ID不能为空\", trigger: \"blur\" }\n        ],\n        activityId: [\n          { required: true, message: \"申请的活动ID不能为空\", trigger: \"blur\" }\n        ],\n        groupId: [\n          { required: true, message: \"申请加入的组ID不能为空\", trigger: \"blur\" }\n        ],\n        groupType: [\n          { required: true, message: \"不能为空\", trigger: \"change\" }\n        ],\n        money: [\n          { required: true, message: \"活动保证金不能为空\", trigger: \"blur\" }\n        ],\n        message: [\n          { required: true, message: \"留言内容不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询活动组队申请列列表 */\n    getList() {\n      this.loading = true;\n      listActivityGroupApply(this.queryParams).then(response => {\n        this.activityGroupApplyList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        applyId: undefined,\n        userId: undefined,\n        activityId: undefined,\n        groupId: undefined,\n        groupType: undefined,\n        money: undefined,\n        message: undefined,\n        createTime: undefined,\n        updateTime: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.applyId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加活动组队申请列\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const applyId = row.applyId || this.ids\n      getActivityGroupApply(applyId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改活动组队申请列\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.applyId != null) {\n            updateActivityGroupApply(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addActivityGroupApply(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const applyIds = row.applyId || this.ids;\n      this.$modal.confirm('是否确认删除活动组队申请列编号为\"' + applyIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delActivityGroupApply(applyIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/activityGroupApply/export', {\n        ...this.queryParams\n      }, `activityGroupApply_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/artist/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"艺人名\" prop=\"name\">\n        <el-input\n          v-model=\"queryParams.name\"\n          placeholder=\"请输入艺人名\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"艺人介绍\" prop=\"description\">\n        <el-input\n          v-model=\"queryParams.description\"\n          placeholder=\"请输入艺人介绍\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:artist:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:artist:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:artist:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:artist:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"artistList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"ID\" align=\"center\" prop=\"artistId\" v-if=\"true\"/>\n      <el-table-column label=\"艺人名\" align=\"center\" prop=\"name\" />\n      <el-table-column label=\"艺人照片\" align=\"center\" prop=\"imageUrl\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <image-preview :src=\"scope.row.imageUrl\" :width=\"50\" :height=\"50\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"艺人介绍\" align=\"center\" prop=\"description\" />\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:artist:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:artist:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改艺人对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"艺人名\" prop=\"name\">\n          <el-input v-model=\"form.name\" placeholder=\"请输入艺人名\" />\n        </el-form-item>\n        <el-form-item label=\"艺人照片\" prop=\"imageUrl\">\n          <image-upload v-model=\"form.imageUrl\"/>\n        </el-form-item>\n        <el-form-item label=\"艺人介绍\" prop=\"description\">\n          <el-input v-model=\"form.description\" type=\"textarea\" placeholder=\"请输入内容\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listArtist, getArtist, delArtist, addArtist, updateArtist } from \"@/api/system/artist\";\n\nexport default {\n  name: \"Artist\",\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 艺人表格数据\n      artistList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        name: undefined,\n        imageUrl: undefined,\n        description: undefined,\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        name: [\n          { required: true, message: \"艺人名不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询艺人列表 */\n    getList() {\n      this.loading = true;\n      listArtist(this.queryParams).then(response => {\n        this.artistList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        artistId: undefined,\n        name: undefined,\n        imageUrl: undefined,\n        description: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n        state: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.artistId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加艺人\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const artistId = row.artistId || this.ids\n      getArtist(artistId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改艺人\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.artistId != null) {\n            updateArtist(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addArtist(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const artistIds = row.artistId || this.ids;\n      this.$modal.confirm('是否确认删除艺人编号为\"' + artistIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delArtist(artistIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/artist/export', {\n        ...this.queryParams\n      }, `artist_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/config/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"参数名称\" prop=\"configName\">\n        <el-input\n          v-model=\"queryParams.configName\"\n          placeholder=\"请输入参数名称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"参数键名\" prop=\"configKey\">\n        <el-input\n          v-model=\"queryParams.configKey\"\n          placeholder=\"请输入参数键名\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"系统内置\" prop=\"configType\">\n        <el-select v-model=\"queryParams.configType\" placeholder=\"系统内置\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.sys_yes_no\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"创建时间\">\n        <el-date-picker\n          v-model=\"dateRange\"\n          style=\"width: 240px\"\n          value-format=\"yyyy-MM-dd HH:mm:ss\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"['00:00:00', '23:59:59']\"\n        ></el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:config:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:config:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:config:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:config:export']\"\n        >导出</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-refresh\"\n          size=\"mini\"\n          @click=\"handleRefreshCache\"\n          v-hasPermi=\"['system:config:remove']\"\n        >刷新缓存</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"configList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"参数主键\" align=\"center\" prop=\"configId\" />\n      <el-table-column label=\"参数名称\" align=\"center\" prop=\"configName\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"参数键名\" align=\"center\" prop=\"configKey\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"参数键值\" align=\"center\" prop=\"configValue\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"系统内置\" align=\"center\" prop=\"configType\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.sys_yes_no\" :value=\"scope.row.configType\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"备注\" align=\"center\" prop=\"remark\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:config:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:config:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改参数配置对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"参数名称\" prop=\"configName\">\n          <el-input v-model=\"form.configName\" placeholder=\"请输入参数名称\" />\n        </el-form-item>\n        <el-form-item label=\"参数键名\" prop=\"configKey\">\n          <el-input v-model=\"form.configKey\" placeholder=\"请输入参数键名\" />\n        </el-form-item>\n        <el-form-item label=\"参数键值\" prop=\"configValue\">\n          <el-input v-model=\"form.configValue\" placeholder=\"请输入参数键值\" />\n        </el-form-item>\n        <el-form-item label=\"系统内置\" prop=\"configType\">\n          <el-radio-group v-model=\"form.configType\">\n            <el-radio\n              v-for=\"dict in dict.type.sys_yes_no\"\n              :key=\"dict.value\"\n              :label=\"dict.value\"\n            >{{dict.label}}</el-radio>\n          </el-radio-group>\n        </el-form-item>\n        <el-form-item label=\"备注\" prop=\"remark\">\n          <el-input v-model=\"form.remark\" type=\"textarea\" placeholder=\"请输入内容\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache } from \"@/api/system/config\";\n\nexport default {\n  name: \"Config\",\n  dicts: ['sys_yes_no'],\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 参数表格数据\n      configList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 日期范围\n      dateRange: [],\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        configName: undefined,\n        configKey: undefined,\n        configType: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        configName: [\n          { required: true, message: \"参数名称不能为空\", trigger: \"blur\" }\n        ],\n        configKey: [\n          { required: true, message: \"参数键名不能为空\", trigger: \"blur\" }\n        ],\n        configValue: [\n          { required: true, message: \"参数键值不能为空\", trigger: \"blur\" }\n        ]\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询参数列表 */\n    getList() {\n      this.loading = true;\n      listConfig(this.addDateRange(this.queryParams, this.dateRange)).then(response => {\n          this.configList = response.rows;\n          this.total = response.total;\n          this.loading = false;\n        }\n      );\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        configId: undefined,\n        configName: undefined,\n        configKey: undefined,\n        configValue: undefined,\n        configType: \"Y\",\n        remark: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.dateRange = [];\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加参数\";\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.configId)\n      this.single = selection.length!=1\n      this.multiple = !selection.length\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.reset();\n      const configId = row.configId || this.ids\n      getConfig(configId).then(response => {\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改参数\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm: function() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          if (this.form.configId != undefined) {\n            updateConfig(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            });\n          } else {\n            addConfig(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const configIds = row.configId || this.ids;\n      this.$modal.confirm('是否确认删除参数编号为\"' + configIds + '\"的数据项？').then(function() {\n          return delConfig(configIds);\n        }).then(() => {\n          this.getList();\n          this.$modal.msgSuccess(\"删除成功\");\n        }).catch(() => {});\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/config/export', {\n        ...this.queryParams\n      }, `config_${new Date().getTime()}.xlsx`)\n    },\n    /** 刷新缓存按钮操作 */\n    handleRefreshCache() {\n      refreshCache().then(() => {\n        this.$modal.msgSuccess(\"刷新成功\");\n      });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/dept/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\">\n      <el-form-item label=\"部门名称\" prop=\"deptName\">\n        <el-input\n          v-model=\"queryParams.deptName\"\n          placeholder=\"请输入部门名称\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select v-model=\"queryParams.status\" placeholder=\"部门状态\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.sys_normal_disable\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:dept:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"info\"\n          plain\n          icon=\"el-icon-sort\"\n          size=\"mini\"\n          @click=\"toggleExpandAll\"\n        >展开/折叠</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table\n      v-if=\"refreshTable\"\n      v-loading=\"loading\"\n      :data=\"deptList\"\n      row-key=\"deptId\"\n      :default-expand-all=\"isExpandAll\"\n      :tree-props=\"{children: 'children', hasChildren: 'hasChildren'}\"\n    >\n      <el-table-column prop=\"deptName\" label=\"部门名称\" width=\"260\"></el-table-column>\n      <el-table-column prop=\"orderNum\" label=\"排序\" width=\"200\"></el-table-column>\n      <el-table-column prop=\"status\" label=\"状态\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.sys_normal_disable\" :value=\"scope.row.status\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"200\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:dept:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-plus\"\n            @click=\"handleAdd(scope.row)\"\n            v-hasPermi=\"['system:dept:add']\"\n          >新增</el-button>\n          <el-button\n            v-if=\"scope.row.parentId != 0\"\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:dept:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <!-- 添加或修改部门对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"600px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-row>\n          <el-col :span=\"24\" v-if=\"form.parentId !== 0\">\n            <el-form-item label=\"上级部门\" prop=\"parentId\">\n              <treeselect v-model=\"form.parentId\" :options=\"deptOptions\" :normalizer=\"normalizer\" placeholder=\"选择上级部门\" />\n            </el-form-item>\n          </el-col>\n        </el-row>\n        <el-row>\n          <el-col :span=\"12\">\n            <el-form-item label=\"部门名称\" prop=\"deptName\">\n              <el-input v-model=\"form.deptName\" placeholder=\"请输入部门名称\" />\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\">\n            <el-form-item label=\"显示排序\" prop=\"orderNum\">\n              <el-input-number v-model=\"form.orderNum\" controls-position=\"right\" :min=\"0\" />\n            </el-form-item>\n          </el-col>\n        </el-row>\n        <el-row>\n          <el-col :span=\"12\">\n            <el-form-item label=\"负责人\" prop=\"leader\">\n              <el-input v-model=\"form.leader\" placeholder=\"请输入负责人\" maxlength=\"20\" />\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\">\n            <el-form-item label=\"联系电话\" prop=\"phone\">\n              <el-input v-model=\"form.phone\" placeholder=\"请输入联系电话\" maxlength=\"11\" />\n            </el-form-item>\n          </el-col>\n        </el-row>\n        <el-row>\n          <el-col :span=\"12\">\n            <el-form-item label=\"邮箱\" prop=\"email\">\n              <el-input v-model=\"form.email\" placeholder=\"请输入邮箱\" maxlength=\"50\" />\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\">\n            <el-form-item label=\"部门状态\">\n              <el-radio-group v-model=\"form.status\">\n                <el-radio\n                  v-for=\"dict in dict.type.sys_normal_disable\"\n                  :key=\"dict.value\"\n                  :label=\"dict.value\"\n                >{{dict.label}}</el-radio>\n              </el-radio-group>\n            </el-form-item>\n          </el-col>\n        </el-row>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from \"@/api/system/dept\";\nimport Treeselect from \"@riophae/vue-treeselect\";\nimport \"@riophae/vue-treeselect/dist/vue-treeselect.css\";\n\nexport default {\n  name: \"Dept\",\n  dicts: ['sys_normal_disable'],\n  components: { Treeselect },\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 表格树数据\n      deptList: [],\n      // 部门树选项\n      deptOptions: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 是否展开，默认全部展开\n      isExpandAll: true,\n      // 重新渲染表格状态\n      refreshTable: true,\n      // 查询参数\n      queryParams: {\n        deptName: undefined,\n        status: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        parentId: [\n          { required: true, message: \"上级部门不能为空\", trigger: \"blur\" }\n        ],\n        deptName: [\n          { required: true, message: \"部门名称不能为空\", trigger: \"blur\" }\n        ],\n        orderNum: [\n          { required: true, message: \"显示排序不能为空\", trigger: \"blur\" }\n        ],\n        email: [\n          {\n            type: \"email\",\n            message: \"请输入正确的邮箱地址\",\n            trigger: [\"blur\", \"change\"]\n          }\n        ],\n        phone: [\n          {\n            pattern: /^1[3|4|5|6|7|8|9][0-9]\\d{8}$/,\n            message: \"请输入正确的手机号码\",\n            trigger: \"blur\"\n          }\n        ]\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询部门列表 */\n    getList() {\n      this.loading = true;\n      listDept(this.queryParams).then(response => {\n        this.deptList = this.handleTree(response.data, \"deptId\");\n        this.loading = false;\n      });\n    },\n    /** 转换部门数据结构 */\n    normalizer(node) {\n      if (node.children && !node.children.length) {\n        delete node.children;\n      }\n      return {\n        id: node.deptId,\n        label: node.deptName,\n        children: node.children\n      };\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        deptId: undefined,\n        parentId: undefined,\n        deptName: undefined,\n        orderNum: undefined,\n        leader: undefined,\n        phone: undefined,\n        email: undefined,\n        status: \"0\"\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 新增按钮操作 */\n    handleAdd(row) {\n      this.reset();\n      if (row != undefined) {\n        this.form.parentId = row.deptId;\n      }\n      this.open = true;\n      this.title = \"添加部门\";\n      listDept().then(response => {\n        this.deptOptions = this.handleTree(response.data, \"deptId\");\n      });\n    },\n    /** 展开/折叠操作 */\n    toggleExpandAll() {\n      this.refreshTable = false;\n      this.isExpandAll = !this.isExpandAll;\n      this.$nextTick(() => {\n        this.refreshTable = true;\n      });\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.reset();\n      getDept(row.deptId).then(response => {\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改部门\";\n        listDeptExcludeChild(row.deptId).then(response => {\n          this.deptOptions = this.handleTree(response.data, \"deptId\");\n          if (this.deptOptions.length == 0) {\n            const noResultsOptions = { deptId: this.form.parentId, deptName: this.form.parentName, children: [] };\n            this.deptOptions.push(noResultsOptions);\n          }\n        });\n      });\n    },\n    /** 提交按钮 */\n    submitForm: function() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          if (this.form.deptId != undefined) {\n            updateDept(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            });\n          } else {\n            addDept(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      this.$modal.confirm('是否确认删除名称为\"' + row.deptName + '\"的数据项？').then(function() {\n        return delDept(row.deptId);\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {});\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/dict/data.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"字典名称\" prop=\"dictType\">\n        <el-select v-model=\"queryParams.dictType\">\n          <el-option\n            v-for=\"item in typeOptions\"\n            :key=\"item.dictId\"\n            :label=\"item.dictName\"\n            :value=\"item.dictType\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"字典标签\" prop=\"dictLabel\">\n        <el-input\n          v-model=\"queryParams.dictLabel\"\n          placeholder=\"请输入字典标签\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select v-model=\"queryParams.status\" placeholder=\"数据状态\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.sys_normal_disable\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:dict:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:dict:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:dict:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:dict:export']\"\n        >导出</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-close\"\n          size=\"mini\"\n          @click=\"handleClose\"\n        >关闭</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"dataList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"字典编码\" align=\"center\" prop=\"dictCode\" />\n      <el-table-column label=\"字典标签\" align=\"center\" prop=\"dictLabel\">\n        <template slot-scope=\"scope\">\n          <span v-if=\"scope.row.listClass == '' || scope.row.listClass == 'default'\">{{scope.row.dictLabel}}</span>\n          <el-tag v-else :type=\"scope.row.listClass == 'primary' ? '' : scope.row.listClass\">{{scope.row.dictLabel}}</el-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"字典键值\" align=\"center\" prop=\"dictValue\" />\n      <el-table-column label=\"字典排序\" align=\"center\" prop=\"dictSort\" />\n      <el-table-column label=\"状态\" align=\"center\" prop=\"status\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.sys_normal_disable\" :value=\"scope.row.status\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"备注\" align=\"center\" prop=\"remark\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:dict:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:dict:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改参数配置对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"字典类型\">\n          <el-input v-model=\"form.dictType\" :disabled=\"true\" />\n        </el-form-item>\n        <el-form-item label=\"数据标签\" prop=\"dictLabel\">\n          <el-input v-model=\"form.dictLabel\" placeholder=\"请输入数据标签\" />\n        </el-form-item>\n        <el-form-item label=\"数据键值\" prop=\"dictValue\">\n          <el-input v-model=\"form.dictValue\" placeholder=\"请输入数据键值\" />\n        </el-form-item>\n        <el-form-item label=\"样式属性\" prop=\"cssClass\">\n          <el-input v-model=\"form.cssClass\" placeholder=\"请输入样式属性\" />\n        </el-form-item>\n        <el-form-item label=\"显示排序\" prop=\"dictSort\">\n          <el-input-number v-model=\"form.dictSort\" controls-position=\"right\" :min=\"0\" />\n        </el-form-item>\n        <el-form-item label=\"回显样式\" prop=\"listClass\">\n          <el-select v-model=\"form.listClass\">\n            <el-option\n              v-for=\"item in listClassOptions\"\n              :key=\"item.value\"\n              :label=\"item.label + '(' + item.value + ')'\"\n              :value=\"item.value\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"状态\" prop=\"status\">\n          <el-radio-group v-model=\"form.status\">\n            <el-radio\n              v-for=\"dict in dict.type.sys_normal_disable\"\n              :key=\"dict.value\"\n              :label=\"dict.value\"\n            >{{dict.label}}</el-radio>\n          </el-radio-group>\n        </el-form-item>\n        <el-form-item label=\"备注\" prop=\"remark\">\n          <el-input v-model=\"form.remark\" type=\"textarea\" placeholder=\"请输入内容\"></el-input>\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listData, getData, delData, addData, updateData } from \"@/api/system/dict/data\";\nimport { optionselect as getDictOptionselect, getType } from \"@/api/system/dict/type\";\n\nexport default {\n  name: \"Data\",\n  dicts: ['sys_normal_disable'],\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 字典表格数据\n      dataList: [],\n      // 默认字典类型\n      defaultDictType: \"\",\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 数据标签回显样式\n      listClassOptions: [\n        {\n          value: \"default\",\n          label: \"默认\"\n        },\n        {\n          value: \"primary\",\n          label: \"主要\"\n        },\n        {\n          value: \"success\",\n          label: \"成功\"\n        },\n        {\n          value: \"info\",\n          label: \"信息\"\n        },\n        {\n          value: \"warning\",\n          label: \"警告\"\n        },\n        {\n          value: \"danger\",\n          label: \"危险\"\n        }\n      ],\n      // 类型数据字典\n      typeOptions: [],\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        dictName: undefined,\n        dictType: undefined,\n        status: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        dictLabel: [\n          { required: true, message: \"数据标签不能为空\", trigger: \"blur\" }\n        ],\n        dictValue: [\n          { required: true, message: \"数据键值不能为空\", trigger: \"blur\" }\n        ],\n        dictSort: [\n          { required: true, message: \"数据顺序不能为空\", trigger: \"blur\" }\n        ]\n      }\n    };\n  },\n  created() {\n    const dictId = this.$route.params && this.$route.params.dictId;\n    this.getType(dictId);\n    this.getTypeList();\n  },\n  methods: {\n    /** 查询字典类型详细 */\n    getType(dictId) {\n      getType(dictId).then(response => {\n        this.queryParams.dictType = response.data.dictType;\n        this.defaultDictType = response.data.dictType;\n        this.getList();\n      });\n    },\n    /** 查询字典类型列表 */\n    getTypeList() {\n      getDictOptionselect().then(response => {\n        this.typeOptions = response.data;\n      });\n    },\n    /** 查询字典数据列表 */\n    getList() {\n      this.loading = true;\n      listData(this.queryParams).then(response => {\n        this.dataList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        dictCode: undefined,\n        dictLabel: undefined,\n        dictValue: undefined,\n        cssClass: undefined,\n        listClass: 'default',\n        dictSort: 0,\n        status: \"0\",\n        remark: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 返回按钮操作 */\n    handleClose() {\n      const obj = { path: \"/system/dict\" };\n      this.$tab.closeOpenPage(obj);\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.queryParams.dictType = this.defaultDictType;\n      this.handleQuery();\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加字典数据\";\n      this.form.dictType = this.queryParams.dictType;\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.dictCode)\n      this.single = selection.length!=1\n      this.multiple = !selection.length\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.reset();\n      const dictCode = row.dictCode || this.ids\n      getData(dictCode).then(response => {\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改字典数据\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm: function() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          if (this.form.dictCode != undefined) {\n            updateData(this.form).then(response => {\n              this.$store.dispatch('dict/removeDict', this.queryParams.dictType);\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            });\n          } else {\n            addData(this.form).then(response => {\n              this.$store.dispatch('dict/removeDict', this.queryParams.dictType);\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const dictCodes = row.dictCode || this.ids;\n      this.$modal.confirm('是否确认删除字典编码为\"' + dictCodes + '\"的数据项？').then(function() {\n        return delData(dictCodes);\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n        this.$store.dispatch('dict/removeDict', this.queryParams.dictType);\n      }).catch(() => {});\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/dict/data/export', {\n        ...this.queryParams\n      }, `data_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/dict/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"字典名称\" prop=\"dictName\">\n        <el-input\n          v-model=\"queryParams.dictName\"\n          placeholder=\"请输入字典名称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"字典类型\" prop=\"dictType\">\n        <el-input\n          v-model=\"queryParams.dictType\"\n          placeholder=\"请输入字典类型\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          v-model=\"queryParams.status\"\n          placeholder=\"字典状态\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <el-option\n            v-for=\"dict in dict.type.sys_normal_disable\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"创建时间\">\n        <el-date-picker\n          v-model=\"dateRange\"\n          style=\"width: 240px\"\n          value-format=\"yyyy-MM-dd HH:mm:ss\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"['00:00:00', '23:59:59']\"\n        ></el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:dict:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:dict:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:dict:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:dict:export']\"\n        >导出</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-refresh\"\n          size=\"mini\"\n          @click=\"handleRefreshCache\"\n          v-hasPermi=\"['system:dict:remove']\"\n        >刷新缓存</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"typeList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"字典编号\" align=\"center\" prop=\"dictId\" />\n      <el-table-column label=\"字典名称\" align=\"center\" prop=\"dictName\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"字典类型\" align=\"center\" :show-overflow-tooltip=\"true\">\n        <template slot-scope=\"scope\">\n          <router-link :to=\"'/system/dict-data/index/' + scope.row.dictId\" class=\"link-type\">\n            <span>{{ scope.row.dictType }}</span>\n          </router-link>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"状态\" align=\"center\" prop=\"status\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.sys_normal_disable\" :value=\"scope.row.status\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"备注\" align=\"center\" prop=\"remark\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:dict:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:dict:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改参数配置对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"字典名称\" prop=\"dictName\">\n          <el-input v-model=\"form.dictName\" placeholder=\"请输入字典名称\" />\n        </el-form-item>\n        <el-form-item label=\"字典类型\" prop=\"dictType\">\n          <el-input v-model=\"form.dictType\" placeholder=\"请输入字典类型\" />\n        </el-form-item>\n        <el-form-item label=\"状态\" prop=\"status\">\n          <el-radio-group v-model=\"form.status\">\n            <el-radio\n              v-for=\"dict in dict.type.sys_normal_disable\"\n              :key=\"dict.value\"\n              :label=\"dict.value\"\n            >{{dict.label}}</el-radio>\n          </el-radio-group>\n        </el-form-item>\n        <el-form-item label=\"备注\" prop=\"remark\">\n          <el-input v-model=\"form.remark\" type=\"textarea\" placeholder=\"请输入内容\"></el-input>\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listType, getType, delType, addType, updateType, refreshCache } from \"@/api/system/dict/type\";\n\nexport default {\n  name: \"Dict\",\n  dicts: ['sys_normal_disable'],\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 字典表格数据\n      typeList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 日期范围\n      dateRange: [],\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        dictName: undefined,\n        dictType: undefined,\n        status: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        dictName: [\n          { required: true, message: \"字典名称不能为空\", trigger: \"blur\" }\n        ],\n        dictType: [\n          { required: true, message: \"字典类型不能为空\", trigger: \"blur\" }\n        ]\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询字典类型列表 */\n    getList() {\n      this.loading = true;\n      listType(this.addDateRange(this.queryParams, this.dateRange)).then(response => {\n          this.typeList = response.rows;\n          this.total = response.total;\n          this.loading = false;\n        }\n      );\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        dictId: undefined,\n        dictName: undefined,\n        dictType: undefined,\n        status: \"0\",\n        remark: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.dateRange = [];\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加字典类型\";\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.dictId)\n      this.single = selection.length!=1\n      this.multiple = !selection.length\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.reset();\n      const dictId = row.dictId || this.ids\n      getType(dictId).then(response => {\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改字典类型\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm: function() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          if (this.form.dictId != undefined) {\n            updateType(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            });\n          } else {\n            addType(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const dictIds = row.dictId || this.ids;\n      this.$modal.confirm('是否确认删除字典编号为\"' + dictIds + '\"的数据项？').then(function() {\n        return delType(dictIds);\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {});\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/dict/type/export', {\n        ...this.queryParams\n      }, `type_${new Date().getTime()}.xlsx`)\n    },\n    /** 刷新缓存按钮操作 */\n    handleRefreshCache() {\n      refreshCache().then(() => {\n        this.$modal.msgSuccess(\"刷新成功\");\n        this.$store.dispatch('dict/cleanDict');\n      });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/intro/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"标题\" prop=\"title\">\n        <el-input\n          v-model=\"queryParams.title\"\n          placeholder=\"请输入标题\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.createTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择创建时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item label=\"更新时间\" prop=\"updateTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.updateTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择更新时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:intro:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:intro:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:intro:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:intro:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"introList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"ID\" align=\"center\" prop=\"introId\" v-if=\"true\"/>\n      <el-table-column label=\"标题\" align=\"center\" prop=\"title\" />\n      <el-table-column label=\"内容\" align=\"center\" prop=\"content.slice(100)\" />\n      <el-table-column label=\"0 场地舞台介绍 1 更多介绍\" align=\"center\" prop=\"type\" />\n      <el-table-column label=\"图片\" align=\"center\" prop=\"imageFullUrl\" width=\"100\">\n        <template slot-scope=\"scope\">\n<!--          <image-upload v-model=\"scope.row.imageFullUrl\" :width=\"50\" :height=\"50\"/>-->\n          <image-preview :src=\"scope.row.imageFullUrl\" :width=\"50\" :height=\"50\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"更新时间\" align=\"center\" prop=\"updateTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:intro:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:intro:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改活动介绍对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"标题\" prop=\"title\">\n          <el-input v-model=\"form.title\" placeholder=\"请输入标题\" />\n        </el-form-item>\n        <el-form-item label=\"介绍类型\" prop=\"type\">\n          <el-input v-model=\"form.type\" placeholder=\"请输入介绍类型 0舞台介绍 1更多介绍\" />\n        </el-form-item>\n        <el-form-item label=\"内容\">\n          <editor v-model=\"form.content\" :min-height=\"192\"/>\n        </el-form-item>\n        <el-form-item label=\"图片\" prop=\"imageFullUrl\">\n          <image-upload v-model=\"form.imageFullUrl\"/>\n        </el-form-item>\n      </el-form>\n\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listIntro, getIntro, delIntro, addIntro, updateIntro } from \"@/api/system/intro\";\n\nexport default {\n  name: \"Intro\",\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 活动介绍表格数据\n      introList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        title: undefined,\n        content: undefined,\n        type: undefined,\n        imageFullUrl: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        introId: [\n          { required: true, message: \"ID不能为空\", trigger: \"blur\" }\n        ],\n        title: [\n          { required: true, message: \"标题不能为空\", trigger: \"blur\" }\n        ],\n        content: [\n          { required: true, message: \"内容不能为空\", trigger: \"blur\" }\n        ],\n        type: [\n          { required: true, message: \"0 场地舞台介绍 1 更多介绍不能为空\", trigger: \"change\" }\n        ],\n        imageFullUrl: [\n          { required: true, message: \"图片不能为空\", trigger: \"blur\" }\n        ]\n        // ],\n        // createTime: [\n        //   { required: true, message: \"创建时间不能为空\", trigger: \"blur\" }\n        // ],\n        // updateTime: [\n        //   { required: true, message: \"更新时间不能为空\", trigger: \"blur\" }\n        // ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询活动介绍列表 */\n    getList() {\n      this.loading = true;\n      listIntro(this.queryParams).then(response => {\n        this.introList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        introId: undefined,\n        title: undefined,\n        content: undefined,\n        type: undefined,\n        imageFullUrl: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n        state: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.introId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加活动介绍\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const introId = row.introId || this.ids\n      getIntro(introId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改活动介绍\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.introId != null) {\n            updateIntro(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addIntro(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const introIds = row.introId || this.ids;\n      this.$modal.confirm('是否确认删除活动介绍编号为\"' + introIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delIntro(introIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/intro/export', {\n        ...this.queryParams\n      }, `intro_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/menu/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\">\n      <el-form-item label=\"菜单名称\" prop=\"menuName\">\n        <el-input\n          v-model=\"queryParams.menuName\"\n          placeholder=\"请输入菜单名称\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select v-model=\"queryParams.status\" placeholder=\"菜单状态\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.sys_normal_disable\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:menu:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"info\"\n          plain\n          icon=\"el-icon-sort\"\n          size=\"mini\"\n          @click=\"toggleExpandAll\"\n        >展开/折叠</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table\n      v-if=\"refreshTable\"\n      v-loading=\"loading\"\n      :data=\"menuList\"\n      row-key=\"menuId\"\n      :default-expand-all=\"isExpandAll\"\n      :tree-props=\"{children: 'children', hasChildren: 'hasChildren'}\"\n    >\n      <el-table-column prop=\"menuName\" label=\"菜单名称\" :show-overflow-tooltip=\"true\" width=\"160\"></el-table-column>\n      <el-table-column prop=\"icon\" label=\"图标\" align=\"center\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <svg-icon :icon-class=\"scope.row.icon\" />\n        </template>\n      </el-table-column>\n      <el-table-column prop=\"orderNum\" label=\"排序\" width=\"60\"></el-table-column>\n      <el-table-column prop=\"perms\" label=\"权限标识\" :show-overflow-tooltip=\"true\"></el-table-column>\n      <el-table-column prop=\"component\" label=\"组件路径\" :show-overflow-tooltip=\"true\"></el-table-column>\n      <el-table-column prop=\"status\" label=\"状态\" width=\"80\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.sys_normal_disable\" :value=\"scope.row.status\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:menu:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-plus\"\n            @click=\"handleAdd(scope.row)\"\n            v-hasPermi=\"['system:menu:add']\"\n          >新增</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:menu:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <!-- 添加或修改菜单对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"680px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"100px\">\n        <el-row>\n          <el-col :span=\"24\">\n            <el-form-item label=\"上级菜单\" prop=\"parentId\">\n              <treeselect\n                v-model=\"form.parentId\"\n                :options=\"menuOptions\"\n                :normalizer=\"normalizer\"\n                :show-count=\"true\"\n                placeholder=\"选择上级菜单\"\n              />\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"24\">\n            <el-form-item label=\"菜单类型\" prop=\"menuType\">\n              <el-radio-group v-model=\"form.menuType\">\n                <el-radio label=\"M\">目录</el-radio>\n                <el-radio label=\"C\">菜单</el-radio>\n                <el-radio label=\"F\">按钮</el-radio>\n              </el-radio-group>\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"24\" v-if=\"form.menuType != 'F'\">\n            <el-form-item label=\"菜单图标\" prop=\"icon\">\n              <el-popover\n                placement=\"bottom-start\"\n                width=\"460\"\n                trigger=\"click\"\n                @show=\"$refs['iconSelect'].reset()\"\n              >\n                <IconSelect ref=\"iconSelect\" @selected=\"selected\" :active-icon=\"form.icon\" />\n                <el-input slot=\"reference\" v-model=\"form.icon\" placeholder=\"点击选择图标\" readonly>\n                  <svg-icon\n                    v-if=\"form.icon\"\n                    slot=\"prefix\"\n                    :icon-class=\"form.icon\"\n                    style=\"width: 25px;\"\n                  />\n                  <i v-else slot=\"prefix\" class=\"el-icon-search el-input__icon\" />\n                </el-input>\n              </el-popover>\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\">\n            <el-form-item label=\"菜单名称\" prop=\"menuName\">\n              <el-input v-model=\"form.menuName\" placeholder=\"请输入菜单名称\" />\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\">\n            <el-form-item label=\"显示排序\" prop=\"orderNum\">\n              <el-input-number v-model=\"form.orderNum\" controls-position=\"right\" :min=\"0\" />\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\" v-if=\"form.menuType != 'F'\">\n            <el-form-item prop=\"isFrame\">\n              <span slot=\"label\">\n                <el-tooltip content=\"选择是外链则路由地址需要以`http(s)://`开头\" placement=\"top\">\n                <i class=\"el-icon-question\"></i>\n                </el-tooltip>\n                是否外链\n              </span>\n              <el-radio-group v-model=\"form.isFrame\">\n                <el-radio label=\"0\">是</el-radio>\n                <el-radio label=\"1\">否</el-radio>\n              </el-radio-group>\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\" v-if=\"form.menuType != 'F'\">\n            <el-form-item prop=\"path\">\n              <span slot=\"label\">\n                <el-tooltip content=\"访问的路由地址，如：`user`，如外网地址需内链访问则以`http(s)://`开头\" placement=\"top\">\n                <i class=\"el-icon-question\"></i>\n                </el-tooltip>\n                路由地址\n              </span>\n              <el-input v-model=\"form.path\" placeholder=\"请输入路由地址\" />\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\" v-if=\"form.menuType == 'C'\">\n            <el-form-item prop=\"component\">\n              <span slot=\"label\">\n                <el-tooltip content=\"访问的组件路径，如：`system/user/index`，默认在`views`目录下\" placement=\"top\">\n                <i class=\"el-icon-question\"></i>\n                </el-tooltip>\n                组件路径\n              </span>\n              <el-input v-model=\"form.component\" placeholder=\"请输入组件路径\" />\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\" v-if=\"form.menuType != 'M'\">\n            <el-form-item prop=\"perms\">\n              <el-input v-model=\"form.perms\" placeholder=\"请输入权限标识\" maxlength=\"100\" />\n              <span slot=\"label\">\n                <el-tooltip content=\"控制器中定义的权限字符，如：@SaCheckPermission('system:user:list')\" placement=\"top\">\n                <i class=\"el-icon-question\"></i>\n                </el-tooltip>\n                权限字符\n              </span>\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\" v-if=\"form.menuType == 'C'\">\n            <el-form-item prop=\"queryParam\">\n              <el-input v-model=\"form.queryParam\" placeholder=\"请输入路由参数\" maxlength=\"255\" />\n              <span slot=\"label\">\n                <el-tooltip content='访问路由的默认传递参数，如：`{\"id\": 1, \"name\": \"ry\"}`' placement=\"top\">\n                <i class=\"el-icon-question\"></i>\n                </el-tooltip>\n                路由参数\n              </span>\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\" v-if=\"form.menuType == 'C'\">\n            <el-form-item prop=\"isCache\">\n              <span slot=\"label\">\n                <el-tooltip content=\"选择是则会被`keep-alive`缓存，需要匹配组件的`name`和地址保持一致\" placement=\"top\">\n                <i class=\"el-icon-question\"></i>\n                </el-tooltip>\n                是否缓存\n              </span>\n              <el-radio-group v-model=\"form.isCache\">\n                <el-radio label=\"0\">缓存</el-radio>\n                <el-radio label=\"1\">不缓存</el-radio>\n              </el-radio-group>\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\" v-if=\"form.menuType != 'F'\">\n            <el-form-item prop=\"visible\">\n              <span slot=\"label\">\n                <el-tooltip content=\"选择隐藏则路由将不会出现在侧边栏，但仍然可以访问\" placement=\"top\">\n                <i class=\"el-icon-question\"></i>\n                </el-tooltip>\n                显示状态\n              </span>\n              <el-radio-group v-model=\"form.visible\">\n                <el-radio\n                  v-for=\"dict in dict.type.sys_show_hide\"\n                  :key=\"dict.value\"\n                  :label=\"dict.value\"\n                >{{dict.label}}</el-radio>\n              </el-radio-group>\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\" v-if=\"form.menuType != 'F'\">\n            <el-form-item prop=\"status\">\n              <span slot=\"label\">\n                <el-tooltip content=\"选择停用则路由将不会出现在侧边栏，也不能被访问\" placement=\"top\">\n                <i class=\"el-icon-question\"></i>\n                </el-tooltip>\n                菜单状态\n              </span>\n              <el-radio-group v-model=\"form.status\">\n                <el-radio\n                  v-for=\"dict in dict.type.sys_normal_disable\"\n                  :key=\"dict.value\"\n                  :label=\"dict.value\"\n                >{{dict.label}}</el-radio>\n              </el-radio-group>\n            </el-form-item>\n          </el-col>\n        </el-row>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listMenu, getMenu, delMenu, addMenu, updateMenu } from \"@/api/system/menu\";\nimport Treeselect from \"@riophae/vue-treeselect\";\nimport \"@riophae/vue-treeselect/dist/vue-treeselect.css\";\nimport IconSelect from \"@/components/IconSelect\";\n\nexport default {\n  name: \"Menu\",\n  dicts: ['sys_show_hide', 'sys_normal_disable'],\n  components: { Treeselect, IconSelect },\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 菜单表格树数据\n      menuList: [],\n      // 菜单树选项\n      menuOptions: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 是否展开，默认全部折叠\n      isExpandAll: false,\n      // 重新渲染表格状态\n      refreshTable: true,\n      // 查询参数\n      queryParams: {\n        menuName: undefined,\n        visible: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        menuName: [\n          { required: true, message: \"菜单名称不能为空\", trigger: \"blur\" }\n        ],\n        orderNum: [\n          { required: true, message: \"菜单顺序不能为空\", trigger: \"blur\" }\n        ],\n        path: [\n          { required: true, message: \"路由地址不能为空\", trigger: \"blur\" }\n        ]\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    // 选择图标\n    selected(name) {\n      this.form.icon = name;\n    },\n    /** 查询菜单列表 */\n    getList() {\n      this.loading = true;\n      listMenu(this.queryParams).then(response => {\n        this.menuList = this.handleTree(response.data, \"menuId\");\n        this.loading = false;\n      });\n    },\n    /** 转换菜单数据结构 */\n    normalizer(node) {\n      if (node.children && !node.children.length) {\n        delete node.children;\n      }\n      return {\n        id: node.menuId,\n        label: node.menuName,\n        children: node.children\n      };\n    },\n    /** 查询菜单下拉树结构 */\n    getTreeselect() {\n      listMenu().then(response => {\n        this.menuOptions = [];\n        const menu = { menuId: 0, menuName: '主类目', children: [] };\n        menu.children = this.handleTree(response.data, \"menuId\");\n        this.menuOptions.push(menu);\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        menuId: undefined,\n        parentId: 0,\n        menuName: undefined,\n        icon: undefined,\n        menuType: \"M\",\n        orderNum: undefined,\n        isFrame: \"1\",\n        isCache: \"0\",\n        visible: \"0\",\n        status: \"0\"\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 新增按钮操作 */\n    handleAdd(row) {\n      this.reset();\n      this.getTreeselect();\n      if (row != null && row.menuId) {\n        this.form.parentId = row.menuId;\n      } else {\n        this.form.parentId = 0;\n      }\n      this.open = true;\n      this.title = \"添加菜单\";\n    },\n    /** 展开/折叠操作 */\n    toggleExpandAll() {\n      this.refreshTable = false;\n      this.isExpandAll = !this.isExpandAll;\n      this.$nextTick(() => {\n        this.refreshTable = true;\n      });\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.reset();\n      this.getTreeselect();\n      getMenu(row.menuId).then(response => {\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改菜单\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm: function() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          if (this.form.menuId != undefined) {\n            updateMenu(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            });\n          } else {\n            addMenu(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      this.$modal.confirm('是否确认删除名称为\"' + row.menuName + '\"的数据项？').then(function() {\n        return delMenu(row.menuId);\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {});\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/notice/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"公告标题\" prop=\"noticeTitle\">\n        <el-input\n          v-model=\"queryParams.noticeTitle\"\n          placeholder=\"请输入公告标题\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"操作人员\" prop=\"createBy\">\n        <el-input\n          v-model=\"queryParams.createBy\"\n          placeholder=\"请输入操作人员\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"类型\" prop=\"noticeType\">\n        <el-select v-model=\"queryParams.noticeType\" placeholder=\"公告类型\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.sys_notice_type\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:notice:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:notice:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:notice:remove']\"\n        >删除</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"noticeList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"序号\" align=\"center\" prop=\"noticeId\" width=\"100\" />\n      <el-table-column\n        label=\"公告标题\"\n        align=\"center\"\n        prop=\"noticeTitle\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column label=\"公告类型\" align=\"center\" prop=\"noticeType\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.sys_notice_type\" :value=\"scope.row.noticeType\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"状态\" align=\"center\" prop=\"status\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.sys_notice_status\" :value=\"scope.row.status\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建者\" align=\"center\" prop=\"createBy\" width=\"100\" />\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:notice:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:notice:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改公告对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"780px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-row>\n          <el-col :span=\"12\">\n            <el-form-item label=\"公告标题\" prop=\"noticeTitle\">\n              <el-input v-model=\"form.noticeTitle\" placeholder=\"请输入公告标题\" />\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\">\n            <el-form-item label=\"公告类型\" prop=\"noticeType\">\n              <el-select v-model=\"form.noticeType\" placeholder=\"请选择公告类型\">\n                <el-option\n                  v-for=\"dict in dict.type.sys_notice_type\"\n                  :key=\"dict.value\"\n                  :label=\"dict.label\"\n                  :value=\"dict.value\"\n                ></el-option>\n              </el-select>\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"24\">\n            <el-form-item label=\"状态\">\n              <el-radio-group v-model=\"form.status\">\n                <el-radio\n                  v-for=\"dict in dict.type.sys_notice_status\"\n                  :key=\"dict.value\"\n                  :label=\"dict.value\"\n                >{{dict.label}}</el-radio>\n              </el-radio-group>\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"24\">\n            <el-form-item label=\"内容\">\n              <editor v-model=\"form.noticeContent\" :min-height=\"192\"/>\n            </el-form-item>\n          </el-col>\n        </el-row>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listNotice, getNotice, delNotice, addNotice, updateNotice } from \"@/api/system/notice\";\n\nexport default {\n  name: \"Notice\",\n  dicts: ['sys_notice_status', 'sys_notice_type'],\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 公告表格数据\n      noticeList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        noticeTitle: undefined,\n        createBy: undefined,\n        status: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        noticeTitle: [\n          { required: true, message: \"公告标题不能为空\", trigger: \"blur\" }\n        ],\n        noticeType: [\n          { required: true, message: \"公告类型不能为空\", trigger: \"change\" }\n        ]\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询公告列表 */\n    getList() {\n      this.loading = true;\n      listNotice(this.queryParams).then(response => {\n        this.noticeList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        noticeId: undefined,\n        noticeTitle: undefined,\n        noticeType: undefined,\n        noticeContent: undefined,\n        status: \"0\"\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.noticeId)\n      this.single = selection.length!=1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加公告\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.reset();\n      const noticeId = row.noticeId || this.ids\n      getNotice(noticeId).then(response => {\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改公告\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm: function() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          if (this.form.noticeId != undefined) {\n            updateNotice(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            });\n          } else {\n            addNotice(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const noticeIds = row.noticeId || this.ids\n      this.$modal.confirm('是否确认删除公告编号为\"' + noticeIds + '\"的数据项？').then(function() {\n        return delNotice(noticeIds);\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {});\n    }\n  }\n};\n</script>"
  },
  {
    "path": "ruoyi-ui/src/views/system/official/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"来自谁的消息\" prop=\"fromUserId\">\n        <el-input\n          v-model=\"queryParams.fromUserId\"\n          placeholder=\"请输入来自谁的消息\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"给谁发的消息\" prop=\"toUserId\">\n        <el-input\n          v-model=\"queryParams.toUserId\"\n          placeholder=\"请输入给谁发的消息\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"标题\" prop=\"title\">\n        <el-input\n          v-model=\"queryParams.title\"\n          placeholder=\"请输入标题\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"是否已读\" prop=\"isRead\">\n        <el-input\n          v-model=\"queryParams.read\"\n          placeholder=\"请输入是否已读\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"来自那场组队的消息\" prop=\"groupId\">\n        <el-input\n          v-model=\"queryParams.groupId\"\n          placeholder=\"请输入来自那场组队的消息\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"来自那场活动的消息\" prop=\"activityId\">\n        <el-input\n          v-model=\"queryParams.activityId\"\n          placeholder=\"请输入来自那场活动的消息\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.createTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择创建时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item label=\"更新时间\" prop=\"updateTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.updateTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择更新时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:official:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:official:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:official:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:official:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"officialList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"ID\" align=\"center\" prop=\"officialId\" v-if=\"true\"/>\n      <el-table-column label=\"来自谁的消息\" align=\"center\" prop=\"fromUserId\" />\n      <el-table-column label=\"给谁发的消息\" align=\"center\" prop=\"toUserId\" />\n      <el-table-column label=\"标题\" align=\"center\" prop=\"title\" />\n      <el-table-column label=\"主体消息\" align=\"center\" prop=\"content\" />\n      <el-table-column label=\"是否已读\" align=\"center\" prop=\"isRead\" />\n      <el-table-column label=\"来自那场组队的消息\" align=\"center\" prop=\"groupId\" />\n      <el-table-column label=\"来自那场活动的消息\" align=\"center\" prop=\"activityId\" />\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"更新时间\" align=\"center\" prop=\"updateTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:official:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:official:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改官方消息对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"来自谁的消息\" prop=\"fromUserId\">\n          <el-input v-model=\"form.fromUserId\" placeholder=\"请输入来自谁的消息\" />\n        </el-form-item>\n        <el-form-item label=\"给谁发的消息\" prop=\"toUserId\">\n          <el-input v-model=\"form.toUserId\" placeholder=\"请输入给谁发的消息\" />\n        </el-form-item>\n        <el-form-item label=\"标题\" prop=\"title\">\n          <el-input v-model=\"form.title\" placeholder=\"请输入标题\" />\n        </el-form-item>\n        <el-form-item label=\"主体消息\">\n          <editor v-model=\"form.content\" :min-height=\"192\"/>\n        </el-form-item>\n        <el-form-item label=\"是否已读\" prop=\"isRead\">\n          <el-input v-model=\"form.read\" placeholder=\"请输入是否已读\" />\n        </el-form-item>\n        <el-form-item label=\"来自那场组队的消息\" prop=\"groupId\">\n          <el-input v-model=\"form.groupId\" placeholder=\"请输入来自那场组队的消息\" />\n        </el-form-item>\n        <el-form-item label=\"来自那场活动的消息\" prop=\"activityId\">\n          <el-input v-model=\"form.activityId\" placeholder=\"请输入来自那场活动的消息\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listOfficial, getOfficial, delOfficial, addOfficial, updateOfficial } from \"@/api/system/official\";\n\nexport default {\n  name: \"Official\",\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 官方消息表格数据\n      officialList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        fromUserId: undefined,\n        toUserId: undefined,\n        title: undefined,\n        content: undefined,\n        read: undefined,\n        groupId: undefined,\n        activityId: undefined,\n        createTime: undefined,\n        updateTime: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        officialId: [\n          { required: true, message: \"ID不能为空\", trigger: \"blur\" }\n        ],\n        fromUserId: [\n          { required: true, message: \"来自谁的消息不能为空\", trigger: \"blur\" }\n        ],\n        toUserId: [\n          { required: true, message: \"给谁发的消息不能为空\", trigger: \"blur\" }\n        ],\n        title: [\n          { required: true, message: \"标题不能为空\", trigger: \"blur\" }\n        ],\n        content: [\n          { required: true, message: \"主体消息不能为空\", trigger: \"blur\" }\n        ],\n        read: [\n          { required: true, message: \"是否已读不能为空\", trigger: \"blur\" }\n        ],\n        groupId: [\n          { required: true, message: \"来自那场组队的消息不能为空\", trigger: \"blur\" }\n        ],\n        activityId: [\n          { required: true, message: \"来自那场活动的消息不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询官方消息列表 */\n    getList() {\n      this.loading = true;\n      listOfficial(this.queryParams).then(response => {\n        this.officialList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        officialId: undefined,\n        fromUserId: undefined,\n        toUserId: undefined,\n        title: undefined,\n        content: undefined,\n        read: undefined,\n        groupId: undefined,\n        activityId: undefined,\n        createTime: undefined,\n        updateTime: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.officialId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加官方消息\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const officialId = row.officialId || this.ids\n      getOfficial(officialId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改官方消息\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.officialId != null) {\n            updateOfficial(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addOfficial(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const officialIds = row.officialId || this.ids;\n      this.$modal.confirm('是否确认删除官方消息编号为\"' + officialIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delOfficial(officialIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/official/export', {\n        ...this.queryParams\n      }, `official_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/organizer/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"电话号码\" prop=\"phone\">\n        <el-input\n          v-model=\"queryParams.phone\"\n          placeholder=\"请输入电话号码\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"名称\" prop=\"name\">\n        <el-input\n          v-model=\"queryParams.name\"\n          placeholder=\"请输入名称\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:organizer:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:organizer:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:organizer:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:organizer:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"organizerList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"ID\" align=\"center\" prop=\"organizerId\" v-if=\"true\"/>\n      <el-table-column label=\"电话号码\" align=\"center\" prop=\"phone\" />\n      <el-table-column label=\"名称\" align=\"center\" prop=\"name\" />\n      <el-table-column label=\"组织者标志图片\" align=\"center\" prop=\"logo\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <image-preview :src=\"scope.row.logo\" :width=\"50\" :height=\"50\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"需要链接的公众号或者文章地址（只能是派之城的，没有就不填，否则会白屏）\" align=\"center\" prop=\"content\" />\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"更新时间\" align=\"center\" prop=\"updateTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:organizer:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:organizer:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改活动主办方对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"电话号码\" prop=\"phone\">\n          <el-input v-model=\"form.phone\" placeholder=\"请输入电话号码\" />\n        </el-form-item>\n        <el-form-item label=\"名称\" prop=\"name\">\n          <el-input v-model=\"form.name\" placeholder=\"请输入名称\" />\n        </el-form-item>\n        <el-form-item label=\"组织者标志图片\" prop=\"logo\">\n          <image-upload v-model=\"form.logo\"/>\n        </el-form-item>\n        <el-form-item label=\"主办方介绍\">\n          <editor v-model=\"form.content\" :min-height=\"192\"/>\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listOrganizer, getOrganizer, delOrganizer, addOrganizer, updateOrganizer } from \"@/api/system/organizer\";\n\nexport default {\n  name: \"Organizer\",\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 活动主办方表格数据\n      organizerList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        phone: undefined,\n        name: undefined,\n        logo: undefined,\n        content: undefined,\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        organizerId: [\n          { required: true, message: \"ID不能为空\", trigger: \"blur\" }\n        ],\n        phone: [\n          { required: true, message: \"电话号码不能为空\", trigger: \"blur\" }\n        ],\n        name: [\n          { required: true, message: \"名称不能为空\", trigger: \"blur\" }\n        ],\n        content: [\n          { required: true, message: \"主办方介绍不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询活动主办方列表 */\n    getList() {\n      this.loading = true;\n      listOrganizer(this.queryParams).then(response => {\n        this.organizerList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        organizerId: undefined,\n        phone: undefined,\n        name: undefined,\n        logo: undefined,\n        content: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n        state: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.organizerId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加活动主办方\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const organizerId = row.organizerId || this.ids\n      getOrganizer(organizerId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改活动主办方\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.organizerId != null) {\n            updateOrganizer(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addOrganizer(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const organizerIds = row.organizerId || this.ids;\n      this.$modal.confirm('是否确认删除活动主办方编号为\"' + organizerIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delOrganizer(organizerIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/organizer/export', {\n        ...this.queryParams\n      }, `organizer_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/organizerTicket/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"名称\" prop=\"name\">\n        <el-input\n          v-model=\"queryParams.name\"\n          placeholder=\"请输入名称\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"关联主办方ID\" prop=\"organizerId\">\n        <el-input\n          v-model=\"queryParams.organizerId\"\n          placeholder=\"请输入关联主办方ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:organizerTicket:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:organizerTicket:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:organizerTicket:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:organizerTicket:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"organizerTicketList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"ID\" align=\"center\" prop=\"organizerTicketId\" v-if=\"true\"/>\n      <el-table-column label=\"名称\" align=\"center\" prop=\"name\" />\n      <el-table-column label=\"二维码图片\" align=\"center\" prop=\"qrImage\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <image-preview :src=\"scope.row.qrImage\" :width=\"50\" :height=\"50\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"logo图\" align=\"center\" prop=\"logoImage\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <image-preview :src=\"scope.row.logoImage\" :width=\"50\" :height=\"50\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"关联主办方ID\" align=\"center\" prop=\"organizerId\" />\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:organizerTicket:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:organizerTicket:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改主办方票务对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"名称\" prop=\"name\">\n          <el-input v-model=\"form.name\" placeholder=\"请输入名称\" />\n        </el-form-item>\n        <el-form-item label=\"二维码图片\" prop=\"qrImage\">\n          <image-upload v-model=\"form.qrImage\"/>\n        </el-form-item>\n        <el-form-item label=\"logo图\" prop=\"logoImage\">\n          <image-upload v-model=\"form.logoImage\"/>\n        </el-form-item>\n        <el-form-item label=\"关联主办方ID\" prop=\"organizerId\">\n          <el-input v-model=\"form.organizerId\" placeholder=\"请输入关联主办方ID\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listOrganizerTicket, getOrganizerTicket, delOrganizerTicket, addOrganizerTicket, updateOrganizerTicket } from \"@/api/system/organizerTicket\";\n\nexport default {\n  name: \"OrganizerTicket\",\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 主办方票务表格数据\n      organizerTicketList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        name: undefined,\n        qrImage: undefined,\n        logoImage: undefined,\n        organizerId: undefined,\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        organizerTicketId: [\n          { required: true, message: \"ID不能为空\", trigger: \"blur\" }\n        ],\n        name: [\n          { required: true, message: \"名称不能为空\", trigger: \"blur\" }\n        ],\n        qrImage: [\n          { required: true, message: \"二维码图片不能为空\", trigger: \"blur\" }\n        ],\n        logoImage: [\n          { required: true, message: \"logo图不能为空\", trigger: \"blur\" }\n        ],\n        organizerId: [\n          { required: true, message: \"关联主办方ID不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询主办方票务列表 */\n    getList() {\n      this.loading = true;\n      listOrganizerTicket(this.queryParams).then(response => {\n        this.organizerTicketList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        organizerTicketId: undefined,\n        name: undefined,\n        qrImage: undefined,\n        logoImage: undefined,\n        organizerId: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n        state: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.organizerTicketId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加主办方票务\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const organizerTicketId = row.organizerTicketId || this.ids\n      getOrganizerTicket(organizerTicketId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改主办方票务\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.organizerTicketId != null) {\n            updateOrganizerTicket(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addOrganizerTicket(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const organizerTicketIds = row.organizerTicketId || this.ids;\n      this.$modal.confirm('是否确认删除主办方票务编号为\"' + organizerTicketIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delOrganizerTicket(organizerTicketIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/organizerTicket/export', {\n        ...this.queryParams\n      }, `organizerTicket_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/oss/config.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"配置key\" prop=\"configKey\">\n        <el-input\n          v-model=\"queryParams.configKey\"\n          placeholder=\"配置key\"\n          clearable\n          size=\"small\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"桶名称\" prop=\"bucketName\">\n        <el-input\n          v-model=\"queryParams.bucketName\"\n          placeholder=\"请输入桶名称\"\n          clearable\n          size=\"small\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"是否默认\" prop=\"status\">\n        <el-select v-model=\"queryParams.status\" placeholder=\"请选择状态\" clearable size=\"small\">\n          <el-option key=\"0\" label=\"是\" value=\"0\"/>\n          <el-option key=\"1\" label=\"否\" value=\"1\"/>\n        </el-select>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:oss:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:oss:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:oss:remove']\"\n        >删除</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"ossConfigList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"主建\" align=\"center\" prop=\"ossConfigId\" v-if=\"false\"/>\n      <el-table-column label=\"配置key\" align=\"center\" prop=\"configKey\" />\n      <el-table-column label=\"访问站点\" align=\"center\" prop=\"endpoint\" width=\"200\" />\n      <el-table-column label=\"自定义域名\" align=\"center\" prop=\"domain\" width=\"200\" />\n      <el-table-column label=\"桶名称\" align=\"center\" prop=\"bucketName\" />\n      <el-table-column label=\"前缀\" align=\"center\" prop=\"prefix\" />\n      <el-table-column label=\"域\" align=\"center\" prop=\"region\" />\n      <el-table-column label=\"桶权限类型\" align=\"center\" prop=\"accessPolicy\">\n        <template slot-scope=\"scope\">\n          <el-tag type=\"warning\" v-if=\"scope.row.accessPolicy === '0'\">private</el-tag>\n          <el-tag type=\"success\" v-if=\"scope.row.accessPolicy === '1'\">public</el-tag>\n          <el-tag type=\"info\" v-if=\"scope.row.accessPolicy === '2'\">custom</el-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"是否默认\" align=\"center\" prop=\"status\">\n        <template slot-scope=\"scope\">\n          <el-switch\n            v-model=\"scope.row.status\"\n            active-value=\"0\"\n            inactive-value=\"1\"\n            @change=\"handleStatusChange(scope.row)\"\n          ></el-switch>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:oss:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:oss:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total > 0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改对象存储配置对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"800px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"120px\">\n        <el-form-item label=\"配置key\" prop=\"configKey\">\n          <el-input v-model=\"form.configKey\" placeholder=\"请输入配置key\" />\n        </el-form-item>\n        <el-form-item label=\"访问站点\" prop=\"endpoint\">\n          <el-input v-model=\"form.endpoint\" placeholder=\"请输入访问站点\" />\n        </el-form-item>\n        <el-form-item label=\"自定义域名\" prop=\"domain\">\n          <el-input v-model=\"form.domain\" placeholder=\"请输入自定义域名\" />\n        </el-form-item>\n        <el-form-item label=\"accessKey\" prop=\"accessKey\">\n          <el-input v-model=\"form.accessKey\" placeholder=\"请输入accessKey\" />\n        </el-form-item>\n        <el-form-item label=\"secretKey\" prop=\"secretKey\">\n          <el-input v-model=\"form.secretKey\" placeholder=\"请输入秘钥\" show-password />\n        </el-form-item>\n        <el-form-item label=\"桶名称\" prop=\"bucketName\">\n          <el-input v-model=\"form.bucketName\" placeholder=\"请输入桶名称\" />\n        </el-form-item>\n        <el-form-item label=\"前缀\" prop=\"prefix\">\n          <el-input v-model=\"form.prefix\" placeholder=\"请输入前缀\" />\n        </el-form-item>\n        <el-form-item label=\"是否HTTPS\">\n          <el-radio-group v-model=\"form.isHttps\">\n            <el-radio\n              v-for=\"dict in dict.type.sys_yes_no\"\n              :key=\"dict.value\"\n              :label=\"dict.value\"\n            >{{dict.label}}</el-radio>\n          </el-radio-group>\n        </el-form-item>\n        <el-form-item label=\"桶权限类型\">\n          <el-radio-group v-model=\"form.accessPolicy\">\n            <el-radio label=\"0\">private</el-radio>\n            <el-radio label=\"1\">public</el-radio>\n            <el-radio label=\"2\">custom</el-radio>\n          </el-radio-group>\n        </el-form-item>\n        <el-form-item label=\"域\" prop=\"region\">\n          <el-input v-model=\"form.region\" placeholder=\"请输入域\" />\n        </el-form-item>\n        <el-form-item label=\"备注\" prop=\"remark\">\n          <el-input v-model=\"form.remark\" type=\"textarea\" placeholder=\"请输入内容\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\n\nimport {\n  listOssConfig,\n  getOssConfig,\n  delOssConfig,\n  addOssConfig,\n  updateOssConfig,\n  changeOssConfigStatus\n} from \"@/api/system/ossConfig\";\n\nexport default {\n  name: \"OssConfig\",\n  dicts: ['sys_yes_no'],\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 导出遮罩层\n      exportLoading: false,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 对象存储配置表格数据\n      ossConfigList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 是否https字典\n      isHttpsOptions: [],\n      // 状态(0正常 1停用)字典\n      statusOptions: [],\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        configKey: undefined,\n        bucketName: undefined,\n        status: undefined,\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        configKey: [\n          { required: true, message: \"configKey不能为空\", trigger: \"blur\" },\n        ],\n        accessKey: [\n          { required: true, message: \"accessKey不能为空\", trigger: \"blur\" },\n          {\n            min: 2,\n            max: 200,\n            message: \"accessKey长度必须介于 2 和 100 之间\",\n            trigger: \"blur\",\n          },\n        ],\n        secretKey: [\n          { required: true, message: \"secretKey不能为空\", trigger: \"blur\" },\n          {\n            min: 2,\n            max: 100,\n            message: \"secretKey长度必须介于 2 和 100 之间\",\n            trigger: \"blur\",\n          },\n        ],\n        bucketName: [\n          { required: true, message: \"bucketName不能为空\", trigger: \"blur\" },\n          {\n            min: 2,\n            max: 100,\n            message: \"bucketName长度必须介于 2 和 100 之间\",\n            trigger: \"blur\",\n          },\n        ],\n        endpoint: [\n          { required: true, message: \"endpoint不能为空\", trigger: \"blur\" },\n          {\n            min: 2,\n            max: 100,\n            message: \"endpoint名称长度必须介于 2 和 100 之间\",\n            trigger: \"blur\",\n          },\n        ],\n        accessPolicy:[\n          { required: true, message: \"accessPolicy不能为空\", trigger: \"blur\" }\n        ]\n      },\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询对象存储配置列表 */\n    getList() {\n      this.loading = true;\n      listOssConfig(this.queryParams).then((response) => {\n        this.ossConfigList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        ossConfigId: undefined,\n        configKey: undefined,\n        accessKey: undefined,\n        secretKey: undefined,\n        bucketName: undefined,\n        prefix: undefined,\n        endpoint: undefined,\n        domain: undefined,\n        isHttps: \"N\",\n        accessPolicy: \"1\",\n        region: undefined,\n        status: \"1\",\n        remark: undefined,\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.ossConfigId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加对象存储配置\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const ossConfigId = row.ossConfigId || this.ids;\n      getOssConfig(ossConfigId).then((response) => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改对象存储配置\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.ossConfigId != null) {\n            updateOssConfig(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addOssConfig(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const ossConfigIds = row.ossConfigId || this.ids;\n      this.$modal.confirm('是否确认删除对象存储配置编号为\"' + ossConfigIds + '\"的数据项?').then(() => {\n        this.loading = true;\n        return delOssConfig(ossConfigIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    // 对象存储配置状态修改\n    handleStatusChange(row) {\n      let text = row.status === \"0\" ? \"启用\" : \"停用\";\n      this.$modal.confirm('确认要\"' + text + '\"\"' + row.configKey + '\"配置吗?').then(() => {\n        return changeOssConfigStatus(row.ossConfigId, row.status, row.configKey);\n      }).then(() => {\n        this.getList()\n        this.$modal.msgSuccess(text + \"成功\");\n      }).catch(() => {\n        row.status = row.status === \"0\" ? \"1\" : \"0\";\n      })\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/oss/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"文件名\" prop=\"fileName\">\n        <el-input\n          v-model=\"queryParams.fileName\"\n          placeholder=\"请输入文件名\"\n          clearable\n          size=\"small\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"原名\" prop=\"originalName\">\n        <el-input\n          v-model=\"queryParams.originalName\"\n          placeholder=\"请输入原名\"\n          clearable\n          size=\"small\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"文件后缀\" prop=\"fileSuffix\">\n        <el-input\n          v-model=\"queryParams.fileSuffix\"\n          placeholder=\"请输入文件后缀\"\n          clearable\n          size=\"small\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"创建时间\">\n        <el-date-picker\n          v-model=\"daterangeCreateTime\"\n          size=\"small\"\n          style=\"width: 240px\"\n          value-format=\"yyyy-MM-dd HH:mm:ss\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"['00:00:00', '23:59:59']\"\n        ></el-date-picker>\n      </el-form-item>\n      <el-form-item label=\"上传人\" prop=\"createBy\">\n        <el-input\n          v-model=\"queryParams.createBy\"\n          placeholder=\"请输入上传人\"\n          clearable\n          size=\"small\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"服务商\" prop=\"service\">\n        <el-input\n          v-model=\"queryParams.service\"\n          placeholder=\"请输入服务商\"\n          clearable\n          size=\"small\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleFile\"\n          v-hasPermi=\"['system:oss:upload']\"\n        >上传文件</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleImage\"\n          v-hasPermi=\"['system:oss:upload']\"\n        >上传图片</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:oss:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          :type=\"previewListResource ? 'danger' : 'warning'\"\n          plain\n          size=\"mini\"\n          @click=\"handlePreviewListResource(!previewListResource)\"\n          v-hasPermi=\"['system:oss:edit']\"\n        >预览开关 : {{previewListResource ? \"禁用\" : \"启用\"}}</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"info\"\n          plain\n          icon=\"el-icon-s-operation\"\n          size=\"mini\"\n          @click=\"handleOssConfig\"\n          v-hasPermi=\"['system:oss:list']\"\n        >配置管理</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"ossList\" @selection-change=\"handleSelectionChange\"\n              :header-cell-class-name=\"handleHeaderClass\"\n              @header-click=\"handleHeaderCLick\"\n              v-if=\"showTable\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"对象存储主键\" align=\"center\" prop=\"ossId\" v-if=\"false\"/>\n      <el-table-column label=\"文件名\" align=\"center\" prop=\"fileName\" />\n      <el-table-column label=\"原名\" align=\"center\" prop=\"originalName\" />\n      <el-table-column label=\"文件后缀\" align=\"center\" prop=\"fileSuffix\" />\n      <el-table-column label=\"文件展示\" align=\"center\" prop=\"url\">\n        <template slot-scope=\"scope\">\n          <ImagePreview\n            v-if=\"previewListResource && checkFileSuffix(scope.row.fileSuffix)\"\n            :width=100 :height=100\n            :src=\"scope.row.url\"\n            :preview-src-list=\"[scope.row.url]\"/>\n          <span v-text=\"scope.row.url\"\n                v-if=\"!checkFileSuffix(scope.row.fileSuffix) || !previewListResource\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\"\n                       sortable=\"custom\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"上传人\" align=\"center\" prop=\"createBy\" />\n      <el-table-column label=\"服务商\" align=\"center\" prop=\"service\"\n                       sortable=\"custom\"/>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleDownload(scope.row)\"\n            v-hasPermi=\"['system:oss:download']\"\n          >下载</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:oss:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改OSS对象存储对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"文件名\">\n          <fileUpload v-model=\"form.file\" v-if=\"type === 0\"/>\n          <imageUpload v-model=\"form.file\" v-if=\"type === 1\"/>\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listOss, delOss } from \"@/api/system/oss\";\n\nexport default {\n  name: \"Oss\",\n  data() {\n    return {\n      showTable: true,\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 导出遮罩层\n      exportLoading: false,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // OSS对象存储表格数据\n      ossList: [],\n      // 弹出层标题\n      title: \"\",\n      // 弹出层标题\n      type: 0,\n      // 是否显示弹出层\n      open: false,\n      // 预览列表图片\n      previewListResource: true,\n      // 创建时间时间范围\n      daterangeCreateTime: [],\n      // 默认排序\n      defaultSort: {prop: 'createTime', order: 'ascending'},\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        fileName: undefined,\n        originalName: undefined,\n        fileSuffix: undefined,\n        url: undefined,\n        createTime: undefined,\n        createBy: undefined,\n        service: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        file: [\n          { required: true, message: \"文件不能为空\", trigger: \"blur\" }\n        ]\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询OSS对象存储列表 */\n    getList() {\n      this.loading = true;\n      this.queryParams.params = {};\n      if (null != this.daterangeCreateTime && '' != this.daterangeCreateTime) {\n        this.queryParams.params[\"beginCreateTime\"] = this.daterangeCreateTime[0];\n        this.queryParams.params[\"endCreateTime\"] = this.daterangeCreateTime[1];\n      }\n      this.getConfigKey(\"sys.oss.previewListResource\").then(response => {\n        this.previewListResource = response.msg === undefined ? true : response.msg === 'true';\n      });\n      listOss(this.queryParams).then(response => {\n        this.ossList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n        this.showTable = true;\n      });\n    },\n    checkFileSuffix(fileSuffix) {\n      let arr = [\"png\", \"jpg\", \"jpeg\"];\n      return arr.some(type => {\n        return fileSuffix.indexOf(type) > -1;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        file: undefined,\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.showTable = false;\n      this.daterangeCreateTime = [];\n      this.resetForm(\"queryForm\");\n      this.queryParams.orderByColumn = this.defaultSort.prop;\n      this.queryParams.isAsc = this.defaultSort.order;\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.ossId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    // 设置列的排序为我们自定义的排序\n    handleHeaderClass({column}) {\n      column.order = column.multiOrder\n    },\n    // 点击表头进行排序\n    handleHeaderCLick(column) {\n      if (column.sortable !== 'custom') {\n        return\n      }\n      switch (column.multiOrder) {\n        case 'descending':\n          column.multiOrder = 'ascending';\n          break;\n        case 'ascending':\n          column.multiOrder = '';\n          break;\n        default:\n          column.multiOrder = 'descending';\n          break;\n      }\n      this.handleOrderChange(column.property, column.multiOrder)\n    },\n    handleOrderChange(prop, order) {\n      let orderByArr = this.queryParams.orderByColumn ? this.queryParams.orderByColumn.split(\",\") : [];\n      let isAscArr = this.queryParams.isAsc ? this.queryParams.isAsc.split(\",\") : [];\n      let propIndex = orderByArr.indexOf(prop)\n      if (propIndex !== -1) {\n        if (order) {\n          //排序里已存在 只修改排序\n          isAscArr[propIndex] = order;\n        } else {\n          //如果order为null 则删除排序字段和属性\n          isAscArr.splice(propIndex, 1);//删除排序\n          orderByArr.splice(propIndex, 1);//删除属性\n        }\n      } else {\n        //排序里不存在则新增排序\n        orderByArr.push(prop);\n        isAscArr.push(order);\n      }\n      //合并排序\n      this.queryParams.orderByColumn = orderByArr.join(\",\");\n      this.queryParams.isAsc = isAscArr.join(\",\");\n      this.getList();\n    },\n    /** 任务日志列表查询 */\n    handleOssConfig() {\n      this.$router.push({ path: '/system/oss-config/index'})\n    },\n    /** 文件按钮操作 */\n    handleFile() {\n      this.reset();\n      this.open = true;\n      this.title = \"上传文件\";\n      this.type = 0;\n    },\n    /** 图片按钮操作 */\n    handleImage() {\n      this.reset();\n      this.open = true;\n      this.title = \"上传图片\";\n      this.type = 1;\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.open = false;\n      this.getList();\n    },\n    /** 下载按钮操作 */\n    handleDownload(row) {\n      this.$download.oss(row.ossId)\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const ossIds = row.ossId || this.ids;\n      this.$modal.confirm('是否确认删除OSS对象存储编号为\"' + ossIds + '\"的数据项?').then(() => {\n        this.loading = true;\n        return delOss(ossIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    // 预览列表图片状态修改\n    handlePreviewListResource(previewListResource) {\n      let text = previewListResource ? \"启用\" : \"停用\";\n      this.$modal.confirm('确认要\"' + text + '\"\"预览列表图片\"配置吗?').then(() => {\n        return this.updateConfigByKey(\"sys.oss.previewListResource\", previewListResource);\n      }).then(() => {\n        this.getList()\n        this.$modal.msgSuccess(text + \"成功\");\n      }).catch(() => {})\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/post/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"岗位编码\" prop=\"postCode\">\n        <el-input\n          v-model=\"queryParams.postCode\"\n          placeholder=\"请输入岗位编码\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"岗位名称\" prop=\"postName\">\n        <el-input\n          v-model=\"queryParams.postName\"\n          placeholder=\"请输入岗位名称\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select v-model=\"queryParams.status\" placeholder=\"岗位状态\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.sys_normal_disable\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:post:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:post:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:post:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:post:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"postList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"岗位编号\" align=\"center\" prop=\"postId\" />\n      <el-table-column label=\"岗位编码\" align=\"center\" prop=\"postCode\" />\n      <el-table-column label=\"岗位名称\" align=\"center\" prop=\"postName\" />\n      <el-table-column label=\"岗位排序\" align=\"center\" prop=\"postSort\" />\n      <el-table-column label=\"状态\" align=\"center\" prop=\"status\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.sys_normal_disable\" :value=\"scope.row.status\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:post:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:post:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改岗位对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"岗位名称\" prop=\"postName\">\n          <el-input v-model=\"form.postName\" placeholder=\"请输入岗位名称\" />\n        </el-form-item>\n        <el-form-item label=\"岗位编码\" prop=\"postCode\">\n          <el-input v-model=\"form.postCode\" placeholder=\"请输入编码名称\" />\n        </el-form-item>\n        <el-form-item label=\"岗位顺序\" prop=\"postSort\">\n          <el-input-number v-model=\"form.postSort\" controls-position=\"right\" :min=\"0\" />\n        </el-form-item>\n        <el-form-item label=\"岗位状态\" prop=\"status\">\n          <el-radio-group v-model=\"form.status\">\n            <el-radio\n              v-for=\"dict in dict.type.sys_normal_disable\"\n              :key=\"dict.value\"\n              :label=\"dict.value\"\n            >{{dict.label}}</el-radio>\n          </el-radio-group>\n        </el-form-item>\n        <el-form-item label=\"备注\" prop=\"remark\">\n          <el-input v-model=\"form.remark\" type=\"textarea\" placeholder=\"请输入内容\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listPost, getPost, delPost, addPost, updatePost } from \"@/api/system/post\";\n\nexport default {\n  name: \"Post\",\n  dicts: ['sys_normal_disable'],\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 岗位表格数据\n      postList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        postCode: undefined,\n        postName: undefined,\n        status: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        postName: [\n          { required: true, message: \"岗位名称不能为空\", trigger: \"blur\" }\n        ],\n        postCode: [\n          { required: true, message: \"岗位编码不能为空\", trigger: \"blur\" }\n        ],\n        postSort: [\n          { required: true, message: \"岗位顺序不能为空\", trigger: \"blur\" }\n        ]\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询岗位列表 */\n    getList() {\n      this.loading = true;\n      listPost(this.queryParams).then(response => {\n        this.postList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        postId: undefined,\n        postCode: undefined,\n        postName: undefined,\n        postSort: 0,\n        status: \"0\",\n        remark: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.postId)\n      this.single = selection.length!=1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加岗位\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.reset();\n      const postId = row.postId || this.ids\n      getPost(postId).then(response => {\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改岗位\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm: function() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          if (this.form.postId != undefined) {\n            updatePost(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            });\n          } else {\n            addPost(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const postIds = row.postId || this.ids;\n      this.$modal.confirm('是否确认删除岗位编号为\"' + postIds + '\"的数据项？').then(function() {\n        return delPost(postIds);\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {});\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/post/export', {\n        ...this.queryParams\n      }, `post_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/pzc_order/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"用户ID\" prop=\"userId\">\n        <el-input\n          v-model=\"queryParams.userId\"\n          placeholder=\"请输入用户ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"活动ID\" prop=\"activityId\">\n        <el-input\n          v-model=\"queryParams.activityId\"\n          placeholder=\"请输入活动ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"订单金额\" prop=\"money\">\n        <el-input\n          v-model=\"queryParams.money\"\n          placeholder=\"请输入订单金额\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"订单状态\" prop=\"orderStatus\">\n        <el-input\n          v-model=\"queryParams.orderStatus\"\n          placeholder=\"请输入订单状态\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"订单类型\" prop=\"type\">\n        <el-input\n          v-model=\"queryParams.type\"\n          placeholder=\"请输入订单类型\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"外部订单号\" prop=\"outOrderNum\">\n        <el-input\n          v-model=\"queryParams.outOrderNum\"\n          placeholder=\"请输入外部订单号\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"描述\" prop=\"intro\">\n        <el-input\n          v-model=\"queryParams.intro\"\n          placeholder=\"请输入描述\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"标题\" prop=\"title\">\n        <el-input\n          v-model=\"queryParams.title\"\n          placeholder=\"请输入标题\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"订单创建时间\" prop=\"createTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.createTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择订单创建时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item label=\"订单更新时间\" prop=\"updateTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.updateTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择订单更新时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:pzc_order:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:pzc_order:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:pzc_order:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:pzc_order:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"pzc_orderList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"订单ID\" align=\"center\" prop=\"orderId\" v-if=\"true\"/>\n      <el-table-column label=\"用户ID\" align=\"center\" prop=\"userId\" />\n      <el-table-column label=\"活动ID\" align=\"center\" prop=\"activityId\" />\n      <el-table-column label=\"订单金额\" align=\"center\" prop=\"money\" />\n      <el-table-column label=\"订单状态\" align=\"center\" prop=\"orderStatus\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.order_status\" :value=\"scope.row.orderStatus\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"订单类型\" align=\"center\" prop=\"type\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.order_type\" :value=\"scope.row.type\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"外部订单号\" align=\"center\" prop=\"outOrderNum\" />\n      <el-table-column label=\"描述\" align=\"center\" prop=\"intro\" />\n      <el-table-column label=\"标题\" align=\"center\" prop=\"title\" />\n      <el-table-column label=\"订单创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"订单更新时间\" align=\"center\" prop=\"updateTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:pzc_order:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:pzc_order:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改订单对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"用户ID\" prop=\"userId\">\n          <el-input v-model=\"form.userId\" placeholder=\"请输入用户ID\" />\n        </el-form-item>\n        <el-form-item label=\"活动ID\" prop=\"activityId\">\n          <el-input v-model=\"form.activityId\" placeholder=\"请输入活动ID\" />\n        </el-form-item>\n        <el-form-item label=\"订单金额\" prop=\"money\">\n          <el-input v-model=\"form.money\" placeholder=\"请输入订单金额\" />\n        </el-form-item>\n        <el-form-item label=\"订单状态\" prop=\"orderStatus\">\n          <el-input v-model=\"form.orderStatus\" placeholder=\"请输入订单状态\" />\n        </el-form-item>\n        <el-form-item label=\"订单类型\" prop=\"type\">\n          <el-input v-model=\"form.type\" placeholder=\"请输入订单类型\" />\n        </el-form-item>\n        <el-form-item label=\"外部订单号\" prop=\"outOrderNum\">\n          <el-input v-model=\"form.outOrderNum\" placeholder=\"请输入外部订单号\" />\n        </el-form-item>\n        <el-form-item label=\"描述\" prop=\"intro\">\n          <el-input v-model=\"form.intro\" placeholder=\"请输入描述\" />\n        </el-form-item>\n        <el-form-item label=\"标题\" prop=\"title\">\n          <el-input v-model=\"form.title\" placeholder=\"请输入标题\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listPzc_order, getPzc_order, delPzc_order, addPzc_order, updatePzc_order } from \"@/api/system/pzc_order\";\n\nexport default {\n  name: \"Pzc_order\",\n  dicts: ['order_type', 'order_status'],\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 订单表格数据\n      pzc_orderList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        userId: undefined,\n        activityId: undefined,\n        money: undefined,\n        orderStatus: undefined,\n        type: undefined,\n        outOrderNum: undefined,\n        intro: undefined,\n        title: undefined,\n        createTime: undefined,\n        updateTime: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        orderId: [\n          { required: true, message: \"订单ID不能为空\", trigger: \"blur\" }\n        ],\n        userId: [\n          { required: true, message: \"用户ID不能为空\", trigger: \"blur\" }\n        ],\n        activityId: [\n          { required: true, message: \"活动ID不能为空\", trigger: \"blur\" }\n        ],\n        money: [\n          { required: true, message: \"订单金额不能为空\", trigger: \"blur\" }\n        ],\n        orderStatus: [\n          { required: true, message: \"订单状态不能为空\", trigger: \"blur\" }\n        ],\n        type: [\n          { required: true, message: \"订单类型不能为空\", trigger: \"blur\" }\n        ],\n        outOrderNum: [\n          { required: true, message: \"外部订单号不能为空\", trigger: \"blur\" }\n        ],\n        intro: [\n          { required: true, message: \"描述不能为空\", trigger: \"blur\" }\n        ],\n        title: [\n          { required: true, message: \"标题不能为空\", trigger: \"blur\" }\n        ],\n        createTime: [\n          { required: true, message: \"订单创建时间不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询订单列表 */\n    getList() {\n      this.loading = true;\n      listPzc_order(this.queryParams).then(response => {\n        this.pzc_orderList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        orderId: undefined,\n        userId: undefined,\n        activityId: undefined,\n        money: undefined,\n        orderStatus: undefined,\n        type: undefined,\n        outOrderNum: undefined,\n        intro: undefined,\n        title: undefined,\n        createTime: undefined,\n        updateTime: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.orderId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加订单\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const orderId = row.orderId || this.ids\n      getPzc_order(orderId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改订单\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.orderId != null) {\n            updatePzc_order(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addPzc_order(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const orderIds = row.orderId || this.ids;\n      this.$modal.confirm('是否确认删除订单编号为\"' + orderIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delPzc_order(orderIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/pzc_order/export', {\n        ...this.queryParams\n      }, `pzc_order_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/pzc_user/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"用户在小程序端的 openId 唯一\" prop=\"openid\">\n        <el-input\n          v-model=\"queryParams.openid\"\n          placeholder=\"请输入用户在小程序端的 openId 唯一\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"派币余额\" prop=\"money\">\n        <el-input\n          v-model=\"queryParams.money\"\n          placeholder=\"请输入派币余额\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"用户等级\" prop=\"userLevel\">\n        <el-input\n          v-model=\"queryParams.userLevel\"\n          placeholder=\"请输入用户等级\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"用户累计积分\" prop=\"integration\">\n        <el-input\n          v-model=\"queryParams.integration\"\n          placeholder=\"请输入用户累计积分\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"用户现有积分\" prop=\"integrationNow\">\n        <el-input\n          v-model=\"queryParams.integrationNow\"\n          placeholder=\"请输入用户现有积分\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"真实姓名\" prop=\"realname\">\n        <el-input\n          v-model=\"queryParams.realname\"\n          placeholder=\"请输入真实姓名\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"昵称\" prop=\"nickname\">\n        <el-input\n          v-model=\"queryParams.nickname\"\n          placeholder=\"请输入昵称\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"用户性别 0 男  1 女  2 未知\" prop=\"sex\">\n        <el-select v-model=\"queryParams.sex\" placeholder=\"请选择用户性别 0 男  1 女  2 未知\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.sys_user_sex\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"手机号\" prop=\"phone\">\n        <el-input\n          v-model=\"queryParams.phone\"\n          placeholder=\"请输入手机号\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"地址\" prop=\"address\">\n        <el-input\n          v-model=\"queryParams.address\"\n          placeholder=\"请输入地址\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"个人介绍\" prop=\"intro\">\n        <el-input\n          v-model=\"queryParams.intro\"\n          placeholder=\"请输入个人介绍\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"年龄\" prop=\"age\">\n        <el-input\n          v-model=\"queryParams.age\"\n          placeholder=\"请输入年龄\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"星座\" prop=\"constellation\">\n        <el-select v-model=\"queryParams.constellation\" placeholder=\"请选择星座\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.constellation\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"人格类型\" prop=\"mbti\">\n        <el-select v-model=\"queryParams.mbti\" placeholder=\"请选择人格类型\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.mbti\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"兴趣爱好\" prop=\"hobby\">\n        <el-input\n          v-model=\"queryParams.hobby\"\n          placeholder=\"请输入兴趣爱好\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"学校\" prop=\"school\">\n        <el-input\n          v-model=\"queryParams.school\"\n          placeholder=\"请输入学校\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"职业\" prop=\"occupation\">\n        <el-input\n          v-model=\"queryParams.occupation\"\n          placeholder=\"请输入职业\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态 是否被封禁\" prop=\"state\">\n        <el-select v-model=\"queryParams.state\" placeholder=\"请选择状态 是否被封禁\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.state\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:pzc_user:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:pzc_user:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:pzc_user:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:pzc_user:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"pzc_userList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"用户主键\" align=\"center\" prop=\"userId\" v-if=\"true\"/>\n      <el-table-column label=\"用户在小程序端的 openId 唯一\" align=\"center\" prop=\"openid\" />\n      <el-table-column label=\"派币余额\" align=\"center\" prop=\"money\" />\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button type=\"success\" @click=\"handlerUpdateMoney(scope.row)\">修改</el-button>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"用户等级\" align=\"center\" prop=\"userLevel\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.user_level\" :value=\"scope.row.userLevel\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"用户累计积分\" align=\"center\" prop=\"integration\" />\n      <el-table-column label=\"用户现有积分\" align=\"center\" prop=\"integrationNow\" />\n      <el-table-column label=\"真实姓名\" align=\"center\" prop=\"realname\" />\n      <el-table-column label=\"昵称\" align=\"center\" prop=\"nickname\" />\n      <el-table-column label=\"用户性别 0 女  1 男  2 未知\" align=\"center\" prop=\"sex\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.sys_user_sex\" :value=\"scope.row.sex\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"手机号\" align=\"center\" prop=\"phone\" />\n      <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <image-preview :src=\"scope.row.avatar\" :width=\"50\" :height=\"50\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"地址\" align=\"center\" prop=\"address\" />\n      <el-table-column label=\"个人介绍\" align=\"center\" prop=\"intro\" />\n      <el-table-column label=\"年龄\" align=\"center\" prop=\"age\" />\n      <el-table-column label=\"星座\" align=\"center\" prop=\"constellation\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.constellation\" :value=\"scope.row.constellation\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"人格类型\" align=\"center\" prop=\"mbti\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.mbti\" :value=\"scope.row.mbti\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"兴趣爱好\" align=\"center\" prop=\"hobby\" >\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.hobby\" :value=\"scope.row.user_hobby\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"学校\" align=\"center\" prop=\"school\" />\n      <el-table-column label=\"职业\" align=\"center\" prop=\"occupation\" />\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"喜欢的音乐风格\" align=\"center\" prop=\"musicStyle\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.music_style\" :value=\"scope.row.musicStyle ? scope.row.musicStyle.split(',') : []\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"状态 是否被封禁\" align=\"center\" prop=\"state\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.state\" :value=\"scope.row.state\"/>\n        </template>\n      </el-table-column>\n\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改用户对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"用户在小程序端的 openId 唯一\" prop=\"openid\">\n          <el-input v-model=\"form.openid\" placeholder=\"请输入用户在小程序端的 openId 唯一\" />\n        </el-form-item>\n        <el-form-item label=\"派币余额\" prop=\"money\">\n          <el-input v-model=\"form.money\" placeholder=\"请输入派币余额\" />\n        </el-form-item>\n        <el-form-item label=\"用户等级\" prop=\"userLevel\">\n          <el-input v-model=\"form.userLevel\" placeholder=\"请输入用户等级\" />\n        </el-form-item>\n        <el-form-item label=\"用户累计积分\" prop=\"integration\">\n          <el-input v-model=\"form.integration\" placeholder=\"请输入用户累计积分\" />\n        </el-form-item>\n        <el-form-item label=\"用户现有积分\" prop=\"integrationNow\">\n          <el-input v-model=\"form.integrationNow\" placeholder=\"请输入用户现有积分\" />\n        </el-form-item>\n        <el-form-item label=\"真实姓名\" prop=\"realname\">\n          <el-input v-model=\"form.realname\" placeholder=\"请输入真实姓名\" />\n        </el-form-item>\n        <el-form-item label=\"昵称\" prop=\"nickname\">\n          <el-input v-model=\"form.nickname\" placeholder=\"请输入昵称\" />\n        </el-form-item>\n        <el-form-item label=\"用户性别 0 男  1 女  2 未知\" prop=\"sex\">\n          <el-radio-group v-model=\"form.sex\">\n            <el-radio\n              v-for=\"dict in dict.type.sys_user_sex\"\n              :key=\"dict.value\"\n              :label=\"parseInt(dict.value)\"\n            >{{dict.label}}</el-radio>\n          </el-radio-group>\n        </el-form-item>\n        <el-form-item label=\"手机号\" prop=\"phone\">\n          <el-input v-model=\"form.phone\" placeholder=\"请输入手机号\" />\n        </el-form-item>\n        <el-form-item label=\"头像\" prop=\"avatar\">\n          <image-upload v-model=\"form.avatar\"/>\n        </el-form-item>\n        <el-form-item label=\"地址\" prop=\"address\">\n          <el-input v-model=\"form.address\" placeholder=\"请输入地址\" />\n        </el-form-item>\n        <el-form-item label=\"个人介绍\" prop=\"intro\">\n          <el-input v-model=\"form.intro\" placeholder=\"请输入个人介绍\" />\n        </el-form-item>\n        <el-form-item label=\"年龄\" prop=\"age\">\n          <el-input v-model=\"form.age\" placeholder=\"请输入年龄\" />\n        </el-form-item>\n        <el-form-item label=\"星座\" prop=\"constellation\">\n          <el-select v-model=\"form.constellation\" placeholder=\"请选择星座\">\n            <el-option\n              v-for=\"dict in dict.type.constellation\"\n              :key=\"dict.value\"\n              :label=\"dict.label\"\n              :value=\"dict.value\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"人格类型\" prop=\"mbti\">\n          <el-select v-model=\"form.mbti\" placeholder=\"请选择人格类型\">\n            <el-option\n              v-for=\"dict in dict.type.mbti\"\n              :key=\"dict.value\"\n              :label=\"dict.label\"\n              :value=\"dict.value\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"兴趣爱好\" prop=\"hobby\">\n          <el-input v-model=\"form.hobby\" placeholder=\"请输入兴趣爱好\" />\n        </el-form-item>\n        <el-form-item label=\"学校\" prop=\"school\">\n          <el-input v-model=\"form.school\" placeholder=\"请输入学校\" />\n        </el-form-item>\n        <el-form-item label=\"职业\" prop=\"occupation\">\n          <el-input v-model=\"form.occupation\" placeholder=\"请输入职业\" />\n        </el-form-item>\n        <el-form-item label=\"喜欢的音乐风格\" prop=\"musicStyle\">\n          <el-checkbox-group v-model=\"form.musicStyle\">\n            <el-checkbox\n              v-for=\"dict in dict.type.music_style\"\n              :key=\"dict.value\"\n              :label=\"dict.value\">\n              {{dict.label}}\n            </el-checkbox>\n          </el-checkbox-group>\n        </el-form-item>\n        <el-form-item label=\"状态 是否被封禁\" prop=\"state\">\n          <el-select v-model=\"form.state\" placeholder=\"请选择状态 是否被封禁\">\n            <el-option\n              v-for=\"dict in dict.type.state\"\n              :key=\"dict.value\"\n              :label=\"dict.label\"\n              :value=\"parseInt(dict.value)\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport {addPzc_user, delPzc_user, getPzc_user, listPzc_user, update_money, updatePzc_user} from \"@/api/system/pzc_user\";\n\nexport default {\n  name: \"Pzc_user\",\n  dicts: ['sys_user_sex', 'music_style', 'mbti', 'state', 'constellation','user_hobby'],\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 用户表格数据\n      pzc_userList: [],\n\n      newMoney: {\n        userId: undefined,\n        money: undefined\n      },\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        openid: undefined,\n        money: undefined,\n        userLevel: undefined,\n        integration: undefined,\n        integrationNow: undefined,\n        realname: undefined,\n        nickname: undefined,\n        sex: undefined,\n        phone: undefined,\n        avatar: undefined,\n        address: undefined,\n        intro: undefined,\n        age: undefined,\n        constellation: undefined,\n        mbti: undefined,\n        hobby: undefined,\n        school: undefined,\n        occupation: undefined,\n        musicStyle: undefined,\n        state: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        userId: [\n          { required: true, message: \"用户主键不能为空\", trigger: \"blur\" }\n        ],\n        openid: [\n          { required: true, message: \"用户在小程序端的 openId 唯一不能为空\", trigger: \"blur\" }\n        ],\n        nickname: [\n          { required: true, message: \"昵称不能为空\", trigger: \"blur\" }\n        ],\n        sex: [\n          { required: true, message: \"用户性别 0 男  1 女  2 未知不能为空\", trigger: \"change\" }\n        ],\n        createTime: [\n          { required: true, message: \"创建时间不能为空\", trigger: \"blur\" }\n        ],\n        updateTime: [\n          { required: true, message: \"更新时间不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询用户列表 */\n    getList() {\n      this.loading = true;\n      listPzc_user(this.queryParams).then(response => {\n        this.pzc_userList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        userId: undefined,\n        openid: undefined,\n        money: undefined,\n        userLevel: undefined,\n        integration: undefined,\n        integrationNow: undefined,\n        realname: undefined,\n        nickname: undefined,\n        sex: undefined,\n        phone: undefined,\n        avatar: undefined,\n        address: undefined,\n        intro: undefined,\n        age: undefined,\n        constellation: undefined,\n        mbti: undefined,\n        hobby: undefined,\n        school: undefined,\n        occupation: undefined,\n        createTime: undefined,\n        musicStyle: [],\n        updateTime: undefined,\n        state: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.userId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.$router.push({ path: \"/system/pzc_user/todoList\" })\n      // this.reset();\n      // this.open = true;\n      // this.title = \"添加用户\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const userId = row.userId || this.ids\n      getPzc_user(userId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.form.musicStyle = this.form.musicStyle.split(\",\");\n        this.open = true;\n        this.title = \"修改用户\";\n      });\n    },\n\n    handlerUpdateMoney(row)\n    {\n      this.loading = true;\n      this.$prompt('请输入用户余额', '提示', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n      }).then(({ value }) => {\n        this.$message({\n          type: 'success',\n          message: '更新后的余额是: ' + value\n        });\n        this.newMoney.money = value;\n        this.newMoney.userId = row.userId || this.ids;\n\n        update_money(this.newMoney).then(response => {\n          this.$modal.msgSuccess(\"修改成功\");\n          this.open = false;\n          this.getList();\n        }).finally(() => {\n          this.buttonLoading = false;\n        });\n      }).catch(() => {\n        this.$message({\n          type: 'info',\n          message: '取消输入'\n        });\n      });\n\n\n\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          this.form.musicStyle = this.form.musicStyle.join(\",\");\n          if (this.form.userId != null) {\n            updatePzc_user(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addPzc_user(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const userIds = row.userId || this.ids;\n      this.$modal.confirm('是否确认删除用户编号为\"' + userIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delPzc_user(userIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/pzc_user/export', {\n        ...this.queryParams\n      }, `pzc_user_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/pzc_user/todoList/index.vue",
    "content": "<template>\n\n  <div class=\"app-container\">\n    <div>\n      <h1>待办事项</h1>\n      <el-result icon=\"success\" :title=\"'代办事件条数为'+tableData.length.toString()\"/>\n    </div>\n    <el-input v-model=\"input\" placeholder=\"请输入需要填入的代办事项\" @change=\"handleAdd(input)\"></el-input>\n    <hr>\n\n    <el-table\n      :data=\"tableData\"\n      ref=\"multipleTable\"\n      @selection-change=\"handleSelectionChange\"\n      style=\"width: 100%\">\n      <el-table-column type=\"selection\" width=\"50\" align=\"center\" />\n      <el-table-column\n        label=\"编号\"\n        width=\"180\">\n        <template slot-scope=\"scope\">\n          <span style=\"margin-left: 10px\">{{ scope.row.id }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"事件名称\"\n        width=\"180\">\n        <template slot-scope=\"scope\">\n          <el-popover trigger=\"hover\" placement=\"top\">\n            <p>事件名称: {{ scope.row.name }}</p>\n            <div slot=\"reference\" class=\"name-wrapper\">\n              <el-tag size=\"medium\">{{ scope.row.name }}</el-tag>\n            </div>\n          </el-popover>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"完成状态\"\n        width=\"100\">\n        <template slot-scope=\"scope\">\n          <el-checkbox id=\"checkbox\" v-model=\"scope.row.complete\"></el-checkbox>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            @click=\"handleEdit(scope.$index, scope.row)\">编辑\n          </el-button>\n          <el-button\n            size=\"mini\"\n            type=\"danger\"\n            @click=\"handleDelete(scope.$index, scope.row)\">删除\n          </el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <el-button type=\"danger\" @click=\"deleteData\" v-show=\"tableData.length\">批量删除代办事项</el-button>\n    <el-button type=\"danger\" @click=\"deleteCompleteData\" v-show=\"tableData.length\">批量清除已完成代办事项</el-button>\n  </div>\n\n\n</template>\n\n\n<script>\nexport default {\n  data() {\n    return {\n      input: '',\n      tableData: JSON.parse(localStorage.getItem(\"tableData\")) || [],\n      multipleSelection: []\n    }\n  },\n  watch:{\n    \"tableData\":{\n      deep:true,\n      handler(newData){\n        localStorage.setItem(\"tableData\",JSON.stringify(newData))\n      }\n    }\n  },\n  methods: {\n    handleEdit(index, row) {\n      this.$prompt('请输入新的代办事项', '提示', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        inputValue: row.name\n      }).then(({ value }) => {\n        row.name=value\n        this.$message({\n          type: 'success',\n          message: '修改后的代办事项是: ' + value\n        });\n      }).catch(() => {\n        this.$message({\n          type: 'info',\n          message: '取消输入'\n        });\n      });\n    },\n    handleDelete(index,row) {\n      console.log(row);\n      this.tableData = this.tableData.filter(item => item.id !== row.id); //过滤掉选中的那个值并且 重新赋值给Data\n    },\n    handleAdd(data) {\n      let lastItemId =0\n      if(this.tableData.length>0)\n      {\n        lastItemId = this.tableData[this.tableData.length - 1].id;\n      }\n      // Create a new item object with input as name and last item's ID + 1\n      const newItem = {\n        id: lastItemId + 1,\n        name: this.input,\n        complete: false\n      };\n      console.log(\"当前用户输入的代办事项为: \", this.input);\n\n      // Add the new item to the tableData array\n      this.tableData.push(newItem);\n      this.input=''\n    }\n    ,\n    handleSelectionChange(val) {\n      this.multipleSelection = val;\n      console.log(\"多选\",this.multipleSelection)\n    },\n    batchDelete(){\n      console.log(\"删除方法1 全局匹配\")\n      // console.log(\"233\",this.multipleSelection.includes(2))\n      this.tableData = this.tableData.filter(item => !this.multipleSelection.includes(item));\n    },\n    deleteData() {\n      console.log(\"删除方法2 只匹配Id\")\n      this.tableData = this.tableData.filter(item => !this.multipleSelection.some(data => data.id === item.id));\n    },\n    deleteCompleteData()\n    {\n      console.log(\"删除已完成的代办事项\")\n      console.log(\"2333\",JSON.parse(localStorage.getItem(\"tableData\")).filter(item => !item.complete === true))\n      localStorage.setItem(\"tableData\",JSON.stringify(JSON.parse(localStorage.getItem(\"tableData\")).filter(item => !item.complete === true)));\n      this.tableData=JSON.parse(localStorage.getItem(\"tableData\"))\n    }\n  },\n  created() {\n\n  }\n\n\n}\n</script>\n\n<style scoped>\n     #checkbox.el-checkbox__input.is-checked.el-checkbox__inner{\n       background-color: greenyellow;\n       border-color: greenyellow;\n\n     }\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/region/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"省\" prop=\"base\">\n        <el-input\n          v-model=\"queryParams.base\"\n          placeholder=\"请输入省\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"地区名称\" prop=\"name\">\n        <el-input\n          v-model=\"queryParams.name\"\n          placeholder=\"请输入地区名称\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.createTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择创建时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item label=\"更新时间\" prop=\"updateTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.updateTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择更新时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:region:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:region:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:region:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:region:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"regionList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"地区id\" align=\"center\" prop=\"regionId\" v-if=\"true\"/>\n      <el-table-column label=\"省\" align=\"center\" prop=\"base\" />\n      <el-table-column label=\"地区名称\" align=\"center\" prop=\"name\" />\n      <el-table-column label=\"城市主活动图\" align=\"center\" prop=\"imgUrl\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <image-preview :src=\"scope.row.imgUrl\" :width=\"50\" :height=\"50\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"更新时间\" align=\"center\" prop=\"updateTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:region:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:region:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改地区对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"省\" prop=\"base\">\n          <el-input v-model=\"form.base\" placeholder=\"请输入省\" />\n        </el-form-item>\n        <el-form-item label=\"地区名称\" prop=\"name\">\n          <el-input v-model=\"form.name\" placeholder=\"请输入地区名称\" />\n        </el-form-item>\n        <el-form-item label=\"城市主活动图\" prop=\"imgUrl\">\n          <image-upload v-model=\"form.imgUrl\"/>\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listRegion, getRegion, delRegion, addRegion, updateRegion } from \"@/api/system/region\";\n\nexport default {\n  name: \"Region\",\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 地区表格数据\n      regionList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        base: undefined,\n        name: undefined,\n        imgUrl: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        regionId: [\n          { required: true, message: \"地区id不能为空\", trigger: \"blur\" }\n        ],\n        base: [\n          { required: true, message: \"省不能为空\", trigger: \"blur\" }\n        ],\n        name: [\n          { required: true, message: \"地区名称不能为空\", trigger: \"blur\" }\n        ],\n        imgUrl: [\n          { required: true, message: \"城市主活动图不能为空\", trigger: \"blur\" }\n        ],\n        createTime: [\n          { required: true, message: \"创建时间不能为空\", trigger: \"blur\" }\n        ],\n        updateTime: [\n          { required: true, message: \"更新时间不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询地区列表 */\n    getList() {\n      this.loading = true;\n      listRegion(this.queryParams).then(response => {\n        this.regionList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        regionId: undefined,\n        base: undefined,\n        name: undefined,\n        imgUrl: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n        state: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.regionId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加地区\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const regionId = row.regionId || this.ids\n      getRegion(regionId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改地区\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.regionId != null) {\n            updateRegion(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addRegion(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const regionIds = row.regionId || this.ids;\n      this.$modal.confirm('是否确认删除地区编号为\"' + regionIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delRegion(regionIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/region/export', {\n        ...this.queryParams\n      }, `region_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/role/authUser.vue",
    "content": "<template>\n  <div class=\"app-container\">\n     <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\">\n      <el-form-item label=\"用户名称\" prop=\"userName\">\n        <el-input\n          v-model=\"queryParams.userName\"\n          placeholder=\"请输入用户名称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"手机号码\" prop=\"phonenumber\">\n        <el-input\n          v-model=\"queryParams.phonenumber\"\n          placeholder=\"请输入手机号码\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"openSelectUser\"\n          v-hasPermi=\"['system:role:add']\"\n        >添加用户</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-circle-close\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"cancelAuthUserAll\"\n          v-hasPermi=\"['system:role:remove']\"\n        >批量取消授权</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-close\"\n          size=\"mini\"\n          @click=\"handleClose\"\n        >关闭</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"userList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"用户名称\" prop=\"userName\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"用户昵称\" prop=\"nickName\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"邮箱\" prop=\"email\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"手机\" prop=\"phonenumber\" :show-overflow-tooltip=\"true\" />\n      <el-table-column label=\"状态\" align=\"center\" prop=\"status\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.sys_normal_disable\" :value=\"scope.row.status\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-circle-close\"\n            @click=\"cancelAuthUser(scope.row)\"\n            v-hasPermi=\"['system:role:remove']\"\n          >取消授权</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n    <select-user ref=\"select\" :roleId=\"queryParams.roleId\" @ok=\"handleQuery\" />\n  </div>\n</template>\n\n<script>\nimport { allocatedUserList, authUserCancel, authUserCancelAll } from \"@/api/system/role\";\nimport selectUser from \"./selectUser\";\n\nexport default {\n  name: \"AuthUser\",\n  dicts: ['sys_normal_disable'],\n  components: { selectUser },\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 选中用户组\n      userIds: [],\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 用户表格数据\n      userList: [],\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        roleId: undefined,\n        userName: undefined,\n        phonenumber: undefined\n      }\n    };\n  },\n  created() {\n    const roleId = this.$route.params && this.$route.params.roleId;\n    if (roleId) {\n      this.queryParams.roleId = roleId;\n      this.getList();\n    }\n  },\n  methods: {\n    /** 查询授权用户列表 */\n    getList() {\n      this.loading = true;\n      allocatedUserList(this.queryParams).then(response => {\n          this.userList = response.rows;\n          this.total = response.total;\n          this.loading = false;\n        }\n      );\n    },\n    // 返回按钮\n    handleClose() {\n      const obj = { path: \"/system/role\" };\n      this.$tab.closeOpenPage(obj);\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.userIds = selection.map(item => item.userId)\n      this.multiple = !selection.length\n    },\n    /** 打开授权用户表弹窗 */\n    openSelectUser() {\n      this.$refs.select.show();\n    },\n    /** 取消授权按钮操作 */\n    cancelAuthUser(row) {\n      const roleId = this.queryParams.roleId;\n      this.$modal.confirm('确认要取消该用户\"' + row.userName + '\"角色吗？').then(function() {\n        return authUserCancel({ userId: row.userId, roleId: roleId });\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"取消授权成功\");\n      }).catch(() => {});\n    },\n    /** 批量取消授权按钮操作 */\n    cancelAuthUserAll(row) {\n      const roleId = this.queryParams.roleId;\n      const userIds = this.userIds.join(\",\");\n      this.$modal.confirm('是否取消选中用户授权数据项？').then(function() {\n        return authUserCancelAll({ roleId: roleId, userIds: userIds });\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"取消授权成功\");\n      }).catch(() => {});\n    }\n  }\n};\n</script>"
  },
  {
    "path": "ruoyi-ui/src/views/system/role/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\">\n      <el-form-item label=\"角色名称\" prop=\"roleName\">\n        <el-input\n          v-model=\"queryParams.roleName\"\n          placeholder=\"请输入角色名称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"权限字符\" prop=\"roleKey\">\n        <el-input\n          v-model=\"queryParams.roleKey\"\n          placeholder=\"请输入权限字符\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          v-model=\"queryParams.status\"\n          placeholder=\"角色状态\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <el-option\n            v-for=\"dict in dict.type.sys_normal_disable\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"创建时间\">\n        <el-date-picker\n          v-model=\"dateRange\"\n          style=\"width: 240px\"\n          value-format=\"yyyy-MM-dd HH:mm:ss\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"['00:00:00', '23:59:59']\"\n        ></el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:role:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:role:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:role:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:role:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"roleList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"角色编号\" prop=\"roleId\" width=\"120\" />\n      <el-table-column label=\"角色名称\" prop=\"roleName\" :show-overflow-tooltip=\"true\" width=\"150\" />\n      <el-table-column label=\"权限字符\" prop=\"roleKey\" :show-overflow-tooltip=\"true\" width=\"150\" />\n      <el-table-column label=\"显示顺序\" prop=\"roleSort\" width=\"100\" />\n      <el-table-column label=\"状态\" align=\"center\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <el-switch\n            v-model=\"scope.row.status\"\n            active-value=\"0\"\n            inactive-value=\"1\"\n            @change=\"handleStatusChange(scope.row)\"\n          ></el-switch>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\" v-if=\"scope.row.roleId !== 1\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:role:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:role:remove']\"\n          >删除</el-button>\n          <el-dropdown size=\"mini\" @command=\"(command) => handleCommand(command, scope.row)\" v-hasPermi=\"['system:role:edit']\">\n            <el-button size=\"mini\" type=\"text\" icon=\"el-icon-d-arrow-right\">更多</el-button>\n            <el-dropdown-menu slot=\"dropdown\">\n              <el-dropdown-item command=\"handleDataScope\" icon=\"el-icon-circle-check\"\n                v-hasPermi=\"['system:role:edit']\">数据权限</el-dropdown-item>\n              <el-dropdown-item command=\"handleAuthUser\" icon=\"el-icon-user\"\n                v-hasPermi=\"['system:role:edit']\">分配用户</el-dropdown-item>\n            </el-dropdown-menu>\n          </el-dropdown>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改角色配置对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"100px\">\n        <el-form-item label=\"角色名称\" prop=\"roleName\">\n          <el-input v-model=\"form.roleName\" placeholder=\"请输入角色名称\" />\n        </el-form-item>\n        <el-form-item prop=\"roleKey\">\n          <span slot=\"label\">\n            <el-tooltip content=\"控制器中定义的权限字符，如：@PreAuthorize(`@ss.hasRole('admin')`)\" placement=\"top\">\n              <i class=\"el-icon-question\"></i>\n            </el-tooltip>\n            权限字符\n          </span>\n          <el-input v-model=\"form.roleKey\" placeholder=\"请输入权限字符\" />\n        </el-form-item>\n        <el-form-item label=\"角色顺序\" prop=\"roleSort\">\n          <el-input-number v-model=\"form.roleSort\" controls-position=\"right\" :min=\"0\" />\n        </el-form-item>\n        <el-form-item label=\"状态\">\n          <el-radio-group v-model=\"form.status\">\n            <el-radio\n              v-for=\"dict in dict.type.sys_normal_disable\"\n              :key=\"dict.value\"\n              :label=\"dict.value\"\n            >{{dict.label}}</el-radio>\n          </el-radio-group>\n        </el-form-item>\n        <el-form-item label=\"菜单权限\">\n          <el-checkbox v-model=\"menuExpand\" @change=\"handleCheckedTreeExpand($event, 'menu')\">展开/折叠</el-checkbox>\n          <el-checkbox v-model=\"menuNodeAll\" @change=\"handleCheckedTreeNodeAll($event, 'menu')\">全选/全不选</el-checkbox>\n          <el-checkbox v-model=\"form.menuCheckStrictly\" @change=\"handleCheckedTreeConnect($event, 'menu')\">父子联动</el-checkbox>\n          <el-tree\n            class=\"tree-border\"\n            :data=\"menuOptions\"\n            show-checkbox\n            ref=\"menu\"\n            node-key=\"id\"\n            :check-strictly=\"!form.menuCheckStrictly\"\n            empty-text=\"加载中，请稍候\"\n            :props=\"defaultProps\"\n          ></el-tree>\n        </el-form-item>\n        <el-form-item label=\"备注\">\n          <el-input v-model=\"form.remark\" type=\"textarea\" placeholder=\"请输入内容\"></el-input>\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n\n    <!-- 分配角色数据权限对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"openDataScope\" width=\"500px\" append-to-body>\n      <el-form :model=\"form\" label-width=\"80px\">\n        <el-form-item label=\"角色名称\">\n          <el-input v-model=\"form.roleName\" :disabled=\"true\" />\n        </el-form-item>\n        <el-form-item label=\"权限字符\">\n          <el-input v-model=\"form.roleKey\" :disabled=\"true\" />\n        </el-form-item>\n        <el-form-item label=\"权限范围\">\n          <el-select v-model=\"form.dataScope\" @change=\"dataScopeSelectChange\">\n            <el-option\n              v-for=\"item in dataScopeOptions\"\n              :key=\"item.value\"\n              :label=\"item.label\"\n              :value=\"item.value\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"数据权限\" v-show=\"form.dataScope == 2\">\n          <el-checkbox v-model=\"deptExpand\" @change=\"handleCheckedTreeExpand($event, 'dept')\">展开/折叠</el-checkbox>\n          <el-checkbox v-model=\"deptNodeAll\" @change=\"handleCheckedTreeNodeAll($event, 'dept')\">全选/全不选</el-checkbox>\n          <el-checkbox v-model=\"form.deptCheckStrictly\" @change=\"handleCheckedTreeConnect($event, 'dept')\">父子联动</el-checkbox>\n          <el-tree\n            class=\"tree-border\"\n            :data=\"deptOptions\"\n            show-checkbox\n            default-expand-all\n            ref=\"dept\"\n            node-key=\"id\"\n            :check-strictly=\"!form.deptCheckStrictly\"\n            empty-text=\"加载中，请稍候\"\n            :props=\"defaultProps\"\n          ></el-tree>\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitDataScope\">确 定</el-button>\n        <el-button @click=\"cancelDataScope\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listRole, getRole, delRole, addRole, updateRole, dataScope, changeRoleStatus, deptTreeSelect } from \"@/api/system/role\";\nimport { treeselect as menuTreeselect, roleMenuTreeselect } from \"@/api/system/menu\";\n\nexport default {\n  name: \"Role\",\n  dicts: ['sys_normal_disable'],\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 角色表格数据\n      roleList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 是否显示弹出层（数据权限）\n      openDataScope: false,\n      menuExpand: false,\n      menuNodeAll: false,\n      deptExpand: true,\n      deptNodeAll: false,\n      // 日期范围\n      dateRange: [],\n      // 数据范围选项\n      dataScopeOptions: [\n        {\n          value: \"1\",\n          label: \"全部数据权限\"\n        },\n        {\n          value: \"2\",\n          label: \"自定数据权限\"\n        },\n        {\n          value: \"3\",\n          label: \"本部门数据权限\"\n        },\n        {\n          value: \"4\",\n          label: \"本部门及以下数据权限\"\n        },\n        {\n          value: \"5\",\n          label: \"仅本人数据权限\"\n        }\n      ],\n      // 菜单列表\n      menuOptions: [],\n      // 部门列表\n      deptOptions: [],\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        roleName: undefined,\n        roleKey: undefined,\n        status: undefined\n      },\n      // 表单参数\n      form: {},\n      defaultProps: {\n        children: \"children\",\n        label: \"label\"\n      },\n      // 表单校验\n      rules: {\n        roleName: [\n          { required: true, message: \"角色名称不能为空\", trigger: \"blur\" }\n        ],\n        roleKey: [\n          { required: true, message: \"权限字符不能为空\", trigger: \"blur\" }\n        ],\n        roleSort: [\n          { required: true, message: \"角色顺序不能为空\", trigger: \"blur\" }\n        ]\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询角色列表 */\n    getList() {\n      this.loading = true;\n      listRole(this.addDateRange(this.queryParams, this.dateRange)).then(response => {\n          this.roleList = response.rows;\n          this.total = response.total;\n          this.loading = false;\n        }\n      );\n    },\n    /** 查询菜单树结构 */\n    getMenuTreeselect() {\n      menuTreeselect().then(response => {\n        this.menuOptions = response.data;\n      });\n    },\n    // 所有菜单节点数据\n    getMenuAllCheckedKeys() {\n      // 目前被选中的菜单节点\n      let checkedKeys = this.$refs.menu.getCheckedKeys();\n      // 半选中的菜单节点\n      let halfCheckedKeys = this.$refs.menu.getHalfCheckedKeys();\n      checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);\n      return checkedKeys;\n    },\n    // 所有部门节点数据\n    getDeptAllCheckedKeys() {\n      // 目前被选中的部门节点\n      let checkedKeys = this.$refs.dept.getCheckedKeys();\n      // 半选中的部门节点\n      let halfCheckedKeys = this.$refs.dept.getHalfCheckedKeys();\n      checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);\n      return checkedKeys;\n    },\n    /** 根据角色ID查询菜单树结构 */\n    getRoleMenuTreeselect(roleId) {\n      return roleMenuTreeselect(roleId).then(response => {\n        this.menuOptions = response.data.menus;\n        return response;\n      });\n    },\n    /** 根据角色ID查询部门树结构 */\n    getDeptTree(roleId) {\n      return deptTreeSelect(roleId).then(response => {\n        this.deptOptions = response.data.depts;\n        return response;\n      });\n    },\n    // 角色状态修改\n    handleStatusChange(row) {\n      let text = row.status === \"0\" ? \"启用\" : \"停用\";\n      this.$modal.confirm('确认要\"' + text + '\"\"' + row.roleName + '\"角色吗？').then(function() {\n        return changeRoleStatus(row.roleId, row.status);\n      }).then(() => {\n        this.$modal.msgSuccess(text + \"成功\");\n      }).catch(function() {\n        row.status = row.status === \"0\" ? \"1\" : \"0\";\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 取消按钮（数据权限）\n    cancelDataScope() {\n      this.openDataScope = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      if (this.$refs.menu != undefined) {\n        this.$refs.menu.setCheckedKeys([]);\n      }\n      this.menuExpand = false,\n      this.menuNodeAll = false,\n      this.deptExpand = true,\n      this.deptNodeAll = false,\n      this.form = {\n        roleId: undefined,\n        roleName: undefined,\n        roleKey: undefined,\n        roleSort: 0,\n        status: \"0\",\n        menuIds: [],\n        deptIds: [],\n        menuCheckStrictly: true,\n        deptCheckStrictly: true,\n        remark: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.dateRange = [];\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.roleId)\n      this.single = selection.length!=1\n      this.multiple = !selection.length\n    },\n    // 更多操作触发\n    handleCommand(command, row) {\n      switch (command) {\n        case \"handleDataScope\":\n          this.handleDataScope(row);\n          break;\n        case \"handleAuthUser\":\n          this.handleAuthUser(row);\n          break;\n        default:\n          break;\n      }\n    },\n    // 树权限（展开/折叠）\n    handleCheckedTreeExpand(value, type) {\n      if (type == 'menu') {\n        let treeList = this.menuOptions;\n        for (let i = 0; i < treeList.length; i++) {\n          this.$refs.menu.store.nodesMap[treeList[i].id].expanded = value;\n        }\n      } else if (type == 'dept') {\n        let treeList = this.deptOptions;\n        for (let i = 0; i < treeList.length; i++) {\n          this.$refs.dept.store.nodesMap[treeList[i].id].expanded = value;\n        }\n      }\n    },\n    // 树权限（全选/全不选）\n    handleCheckedTreeNodeAll(value, type) {\n      if (type == 'menu') {\n        this.$refs.menu.setCheckedNodes(value ? this.menuOptions: []);\n      } else if (type == 'dept') {\n        this.$refs.dept.setCheckedNodes(value ? this.deptOptions: []);\n      }\n    },\n    // 树权限（父子联动）\n    handleCheckedTreeConnect(value, type) {\n      if (type == 'menu') {\n        this.form.menuCheckStrictly = value ? true: false;\n      } else if (type == 'dept') {\n        this.form.deptCheckStrictly = value ? true: false;\n      }\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.getMenuTreeselect();\n      this.open = true;\n      this.title = \"添加角色\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.reset();\n      const roleId = row.roleId || this.ids\n      const roleMenu = this.getRoleMenuTreeselect(roleId);\n      getRole(roleId).then(response => {\n        this.form = response.data;\n        this.open = true;\n        this.$nextTick(() => {\n          roleMenu.then(res => {\n            let checkedKeys = res.data.checkedKeys\n            checkedKeys.forEach((v) => {\n                this.$nextTick(()=>{\n                    this.$refs.menu.setChecked(v, true ,false);\n                })\n            })\n          });\n        });\n        this.title = \"修改角色\";\n      });\n    },\n    /** 选择角色权限范围触发 */\n    dataScopeSelectChange(value) {\n      if(value !== '2') {\n        this.$refs.dept.setCheckedKeys([]);\n      }\n    },\n    /** 分配数据权限操作 */\n    handleDataScope(row) {\n      this.reset();\n      const deptTreeSelect = this.getDeptTree(row.roleId);\n      getRole(row.roleId).then(response => {\n        this.form = response.data;\n        this.openDataScope = true;\n        this.$nextTick(() => {\n          deptTreeSelect.then(res => {\n            this.$refs.dept.setCheckedKeys(res.data.checkedKeys);\n          });\n        });\n        this.title = \"分配数据权限\";\n      });\n    },\n    /** 分配用户操作 */\n    handleAuthUser: function(row) {\n      const roleId = row.roleId;\n      this.$router.push(\"/system/role-auth/user/\" + roleId);\n    },\n    /** 提交按钮 */\n    submitForm: function() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          if (this.form.roleId != undefined) {\n            this.form.menuIds = this.getMenuAllCheckedKeys();\n            updateRole(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            });\n          } else {\n            this.form.menuIds = this.getMenuAllCheckedKeys();\n            addRole(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            });\n          }\n        }\n      });\n    },\n    /** 提交按钮（数据权限） */\n    submitDataScope: function() {\n      if (this.form.roleId != undefined) {\n        this.form.deptIds = this.getDeptAllCheckedKeys();\n        dataScope(this.form).then(response => {\n          this.$modal.msgSuccess(\"修改成功\");\n          this.openDataScope = false;\n          this.getList();\n        });\n      }\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const roleIds = row.roleId || this.ids;\n      this.$modal.confirm('是否确认删除角色编号为\"' + roleIds + '\"的数据项？').then(function() {\n        return delRole(roleIds);\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {});\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/role/export', {\n        ...this.queryParams\n      }, `role_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/role/selectUser.vue",
    "content": "<template>\n  <!-- 授权用户 -->\n  <el-dialog title=\"选择用户\" :visible.sync=\"visible\" width=\"800px\" top=\"5vh\" append-to-body>\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\">\n      <el-form-item label=\"用户名称\" prop=\"userName\">\n        <el-input\n          v-model=\"queryParams.userName\"\n          placeholder=\"请输入用户名称\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"手机号码\" prop=\"phonenumber\">\n        <el-input\n          v-model=\"queryParams.phonenumber\"\n          placeholder=\"请输入手机号码\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n    <el-row>\n      <el-table @row-click=\"clickRow\" ref=\"table\" :data=\"userList\" @selection-change=\"handleSelectionChange\" height=\"260px\">\n        <el-table-column type=\"selection\" width=\"55\"></el-table-column>\n        <el-table-column label=\"用户名称\" prop=\"userName\" :show-overflow-tooltip=\"true\" />\n        <el-table-column label=\"用户昵称\" prop=\"nickName\" :show-overflow-tooltip=\"true\" />\n        <el-table-column label=\"邮箱\" prop=\"email\" :show-overflow-tooltip=\"true\" />\n        <el-table-column label=\"手机\" prop=\"phonenumber\" :show-overflow-tooltip=\"true\" />\n        <el-table-column label=\"状态\" align=\"center\" prop=\"status\">\n          <template slot-scope=\"scope\">\n            <dict-tag :options=\"dict.type.sys_normal_disable\" :value=\"scope.row.status\"/>\n          </template>\n        </el-table-column>\n        <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n          <template slot-scope=\"scope\">\n            <span>{{ parseTime(scope.row.createTime) }}</span>\n          </template>\n        </el-table-column>\n      </el-table>\n      <pagination\n        v-show=\"total>0\"\n        :total=\"total\"\n        :page.sync=\"queryParams.pageNum\"\n        :limit.sync=\"queryParams.pageSize\"\n        @pagination=\"getList\"\n      />\n    </el-row>\n    <div slot=\"footer\" class=\"dialog-footer\">\n      <el-button type=\"primary\" @click=\"handleSelectUser\">确 定</el-button>\n      <el-button @click=\"visible = false\">取 消</el-button>\n    </div>\n  </el-dialog>\n</template>\n\n<script>\nimport { unallocatedUserList, authUserSelectAll } from \"@/api/system/role\";\nexport default {\n  dicts: ['sys_normal_disable'],\n  props: {\n    // 角色编号\n    roleId: {\n      type: [Number, String]\n    }\n  },\n  data() {\n    return {\n      // 遮罩层\n      visible: false,\n      // 选中数组值\n      userIds: [],\n      // 总条数\n      total: 0,\n      // 未授权用户数据\n      userList: [],\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        roleId: undefined,\n        userName: undefined,\n        phonenumber: undefined\n      }\n    };\n  },\n  methods: {\n    // 显示弹框\n    show() {\n      this.queryParams.roleId = this.roleId;\n      this.getList();\n      this.visible = true;\n    },\n    clickRow(row) {\n      this.$refs.table.toggleRowSelection(row);\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.userIds = selection.map(item => item.userId);\n    },\n    // 查询表数据\n    getList() {\n      unallocatedUserList(this.queryParams).then(res => {\n        this.userList = res.rows;\n        this.total = res.total;\n      });\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 选择授权用户操作 */\n    handleSelectUser() {\n      const roleId = this.queryParams.roleId;\n      const userIds = this.userIds.join(\",\");\n      if (userIds == \"\") {\n        this.$modal.msgError(\"请选择要分配的用户\");\n        return;\n      }\n      authUserSelectAll({ roleId: roleId, userIds: userIds }).then(res => {\n        this.$modal.msgSuccess(res.msg);\n        if (res.code === 200) {\n          this.visible = false;\n          this.$emit(\"ok\");\n        }\n      });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/tag/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"名称\" prop=\"name\">\n        <el-input\n          v-model=\"queryParams.name\"\n          placeholder=\"请输入名称\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.createTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择创建时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item label=\"更新时间\" prop=\"updateTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.updateTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择更新时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:tag:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:tag:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:tag:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:tag:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"tagList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"ID\" align=\"center\" prop=\"tagId\" v-if=\"true\"/>\n      <el-table-column label=\"名称\" align=\"center\" prop=\"name\" />\n      <el-table-column label=\"图片\" align=\"center\" prop=\"imageUrl\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <image-preview :src=\"scope.row.imageUrl\" :width=\"50\" :height=\"50\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"更新时间\" align=\"center\" prop=\"updateTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:tag:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:tag:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改活动标签对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"名称\" prop=\"name\">\n          <el-input v-model=\"form.name\" placeholder=\"请输入名称\" />\n        </el-form-item>\n        <el-form-item label=\"图片\" prop=\"imageUrl\">\n          <image-upload v-model=\"form.imageUrl\"/>\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listTag, getTag, delTag, addTag, updateTag } from \"@/api/system/tag\";\n\nexport default {\n  name: \"Tag\",\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 活动标签表格数据\n      tagList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        name: undefined,\n        imageUrl: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        tagId: [\n          { required: true, message: \"ID不能为空\", trigger: \"blur\" }\n        ],\n        name: [\n          { required: true, message: \"名称不能为空\", trigger: \"blur\" }\n        ],\n        imageUrl: [\n          { required: true, message: \"图片不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询活动标签列表 */\n    getList() {\n      this.loading = true;\n      listTag(this.queryParams).then(response => {\n        this.tagList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        tagId: undefined,\n        name: undefined,\n        imageUrl: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n        state: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.tagId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加活动标签\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const tagId = row.tagId || this.ids\n      getTag(tagId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改活动标签\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.tagId != null) {\n            updateTag(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addTag(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const tagIds = row.tagId || this.ids;\n      this.$modal.confirm('是否确认删除活动标签编号为\"' + tagIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delTag(tagIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/tag/export', {\n        ...this.queryParams\n      }, `tag_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/user/authRole.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <h4 class=\"form-header h4\">基本信息</h4>\n    <el-form ref=\"form\" :model=\"form\" label-width=\"80px\">\n      <el-row>\n        <el-col :span=\"8\" :offset=\"2\">\n          <el-form-item label=\"用户昵称\" prop=\"nickName\">\n            <el-input v-model=\"form.nickName\" disabled />\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"8\" :offset=\"2\">\n          <el-form-item label=\"登录账号\" prop=\"userName\">\n            <el-input  v-model=\"form.userName\" disabled />\n          </el-form-item>\n        </el-col>\n      </el-row>\n    </el-form>\n\n    <h4 class=\"form-header h4\">角色信息</h4>\n    <el-table v-loading=\"loading\" :row-key=\"getRowKey\" @row-click=\"clickRow\" ref=\"table\" @selection-change=\"handleSelectionChange\" :data=\"roles.slice((pageNum-1)*pageSize,pageNum*pageSize)\">\n      <el-table-column label=\"序号\" type=\"index\" align=\"center\">\n        <template slot-scope=\"scope\">\n          <span>{{(pageNum - 1) * pageSize + scope.$index + 1}}</span>\n        </template>\n      </el-table-column>\n      <el-table-column type=\"selection\" :reserve-selection=\"true\" width=\"55\"></el-table-column>\n      <el-table-column label=\"角色编号\" align=\"center\" prop=\"roleId\" />\n      <el-table-column label=\"角色名称\" align=\"center\" prop=\"roleName\" />\n      <el-table-column label=\"权限字符\" align=\"center\" prop=\"roleKey\" />\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime) }}</span>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination v-show=\"total>0\" :total=\"total\" :page.sync=\"pageNum\" :limit.sync=\"pageSize\" />\n\n    <el-form label-width=\"100px\">\n      <el-form-item style=\"text-align: center;margin-left:-120px;margin-top:30px;\">\n        <el-button type=\"primary\" @click=\"submitForm()\">提交</el-button>\n        <el-button @click=\"close()\">返回</el-button>\n      </el-form-item>\n    </el-form>\n  </div>\n</template>\n\n<script>\nimport { getAuthRole, updateAuthRole } from \"@/api/system/user\";\n\nexport default {\n  name: \"AuthRole\",\n  data() {\n    return {\n       // 遮罩层\n      loading: true,\n      // 分页信息\n      total: 0,\n      pageNum: 1,\n      pageSize: 10,\n      // 选中角色编号\n      roleIds:[],\n      // 角色信息\n      roles: [],\n      // 用户信息\n      form: {}\n    };\n  },\n  created() {\n    const userId = this.$route.params && this.$route.params.userId;\n    if (userId) {\n      this.loading = true;\n      getAuthRole(userId).then((response) => {\n        this.form = response.data.user;\n        this.roles = response.data.roles;\n        this.total = this.roles.length;\n        this.$nextTick(() => {\n          this.roles.forEach((row) => {\n            if (row.flag) {\n              this.$refs.table.toggleRowSelection(row);\n            }\n          });\n        });\n        this.loading = false;\n      });\n    }\n  },\n  methods: {\n    /** 单击选中行数据 */\n    clickRow(row) {\n      this.$refs.table.toggleRowSelection(row);\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.roleIds = selection.map((item) => item.roleId);\n    },\n    // 保存选中的数据编号\n    getRowKey(row) {\n      return row.roleId;\n    },\n    /** 提交按钮 */\n    submitForm() {\n      const userId = this.form.userId;\n      const roleIds = this.roleIds.join(\",\");\n      updateAuthRole({ userId: userId, roleIds: roleIds }).then((response) => {\n        this.$modal.msgSuccess(\"授权成功\");\n        this.close();\n      });\n    },\n    /** 关闭按钮 */\n    close() {\n      const obj = { path: \"/system/user\" };\n      this.$tab.closeOpenPage(obj);\n    },\n  },\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/user/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-row :gutter=\"20\">\n      <!--部门数据-->\n      <el-col :span=\"4\" :xs=\"24\">\n        <div class=\"head-container\">\n          <el-input\n            v-model=\"deptName\"\n            placeholder=\"请输入部门名称\"\n            clearable\n            size=\"small\"\n            prefix-icon=\"el-icon-search\"\n            style=\"margin-bottom: 20px\"\n          />\n        </div>\n        <div class=\"head-container\">\n          <el-tree\n            :data=\"deptOptions\"\n            :props=\"defaultProps\"\n            :expand-on-click-node=\"false\"\n            :filter-node-method=\"filterNode\"\n            ref=\"tree\"\n            node-key=\"id\"\n            default-expand-all\n            highlight-current\n            @node-click=\"handleNodeClick\"\n          />\n        </div>\n      </el-col>\n      <!--用户数据-->\n      <el-col :span=\"20\" :xs=\"24\">\n        <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n          <el-form-item label=\"用户名称\" prop=\"userName\">\n            <el-input\n              v-model=\"queryParams.userName\"\n              placeholder=\"请输入用户名称\"\n              clearable\n              style=\"width: 240px\"\n              @keyup.enter.native=\"handleQuery\"\n            />\n          </el-form-item>\n          <el-form-item label=\"手机号码\" prop=\"phonenumber\">\n            <el-input\n              v-model=\"queryParams.phonenumber\"\n              placeholder=\"请输入手机号码\"\n              clearable\n              style=\"width: 240px\"\n              @keyup.enter.native=\"handleQuery\"\n            />\n          </el-form-item>\n          <el-form-item label=\"状态\" prop=\"status\">\n            <el-select\n              v-model=\"queryParams.status\"\n              placeholder=\"用户状态\"\n              clearable\n              style=\"width: 240px\"\n            >\n              <el-option\n                v-for=\"dict in dict.type.sys_normal_disable\"\n                :key=\"dict.value\"\n                :label=\"dict.label\"\n                :value=\"dict.value\"\n              />\n            </el-select>\n          </el-form-item>\n          <el-form-item label=\"创建时间\">\n            <el-date-picker\n              v-model=\"dateRange\"\n              style=\"width: 240px\"\n              value-format=\"yyyy-MM-dd HH:mm:ss\"\n              type=\"daterange\"\n              range-separator=\"-\"\n              start-placeholder=\"开始日期\"\n              end-placeholder=\"结束日期\"\n              :default-time=\"['00:00:00', '23:59:59']\"\n            ></el-date-picker>\n          </el-form-item>\n          <el-form-item>\n            <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n            <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n          </el-form-item>\n        </el-form>\n\n        <el-row :gutter=\"10\" class=\"mb8\">\n          <el-col :span=\"1.5\">\n            <el-button\n              type=\"primary\"\n              plain\n              icon=\"el-icon-plus\"\n              size=\"mini\"\n              @click=\"handleAdd\"\n              v-hasPermi=\"['system:user:add']\"\n            >新增</el-button>\n          </el-col>\n          <el-col :span=\"1.5\">\n            <el-button\n              type=\"success\"\n              plain\n              icon=\"el-icon-edit\"\n              size=\"mini\"\n              :disabled=\"single\"\n              @click=\"handleUpdate\"\n              v-hasPermi=\"['system:user:edit']\"\n            >修改</el-button>\n          </el-col>\n          <el-col :span=\"1.5\">\n            <el-button\n              type=\"danger\"\n              plain\n              icon=\"el-icon-delete\"\n              size=\"mini\"\n              :disabled=\"multiple\"\n              @click=\"handleDelete\"\n              v-hasPermi=\"['system:user:remove']\"\n            >删除</el-button>\n          </el-col>\n          <el-col :span=\"1.5\">\n            <el-button\n              type=\"info\"\n              plain\n              icon=\"el-icon-upload2\"\n              size=\"mini\"\n              @click=\"handleImport\"\n              v-hasPermi=\"['system:user:import']\"\n            >导入</el-button>\n          </el-col>\n          <el-col :span=\"1.5\">\n            <el-button\n              type=\"warning\"\n              plain\n              icon=\"el-icon-download\"\n              size=\"mini\"\n              @click=\"handleExport\"\n              v-hasPermi=\"['system:user:export']\"\n            >导出</el-button>\n          </el-col>\n          <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\" :columns=\"columns\"></right-toolbar>\n        </el-row>\n\n        <el-table v-loading=\"loading\" :data=\"userList\" @selection-change=\"handleSelectionChange\">\n          <el-table-column type=\"selection\" width=\"50\" align=\"center\" />\n          <el-table-column label=\"用户编号\" align=\"center\" key=\"userId\" prop=\"userId\" v-if=\"columns[0].visible\" />\n          <el-table-column label=\"用户名称\" align=\"center\" key=\"userName\" prop=\"userName\" v-if=\"columns[1].visible\" :show-overflow-tooltip=\"true\" />\n          <el-table-column label=\"用户昵称\" align=\"center\" key=\"nickName\" prop=\"nickName\" v-if=\"columns[2].visible\" :show-overflow-tooltip=\"true\" />\n          <el-table-column label=\"部门\" align=\"center\" key=\"deptName\" prop=\"dept.deptName\" v-if=\"columns[3].visible\" :show-overflow-tooltip=\"true\" />\n          <el-table-column label=\"手机号码\" align=\"center\" key=\"phonenumber\" prop=\"phonenumber\" v-if=\"columns[4].visible\" width=\"120\" />\n          <el-table-column label=\"状态\" align=\"center\" key=\"status\" v-if=\"columns[5].visible\">\n            <template slot-scope=\"scope\">\n              <el-switch\n                v-model=\"scope.row.status\"\n                active-value=\"0\"\n                inactive-value=\"1\"\n                @change=\"handleStatusChange(scope.row)\"\n              ></el-switch>\n            </template>\n          </el-table-column>\n          <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" v-if=\"columns[6].visible\" width=\"160\">\n            <template slot-scope=\"scope\">\n              <span>{{ parseTime(scope.row.createTime) }}</span>\n            </template>\n          </el-table-column>\n          <el-table-column\n            label=\"操作\"\n            align=\"center\"\n            width=\"160\"\n            class-name=\"small-padding fixed-width\"\n          >\n            <template slot-scope=\"scope\" v-if=\"scope.row.userId !== 1\">\n              <el-button\n                size=\"mini\"\n                type=\"text\"\n                icon=\"el-icon-edit\"\n                @click=\"handleUpdate(scope.row)\"\n                v-hasPermi=\"['system:user:edit']\"\n              >修改</el-button>\n              <el-button\n                size=\"mini\"\n                type=\"text\"\n                icon=\"el-icon-delete\"\n                @click=\"handleDelete(scope.row)\"\n                v-hasPermi=\"['system:user:remove']\"\n              >删除</el-button>\n              <el-dropdown size=\"mini\" @command=\"(command) => handleCommand(command, scope.row)\" v-hasPermi=\"['system:user:resetPwd', 'system:user:edit']\">\n                <el-button size=\"mini\" type=\"text\" icon=\"el-icon-d-arrow-right\">更多</el-button>\n                <el-dropdown-menu slot=\"dropdown\">\n                  <el-dropdown-item command=\"handleResetPwd\" icon=\"el-icon-key\"\n                    v-hasPermi=\"['system:user:resetPwd']\">重置密码</el-dropdown-item>\n                  <el-dropdown-item command=\"handleAuthRole\" icon=\"el-icon-circle-check\"\n                    v-hasPermi=\"['system:user:edit']\">分配角色</el-dropdown-item>\n                </el-dropdown-menu>\n              </el-dropdown>\n            </template>\n          </el-table-column>\n        </el-table>\n\n        <pagination\n          v-show=\"total>0\"\n          :total=\"total\"\n          :page.sync=\"queryParams.pageNum\"\n          :limit.sync=\"queryParams.pageSize\"\n          @pagination=\"getList\"\n        />\n      </el-col>\n    </el-row>\n\n    <!-- 添加或修改用户配置对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"600px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-row>\n          <el-col :span=\"12\">\n            <el-form-item label=\"用户昵称\" prop=\"nickName\">\n              <el-input v-model=\"form.nickName\" placeholder=\"请输入用户昵称\" maxlength=\"30\" />\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\">\n            <el-form-item label=\"归属部门\" prop=\"deptId\">\n              <treeselect v-model=\"form.deptId\" :options=\"deptOptions\" :show-count=\"true\" placeholder=\"请选择归属部门\" />\n            </el-form-item>\n          </el-col>\n        </el-row>\n        <el-row>\n          <el-col :span=\"12\">\n            <el-form-item label=\"手机号码\" prop=\"phonenumber\">\n              <el-input v-model=\"form.phonenumber\" placeholder=\"请输入手机号码\" maxlength=\"11\" />\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\">\n            <el-form-item label=\"邮箱\" prop=\"email\">\n              <el-input v-model=\"form.email\" placeholder=\"请输入邮箱\" maxlength=\"50\" />\n            </el-form-item>\n          </el-col>\n        </el-row>\n        <el-row>\n          <el-col :span=\"12\">\n            <el-form-item v-if=\"form.userId == undefined\" label=\"用户名称\" prop=\"userName\">\n              <el-input v-model=\"form.userName\" placeholder=\"请输入用户名称\" maxlength=\"30\" />\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\">\n            <el-form-item v-if=\"form.userId == undefined\" label=\"用户密码\" prop=\"password\">\n              <el-input v-model=\"form.password\" placeholder=\"请输入用户密码\" type=\"password\" maxlength=\"20\" show-password/>\n            </el-form-item>\n          </el-col>\n        </el-row>\n        <el-row>\n          <el-col :span=\"12\">\n            <el-form-item label=\"用户性别\">\n              <el-select v-model=\"form.sex\" placeholder=\"请选择性别\">\n                <el-option\n                  v-for=\"dict in dict.type.sys_user_sex\"\n                  :key=\"dict.value\"\n                  :label=\"dict.label\"\n                  :value=\"dict.value\"\n                ></el-option>\n              </el-select>\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\">\n            <el-form-item label=\"状态\">\n              <el-radio-group v-model=\"form.status\">\n                <el-radio\n                  v-for=\"dict in dict.type.sys_normal_disable\"\n                  :key=\"dict.value\"\n                  :label=\"dict.value\"\n                >{{dict.label}}</el-radio>\n              </el-radio-group>\n            </el-form-item>\n          </el-col>\n        </el-row>\n        <el-row>\n          <el-col :span=\"12\">\n            <el-form-item label=\"岗位\">\n              <el-select v-model=\"form.postIds\" multiple placeholder=\"请选择岗位\">\n                <el-option\n                  v-for=\"item in postOptions\"\n                  :key=\"item.postId\"\n                  :label=\"item.postName\"\n                  :value=\"item.postId\"\n                  :disabled=\"item.status == 1\"\n                ></el-option>\n              </el-select>\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"12\">\n            <el-form-item label=\"角色\">\n              <el-select v-model=\"form.roleIds\" multiple placeholder=\"请选择角色\">\n                <el-option\n                  v-for=\"item in roleOptions\"\n                  :key=\"item.roleId\"\n                  :label=\"item.roleName\"\n                  :value=\"item.roleId\"\n                  :disabled=\"item.status == 1\"\n                ></el-option>\n              </el-select>\n            </el-form-item>\n          </el-col>\n        </el-row>\n        <el-row>\n          <el-col :span=\"24\">\n            <el-form-item label=\"备注\">\n              <el-input v-model=\"form.remark\" type=\"textarea\" placeholder=\"请输入内容\"></el-input>\n            </el-form-item>\n          </el-col>\n        </el-row>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n\n    <!-- 用户导入对话框 -->\n    <el-dialog :title=\"upload.title\" :visible.sync=\"upload.open\" width=\"400px\" append-to-body>\n      <el-upload\n        ref=\"upload\"\n        :limit=\"1\"\n        accept=\".xlsx, .xls\"\n        :headers=\"upload.headers\"\n        :action=\"upload.url + '?updateSupport=' + upload.updateSupport\"\n        :disabled=\"upload.isUploading\"\n        :on-progress=\"handleFileUploadProgress\"\n        :on-success=\"handleFileSuccess\"\n        :auto-upload=\"false\"\n        drag\n      >\n        <i class=\"el-icon-upload\"></i>\n        <div class=\"el-upload__text\">将文件拖到此处，或<em>点击上传</em></div>\n        <div class=\"el-upload__tip text-center\" slot=\"tip\">\n          <div class=\"el-upload__tip\" slot=\"tip\">\n            <el-checkbox v-model=\"upload.updateSupport\" /> 是否更新已经存在的用户数据\n          </div>\n          <span>仅允许导入xls、xlsx格式文件。</span>\n          <el-link type=\"primary\" :underline=\"false\" style=\"font-size:12px;vertical-align: baseline;\" @click=\"importTemplate\">下载模板</el-link>\n        </div>\n      </el-upload>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitFileForm\">确 定</el-button>\n        <el-button @click=\"upload.open = false\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus, deptTreeSelect } from \"@/api/system/user\";\nimport { getToken } from \"@/utils/auth\";\nimport Treeselect from \"@riophae/vue-treeselect\";\nimport \"@riophae/vue-treeselect/dist/vue-treeselect.css\";\n\nexport default {\n  name: \"User\",\n  dicts: ['sys_normal_disable', 'sys_user_sex'],\n  components: { Treeselect },\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 用户表格数据\n      userList: null,\n      // 弹出层标题\n      title: \"\",\n      // 部门树选项\n      deptOptions: undefined,\n      // 是否显示弹出层\n      open: false,\n      // 部门名称\n      deptName: undefined,\n      // 默认密码\n      initPassword: undefined,\n      // 日期范围\n      dateRange: [],\n      // 岗位选项\n      postOptions: [],\n      // 角色选项\n      roleOptions: [],\n      // 表单参数\n      form: {},\n      defaultProps: {\n        children: \"children\",\n        label: \"label\"\n      },\n      // 用户导入参数\n      upload: {\n        // 是否显示弹出层（用户导入）\n        open: false,\n        // 弹出层标题（用户导入）\n        title: \"\",\n        // 是否禁用上传\n        isUploading: false,\n        // 是否更新已经存在的用户数据\n        updateSupport: 0,\n        // 设置上传的请求头部\n        headers: { Authorization: \"Bearer \" + getToken() },\n        // 上传的地址\n        url: process.env.VUE_APP_BASE_API + \"/system/user/importData\"\n      },\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        userName: undefined,\n        phonenumber: undefined,\n        status: undefined,\n        deptId: undefined\n      },\n      // 列信息\n      columns: [\n        { key: 0, label: `用户编号`, visible: true },\n        { key: 1, label: `用户名称`, visible: true },\n        { key: 2, label: `用户昵称`, visible: true },\n        { key: 3, label: `部门`, visible: true },\n        { key: 4, label: `手机号码`, visible: true },\n        { key: 5, label: `状态`, visible: true },\n        { key: 6, label: `创建时间`, visible: true }\n      ],\n      // 表单校验\n      rules: {\n        userName: [\n          { required: true, message: \"用户名称不能为空\", trigger: \"blur\" },\n          { min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' }\n        ],\n        nickName: [\n          { required: true, message: \"用户昵称不能为空\", trigger: \"blur\" }\n        ],\n        password: [\n          { required: true, message: \"用户密码不能为空\", trigger: \"blur\" },\n          { min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' }\n        ],\n        email: [\n          {\n            type: \"email\",\n            message: \"请输入正确的邮箱地址\",\n            trigger: [\"blur\", \"change\"]\n          }\n        ],\n        phonenumber: [\n          {\n            pattern: /^1[3|4|5|6|7|8|9][0-9]\\d{8}$/,\n            message: \"请输入正确的手机号码\",\n            trigger: \"blur\"\n          }\n        ]\n      }\n    };\n  },\n  watch: {\n    // 根据名称筛选部门树\n    deptName(val) {\n      this.$refs.tree.filter(val);\n    }\n  },\n  created() {\n    this.getList();\n    this.getDeptTree();\n    this.getConfigKey(\"sys.user.initPassword\").then(response => {\n      this.initPassword = response.msg;\n    });\n  },\n  methods: {\n    /** 查询用户列表 */\n    getList() {\n      this.loading = true;\n      listUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => {\n          this.userList = response.rows;\n          this.total = response.total;\n          this.loading = false;\n        }\n      );\n    },\n    /** 查询部门下拉树结构 */\n    getDeptTree() {\n      deptTreeSelect().then(response => {\n        this.deptOptions = response.data;\n      });\n    },\n    // 筛选节点\n    filterNode(value, data) {\n      if (!value) return true;\n      return data.label.indexOf(value) !== -1;\n    },\n    // 节点单击事件\n    handleNodeClick(data) {\n      this.queryParams.deptId = data.id;\n      this.handleQuery();\n    },\n    // 用户状态修改\n    handleStatusChange(row) {\n      let text = row.status === \"0\" ? \"启用\" : \"停用\";\n      this.$modal.confirm('确认要\"' + text + '\"\"' + row.userName + '\"用户吗？').then(function() {\n        return changeUserStatus(row.userId, row.status);\n      }).then(() => {\n        this.$modal.msgSuccess(text + \"成功\");\n      }).catch(function() {\n        row.status = row.status === \"0\" ? \"1\" : \"0\";\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        userId: undefined,\n        deptId: undefined,\n        userName: undefined,\n        nickName: undefined,\n        password: undefined,\n        phonenumber: undefined,\n        email: undefined,\n        sex: undefined,\n        status: \"0\",\n        remark: undefined,\n        postIds: [],\n        roleIds: []\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.dateRange = [];\n      this.resetForm(\"queryForm\");\n      this.queryParams.deptId = undefined;\n      this.$refs.tree.setCurrentKey(null);\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.userId);\n      this.single = selection.length != 1;\n      this.multiple = !selection.length;\n    },\n    // 更多操作触发\n    handleCommand(command, row) {\n      switch (command) {\n        case \"handleResetPwd\":\n          this.handleResetPwd(row);\n          break;\n        case \"handleAuthRole\":\n          this.handleAuthRole(row);\n          break;\n        default:\n          break;\n      }\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      getUser().then(response => {\n        this.postOptions = response.data.posts;\n        this.roleOptions = response.data.roles;\n        this.open = true;\n        this.title = \"添加用户\";\n        this.form.password = this.initPassword;\n      });\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.reset();\n      const userId = row.userId || this.ids;\n      getUser(userId).then(response => {\n        this.form = response.data.user;\n        this.postOptions = response.data.posts;\n        this.roleOptions = response.data.roles;\n        this.$set(this.form, \"postIds\", response.data.postIds);\n        this.$set(this.form, \"roleIds\", response.data.roleIds);\n        this.open = true;\n        this.title = \"修改用户\";\n        this.form.password = \"\";\n      });\n    },\n    /** 重置密码按钮操作 */\n    handleResetPwd(row) {\n      this.$prompt('请输入\"' + row.userName + '\"的新密码', \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        closeOnClickModal: false,\n        inputPattern: /^.{5,20}$/,\n        inputErrorMessage: \"用户密码长度必须介于 5 和 20 之间\"\n      }).then(({ value }) => {\n          resetUserPwd(row.userId, value).then(response => {\n            this.$modal.msgSuccess(\"修改成功，新密码是：\" + value);\n          });\n        }).catch(() => {});\n    },\n    /** 分配角色操作 */\n    handleAuthRole: function(row) {\n      const userId = row.userId;\n      this.$router.push(\"/system/user-auth/role/\" + userId);\n    },\n    /** 提交按钮 */\n    submitForm: function() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          if (this.form.userId != undefined) {\n            updateUser(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            });\n          } else {\n            addUser(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const userIds = row.userId || this.ids;\n      this.$modal.confirm('是否确认删除用户编号为\"' + userIds + '\"的数据项？').then(function() {\n        return delUser(userIds);\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {});\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/user/export', {\n        ...this.queryParams\n      }, `user_${new Date().getTime()}.xlsx`)\n    },\n    /** 导入按钮操作 */\n    handleImport() {\n      this.upload.title = \"用户导入\";\n      this.upload.open = true;\n    },\n    /** 下载模板操作 */\n    importTemplate() {\n      this.download('system/user/importTemplate', {\n      }, `user_template_${new Date().getTime()}.xlsx`)\n    },\n    // 文件上传中处理\n    handleFileUploadProgress(event, file, fileList) {\n      this.upload.isUploading = true;\n    },\n    // 文件上传成功处理\n    handleFileSuccess(response, file, fileList) {\n      this.upload.open = false;\n      this.upload.isUploading = false;\n      this.$refs.upload.clearFiles();\n      this.$alert(\"<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>\" + response.msg + \"</div>\", \"导入结果\", { dangerouslyUseHTMLString: true });\n      this.getList();\n    },\n    // 提交上传文件\n    submitFileForm() {\n      this.$refs.upload.submit();\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/user/profile/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-row :gutter=\"20\">\n      <el-col :span=\"6\" :xs=\"24\">\n        <el-card class=\"box-card\">\n          <div slot=\"header\" class=\"clearfix\">\n            <span>个人信息</span>\n          </div>\n          <div>\n            <div class=\"text-center\">\n              <userAvatar :user=\"user\" />\n            </div>\n            <ul class=\"list-group list-group-striped\">\n              <li class=\"list-group-item\">\n                <svg-icon icon-class=\"user\" />用户名称\n                <div class=\"pull-right\">{{ user.userName }}</div>\n              </li>\n              <li class=\"list-group-item\">\n                <svg-icon icon-class=\"phone\" />手机号码\n                <div class=\"pull-right\">{{ user.phonenumber }}</div>\n              </li>\n              <li class=\"list-group-item\">\n                <svg-icon icon-class=\"email\" />用户邮箱\n                <div class=\"pull-right\">{{ user.email }}</div>\n              </li>\n              <li class=\"list-group-item\">\n                <svg-icon icon-class=\"tree\" />所属部门\n                <div class=\"pull-right\" v-if=\"user.dept\">{{ user.dept.deptName }} / {{ postGroup }}</div>\n              </li>\n              <li class=\"list-group-item\">\n                <svg-icon icon-class=\"peoples\" />所属角色\n                <div class=\"pull-right\">{{ roleGroup }}</div>\n              </li>\n              <li class=\"list-group-item\">\n                <svg-icon icon-class=\"date\" />创建日期\n                <div class=\"pull-right\">{{ user.createTime }}</div>\n              </li>\n            </ul>\n          </div>\n        </el-card>\n      </el-col>\n      <el-col :span=\"18\" :xs=\"24\">\n        <el-card>\n          <div slot=\"header\" class=\"clearfix\">\n            <span>基本资料</span>\n          </div>\n          <el-tabs v-model=\"activeTab\">\n            <el-tab-pane label=\"基本资料\" name=\"userinfo\">\n              <userInfo :user=\"user\" />\n            </el-tab-pane>\n            <el-tab-pane label=\"修改密码\" name=\"resetPwd\">\n              <resetPwd />\n            </el-tab-pane>\n          </el-tabs>\n        </el-card>\n      </el-col>\n    </el-row>\n  </div>\n</template>\n\n<script>\nimport userAvatar from \"./userAvatar\";\nimport userInfo from \"./userInfo\";\nimport resetPwd from \"./resetPwd\";\nimport { getUserProfile } from \"@/api/system/user\";\n\nexport default {\n  name: \"Profile\",\n  components: { userAvatar, userInfo, resetPwd },\n  data() {\n    return {\n      user: {},\n      roleGroup: {},\n      postGroup: {},\n      activeTab: \"userinfo\"\n    };\n  },\n  created() {\n    this.getUser();\n  },\n  methods: {\n    getUser() {\n      getUserProfile().then(response => {\n        this.user = response.data.user;\n        this.roleGroup = response.data.roleGroup;\n        this.postGroup = response.data.postGroup;\n      });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/user/profile/resetPwd.vue",
    "content": "<template>\n  <el-form ref=\"form\" :model=\"user\" :rules=\"rules\" label-width=\"80px\">\n    <el-form-item label=\"旧密码\" prop=\"oldPassword\">\n      <el-input v-model=\"user.oldPassword\" placeholder=\"请输入旧密码\" type=\"password\" show-password/>\n    </el-form-item>\n    <el-form-item label=\"新密码\" prop=\"newPassword\">\n      <el-input v-model=\"user.newPassword\" placeholder=\"请输入新密码\" type=\"password\" show-password/>\n    </el-form-item>\n    <el-form-item label=\"确认密码\" prop=\"confirmPassword\">\n      <el-input v-model=\"user.confirmPassword\" placeholder=\"请确认新密码\" type=\"password\" show-password/>\n    </el-form-item>\n    <el-form-item>\n      <el-button type=\"primary\" size=\"mini\" @click=\"submit\">保存</el-button>\n      <el-button type=\"danger\" size=\"mini\" @click=\"close\">关闭</el-button>\n    </el-form-item>\n  </el-form>\n</template>\n\n<script>\nimport { updateUserPwd } from \"@/api/system/user\";\n\nexport default {\n  data() {\n    const equalToPassword = (rule, value, callback) => {\n      if (this.user.newPassword !== value) {\n        callback(new Error(\"两次输入的密码不一致\"));\n      } else {\n        callback();\n      }\n    };\n    return {\n      user: {\n        oldPassword: undefined,\n        newPassword: undefined,\n        confirmPassword: undefined\n      },\n      // 表单校验\n      rules: {\n        oldPassword: [\n          { required: true, message: \"旧密码不能为空\", trigger: \"blur\" }\n        ],\n        newPassword: [\n          { required: true, message: \"新密码不能为空\", trigger: \"blur\" },\n          { min: 6, max: 20, message: \"长度在 6 到 20 个字符\", trigger: \"blur\" }\n        ],\n        confirmPassword: [\n          { required: true, message: \"确认密码不能为空\", trigger: \"blur\" },\n          { required: true, validator: equalToPassword, trigger: \"blur\" }\n        ]\n      }\n    };\n  },\n  methods: {\n    submit() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          updateUserPwd(this.user.oldPassword, this.user.newPassword).then(response => {\n            this.$modal.msgSuccess(\"修改成功\");\n          });\n        }\n      });\n    },\n    close() {\n      this.$tab.closePage();\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/user/profile/userAvatar.vue",
    "content": "<template>\n  <div>\n    <div class=\"user-info-head\" @click=\"editCropper()\"><img v-bind:src=\"options.img\" title=\"点击上传头像\" class=\"img-circle img-lg\" /></div>\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"800px\" append-to-body @opened=\"modalOpened\"  @close=\"closeDialog\">\n      <el-row>\n        <el-col :xs=\"24\" :md=\"12\" :style=\"{height: '350px'}\">\n          <vue-cropper\n            ref=\"cropper\"\n            :img=\"options.img\"\n            :info=\"true\"\n            :autoCrop=\"options.autoCrop\"\n            :autoCropWidth=\"options.autoCropWidth\"\n            :autoCropHeight=\"options.autoCropHeight\"\n            :fixedBox=\"options.fixedBox\"\n            :outputType=\"options.outputType\"\n            @realTime=\"realTime\"\n            v-if=\"visible\"\n          />\n        </el-col>\n        <el-col :xs=\"24\" :md=\"12\" :style=\"{height: '350px'}\">\n          <div class=\"avatar-upload-preview\">\n            <img :src=\"previews.url\" :style=\"previews.img\" />\n          </div>\n        </el-col>\n      </el-row>\n      <br />\n      <el-row>\n        <el-col :lg=\"2\" :sm=\"3\" :xs=\"3\">\n          <el-upload action=\"#\" :http-request=\"requestUpload\" :show-file-list=\"false\" :before-upload=\"beforeUpload\">\n            <el-button size=\"small\">\n              选择\n              <i class=\"el-icon-upload el-icon--right\"></i>\n            </el-button>\n          </el-upload>\n        </el-col>\n        <el-col :lg=\"{span: 1, offset: 2}\" :sm=\"2\" :xs=\"2\">\n          <el-button icon=\"el-icon-plus\" size=\"small\" @click=\"changeScale(1)\"></el-button>\n        </el-col>\n        <el-col :lg=\"{span: 1, offset: 1}\" :sm=\"2\" :xs=\"2\">\n          <el-button icon=\"el-icon-minus\" size=\"small\" @click=\"changeScale(-1)\"></el-button>\n        </el-col>\n        <el-col :lg=\"{span: 1, offset: 1}\" :sm=\"2\" :xs=\"2\">\n          <el-button icon=\"el-icon-refresh-left\" size=\"small\" @click=\"rotateLeft()\"></el-button>\n        </el-col>\n        <el-col :lg=\"{span: 1, offset: 1}\" :sm=\"2\" :xs=\"2\">\n          <el-button icon=\"el-icon-refresh-right\" size=\"small\" @click=\"rotateRight()\"></el-button>\n        </el-col>\n        <el-col :lg=\"{span: 2, offset: 6}\" :sm=\"2\" :xs=\"2\">\n          <el-button type=\"primary\" size=\"small\" @click=\"uploadImg()\">提 交</el-button>\n        </el-col>\n      </el-row>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport store from \"@/store\";\nimport { VueCropper } from \"vue-cropper\";\nimport { uploadAvatar } from \"@/api/system/user\";\nimport { debounce } from '@/utils'\n\nexport default {\n  components: { VueCropper },\n  props: {\n    user: {\n      type: Object\n    }\n  },\n  data() {\n    return {\n      // 是否显示弹出层\n      open: false,\n      // 是否显示cropper\n      visible: false,\n      // 弹出层标题\n      title: \"修改头像\",\n      options: {\n        img: store.getters.avatar, //裁剪图片的地址\n        autoCrop: true, // 是否默认生成截图框\n        autoCropWidth: 200, // 默认生成截图框宽度\n        autoCropHeight: 200, // 默认生成截图框高度\n        fixedBox: true, // 固定截图框大小 不允许改变\n        outputType:\"png\", // 默认生成截图为PNG格式\n        filename: ''\n      },\n      previews: {},\n      resizeHandler: null\n    };\n  },\n  methods: {\n    // 编辑头像\n    editCropper() {\n      this.open = true;\n    },\n    // 打开弹出层结束时的回调\n    modalOpened() {\n      this.visible = true;\n      if (!this.resizeHandler) {\n        this.resizeHandler = debounce(() => {\n          this.refresh()\n        }, 100)\n      }\n      window.addEventListener(\"resize\", this.resizeHandler)\n    },\n    // 刷新组件\n    refresh() {\n      this.$refs.cropper.refresh();\n    },\n    // 覆盖默认的上传行为\n    requestUpload() {\n    },\n    // 向左旋转\n    rotateLeft() {\n      this.$refs.cropper.rotateLeft();\n    },\n    // 向右旋转\n    rotateRight() {\n      this.$refs.cropper.rotateRight();\n    },\n    // 图片缩放\n    changeScale(num) {\n      num = num || 1;\n      this.$refs.cropper.changeScale(num);\n    },\n    // 上传预处理\n    beforeUpload(file) {\n      if (file.type.indexOf(\"image/\") == -1) {\n        this.$modal.msgError(\"文件格式错误，请上传图片类型,如：JPG，PNG后缀的文件。\");\n      } else {\n        const reader = new FileReader();\n        reader.readAsDataURL(file);\n        reader.onload = () => {\n          this.options.img = reader.result;\n          this.options.filename = file.name;\n        };\n      }\n    },\n    // 上传图片\n    uploadImg() {\n      this.$refs.cropper.getCropBlob(data => {\n        let formData = new FormData();\n        console.log(this.options.filename)\n        formData.append(\"avatarfile\", data, this.options.filename);\n        uploadAvatar(formData).then(response => {\n          this.open = false;\n          this.options.img = response.data.imgUrl;\n          store.commit('SET_AVATAR', this.options.img);\n          this.$modal.msgSuccess(\"修改成功\");\n          this.visible = false;\n        });\n      });\n    },\n    // 实时预览\n    realTime(data) {\n      this.previews = data;\n    },\n    // 关闭窗口\n    closeDialog() {\n      this.options.img = store.getters.avatar\n      this.visible = false;\n      window.removeEventListener(\"resize\", this.resizeHandler)\n    }\n  }\n};\n</script>\n<style scoped lang=\"scss\">\n.user-info-head {\n  position: relative;\n  display: inline-block;\n  height: 120px;\n}\n\n.user-info-head:hover:after {\n  content: '+';\n  position: absolute;\n  left: 0;\n  right: 0;\n  top: 0;\n  bottom: 0;\n  color: #eee;\n  background: rgba(0, 0, 0, 0.5);\n  font-size: 24px;\n  font-style: normal;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  cursor: pointer;\n  line-height: 110px;\n  border-radius: 50%;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/user/profile/userInfo.vue",
    "content": "<template>\n  <el-form ref=\"form\" :model=\"user\" :rules=\"rules\" label-width=\"80px\">\n    <el-form-item label=\"今日播放音乐ID\" prop=\"nickName\">\n      <el-input v-model=\"user.nickName\" maxlength=\"300\" />\n    </el-form-item>\n    <el-form-item label=\"手机号码\" prop=\"phonenumber\">\n      <el-input v-model=\"user.phonenumber\" maxlength=\"11\" />\n    </el-form-item>\n    <el-form-item label=\"邮箱\" prop=\"email\">\n      <el-input v-model=\"user.email\" maxlength=\"50\" />\n    </el-form-item>\n    <el-form-item label=\"性别\">\n      <el-radio-group v-model=\"user.sex\">\n        <el-radio label=\"0\">男</el-radio>\n        <el-radio label=\"1\">女</el-radio>\n      </el-radio-group>\n    </el-form-item>\n    <el-form-item>\n      <el-button type=\"primary\" size=\"mini\" @click=\"submit\">保存</el-button>\n      <el-button type=\"danger\" size=\"mini\" @click=\"close\">关闭</el-button>\n    </el-form-item>\n  </el-form>\n</template>\n\n<script>\nimport { updateUserProfile } from \"@/api/system/user\";\n\nexport default {\n  props: {\n    user: {\n      type: Object\n    }\n  },\n  data() {\n    return {\n      // 表单校验\n      rules: {\n        nickName: [\n          { required: true, message: \"用户昵称不能为空\", trigger: \"blur\" }\n        ],\n        email: [\n          { required: true, message: \"邮箱地址不能为空\", trigger: \"blur\" },\n          {\n            type: \"email\",\n            message: \"请输入正确的邮箱地址\",\n            trigger: [\"blur\", \"change\"]\n          }\n        ],\n        phonenumber: [\n          { required: true, message: \"手机号码不能为空\", trigger: \"blur\" },\n          {\n            pattern: /^1[3|4|5|6|7|8|9][0-9]\\d{8}$/,\n            message: \"请输入正确的手机号码\",\n            trigger: \"blur\"\n          }\n        ]\n      }\n    };\n  },\n  methods: {\n    submit() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          updateUserProfile(this.user).then(response => {\n            this.$modal.msgSuccess(\"修改成功\");\n          });\n        }\n      });\n    },\n    close() {\n      this.$tab.closePage();\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/userCollect/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"用户Id\" prop=\"userId\">\n        <el-input\n          v-model=\"queryParams.userId\"\n          placeholder=\"请输入用户Id\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"收藏活动的Id\" prop=\"activityId\">\n        <el-input\n          v-model=\"queryParams.activityId\"\n          placeholder=\"请输入收藏活动的Id\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:userCollect:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:userCollect:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:userCollect:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:userCollect:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"userCollectList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"ID\" align=\"center\" prop=\"collectId\" v-if=\"true\"/>\n      <el-table-column label=\"用户Id\" align=\"center\" prop=\"userId\" />\n      <el-table-column label=\"收藏活动的Id\" align=\"center\" prop=\"activityId\" />\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:userCollect:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:userCollect:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改用户收藏活动对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"用户Id\" prop=\"userId\">\n          <el-input v-model=\"form.userId\" placeholder=\"请输入用户Id\" />\n        </el-form-item>\n        <el-form-item label=\"收藏活动的Id\" prop=\"activityId\">\n          <el-input v-model=\"form.activityId\" placeholder=\"请输入收藏活动的Id\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listUserCollect, getUserCollect, delUserCollect, addUserCollect, updateUserCollect } from \"@/api/system/userCollect\";\n\nexport default {\n  name: \"UserCollect\",\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 用户收藏活动表格数据\n      userCollectList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        userId: undefined,\n        activityId: undefined,\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        collectId: [\n          { required: true, message: \"ID不能为空\", trigger: \"blur\" }\n        ],\n        userId: [\n          { required: true, message: \"用户Id不能为空\", trigger: \"blur\" }\n        ],\n        activityId: [\n          { required: true, message: \"收藏活动的Id不能为空\", trigger: \"blur\" }\n        ],\n        createTime: [\n          { required: true, message: \"不能为空\", trigger: \"blur\" }\n        ]\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询用户收藏活动列表 */\n    getList() {\n      this.loading = true;\n      listUserCollect(this.queryParams).then(response => {\n        this.userCollectList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        collectId: undefined,\n        userId: undefined,\n        activityId: undefined,\n        createTime: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.collectId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加用户收藏活动\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const collectId = row.collectId || this.ids\n      getUserCollect(collectId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改用户收藏活动\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.collectId != null) {\n            updateUserCollect(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addUserCollect(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const collectIds = row.collectId || this.ids;\n      this.$modal.confirm('是否确认删除用户收藏活动编号为\"' + collectIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delUserCollect(collectIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/userCollect/export', {\n        ...this.queryParams\n      }, `userCollect_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/userHistory/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"关联用户Id\" prop=\"userId\">\n        <el-input\n          v-model=\"queryParams.userId\"\n          placeholder=\"请输入关联用户Id\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"关联活动Id\" prop=\"activityId\">\n        <el-input\n          v-model=\"queryParams.activityId\"\n          placeholder=\"请输入关联活动Id\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"操作类型\" prop=\"type\">\n        <el-select v-model=\"queryParams.type\" placeholder=\"请选择操作类型\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.user_history_type\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"信息\" prop=\"message\">\n        <el-input\n          v-model=\"queryParams.message\"\n          placeholder=\"请输入信息\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:userHistory:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:userHistory:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:userHistory:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:userHistory:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"userHistoryList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"\" align=\"center\" prop=\"historyId\" v-if=\"true\"/>\n      <el-table-column label=\"关联用户Id\" align=\"center\" prop=\"userId\" />\n      <el-table-column label=\"关联活动Id\" align=\"center\" prop=\"activityId\" />\n      <el-table-column label=\"操作类型\" align=\"center\" prop=\"type\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.user_history_type\" :value=\"scope.row.type\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"信息\" align=\"center\" prop=\"message\" />\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:userHistory:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:userHistory:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改用户操作历史记录对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"关联用户Id\" prop=\"userId\">\n          <el-input v-model=\"form.userId\" placeholder=\"请输入关联用户Id\" />\n        </el-form-item>\n        <el-form-item label=\"关联活动Id\" prop=\"activityId\">\n          <el-input v-model=\"form.activityId\" placeholder=\"请输入关联活动Id\" />\n        </el-form-item>\n        <el-form-item label=\"操作类型\" prop=\"type\">\n          <el-select v-model=\"form.type\" placeholder=\"请选择操作类型\">\n            <el-option\n              v-for=\"dict in dict.type.user_history_type\"\n              :key=\"dict.value\"\n              :label=\"dict.label\"\n              :value=\"parseInt(dict.value)\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"信息\" prop=\"message\">\n          <el-input v-model=\"form.message\" placeholder=\"请输入信息\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listUserHistory, getUserHistory, delUserHistory, addUserHistory, updateUserHistory } from \"@/api/system/userHistory\";\n\nexport default {\n  name: \"UserHistory\",\n  dicts: ['user_history_type'],\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 用户操作历史记录表格数据\n      userHistoryList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        userId: undefined,\n        activityId: undefined,\n        type: undefined,\n        message: undefined,\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        historyId: [\n          { required: true, message: \"不能为空\", trigger: \"blur\" }\n        ],\n        userId: [\n          { required: true, message: \"关联用户Id不能为空\", trigger: \"blur\" }\n        ],\n        activityId: [\n          { required: true, message: \"关联活动Id不能为空\", trigger: \"blur\" }\n        ],\n        type: [\n          { required: true, message: \"操作类型不能为空\", trigger: \"change\" }\n        ],\n        message: [\n          { required: true, message: \"信息不能为空\", trigger: \"blur\" }\n        ],\n        createTime: [\n          { required: true, message: \"记录创建时间不能为空\", trigger: \"blur\" }\n        ]\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询用户操作历史记录列表 */\n    getList() {\n      this.loading = true;\n      listUserHistory(this.queryParams).then(response => {\n        this.userHistoryList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        historyId: undefined,\n        userId: undefined,\n        activityId: undefined,\n        type: undefined,\n        message: undefined,\n        createTime: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.historyId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加用户操作历史记录\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const historyId = row.historyId || this.ids\n      getUserHistory(historyId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改用户操作历史记录\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.historyId != null) {\n            updateUserHistory(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addUserHistory(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const historyIds = row.historyId || this.ids;\n      this.$modal.confirm('是否确认删除用户操作历史记录编号为\"' + historyIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delUserHistory(historyIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/userHistory/export', {\n        ...this.queryParams\n      }, `userHistory_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/userPhoto/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"用户ID\" prop=\"userId\">\n        <el-input\n          v-model=\"queryParams.userId\"\n          placeholder=\"请输入用户ID\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"照片说明\" prop=\"message\">\n        <el-input\n          v-model=\"queryParams.message\"\n          placeholder=\"请输入照片说明\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.createTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择创建时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item label=\"更新时间\" prop=\"updateTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.updateTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择更新时间\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:userPhoto:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:userPhoto:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:userPhoto:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:userPhoto:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"userPhotoList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"照片ID\" align=\"center\" prop=\"photoId\" v-if=\"true\"/>\n      <el-table-column label=\"用户ID\" align=\"center\" prop=\"userId\" />\n      <el-table-column label=\"照片\" align=\"center\" prop=\"url\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <image-preview :src=\"scope.row.url\" :width=\"50\" :height=\"50\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"照片说明\" align=\"center\" prop=\"message\" />\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"更新时间\" align=\"center\" prop=\"updateTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:userPhoto:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:userPhoto:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改用户资料相册对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"用户ID\" prop=\"userId\">\n          <el-input v-model=\"form.userId\" placeholder=\"请输入用户ID\" />\n        </el-form-item>\n        <el-form-item label=\"照片\" prop=\"url\">\n          <image-upload v-model=\"form.url\"/>\n        </el-form-item>\n        <el-form-item label=\"照片说明\" prop=\"message\">\n          <el-input v-model=\"form.message\" placeholder=\"请输入照片说明\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listUserPhoto, getUserPhoto, delUserPhoto, addUserPhoto, updateUserPhoto } from \"@/api/system/userPhoto\";\n\nexport default {\n  name: \"UserPhoto\",\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 用户资料相册表格数据\n      userPhotoList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        userId: undefined,\n        url: undefined,\n        message: undefined,\n        createTime: undefined,\n        updateTime: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        photoId: [\n          { required: true, message: \"照片ID不能为空\", trigger: \"blur\" }\n        ],\n        userId: [\n          { required: true, message: \"用户ID不能为空\", trigger: \"blur\" }\n        ],\n        url: [\n          { required: true, message: \"照片不能为空\", trigger: \"blur\" }\n        ],\n        message: [\n          { required: true, message: \"照片说明不能为空\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询用户资料相册列表 */\n    getList() {\n      this.loading = true;\n      listUserPhoto(this.queryParams).then(response => {\n        this.userPhotoList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        photoId: undefined,\n        userId: undefined,\n        url: undefined,\n        message: undefined,\n        createTime: undefined,\n        updateTime: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.photoId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加用户资料相册\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const photoId = row.photoId || this.ids\n      getUserPhoto(photoId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改用户资料相册\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.photoId != null) {\n            updateUserPhoto(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addUserPhoto(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const photoIds = row.photoId || this.ids;\n      this.$modal.confirm('是否确认删除用户资料相册编号为\"' + photoIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delUserPhoto(photoIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/userPhoto/export', {\n        ...this.queryParams\n      }, `userPhoto_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/userTalk/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"发起方\" prop=\"fromUserId\">\n        <el-input\n          v-model=\"queryParams.fromUserId\"\n          placeholder=\"请输入发起方\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"接受方\" prop=\"toUserId\">\n        <el-input\n          v-model=\"queryParams.toUserId\"\n          placeholder=\"请输入接受方\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"消息\" prop=\"message\">\n        <el-input\n          v-model=\"queryParams.message\"\n          placeholder=\"请输入消息\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"消息状态\" prop=\"messageStatus\">\n        <el-select v-model=\"queryParams.messageStatus\" placeholder=\"请选择消息状态\" clearable>\n          <el-option\n            v-for=\"dict in dict.type.user_talk_msg_status\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"\" prop=\"createTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.createTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item label=\"\" prop=\"updateTime\">\n        <el-date-picker clearable\n          v-model=\"queryParams.updateTime\"\n          type=\"date\"\n          value-format=\"yyyy-MM-dd\"\n          placeholder=\"请选择\">\n        </el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:userTalk:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:userTalk:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:userTalk:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:userTalk:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"userTalkList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"聊天ID\" align=\"center\" prop=\"talkId\" v-if=\"true\"/>\n      <el-table-column label=\"发起方\" align=\"center\" prop=\"fromUserId\" />\n      <el-table-column label=\"接受方\" align=\"center\" prop=\"toUserId\" />\n      <el-table-column label=\"消息\" align=\"center\" prop=\"message\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.user_talk_msg_type\" :value=\"scope.row.message\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"消息状态\" align=\"center\" prop=\"messageStatus\">\n        <template slot-scope=\"scope\">\n          <dict-tag :options=\"dict.type.user_talk_msg_status\" :value=\"scope.row.messageStatus\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"消息类型\" align=\"center\" prop=\"messageType\" />\n      <el-table-column label=\"\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"\" align=\"center\" prop=\"updateTime\" width=\"180\">\n        <template slot-scope=\"scope\">\n          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:userTalk:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:userTalk:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改用户聊天对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"发起方\" prop=\"fromUserId\">\n          <el-input v-model=\"form.fromUserId\" placeholder=\"请输入发起方\" />\n        </el-form-item>\n        <el-form-item label=\"接受方\" prop=\"toUserId\">\n          <el-input v-model=\"form.toUserId\" placeholder=\"请输入接受方\" />\n        </el-form-item>\n        <el-form-item label=\"消息\" prop=\"message\">\n          <el-input v-model=\"form.message\" type=\"textarea\" placeholder=\"请输入内容\" />\n        </el-form-item>\n        <el-form-item label=\"消息状态\" prop=\"messageStatus\">\n          <el-radio-group v-model=\"form.messageStatus\">\n            <el-radio\n              v-for=\"dict in dict.type.user_talk_msg_status\"\n              :key=\"dict.value\"\n              :label=\"parseInt(dict.value)\"\n            >{{dict.label}}</el-radio>\n          </el-radio-group>\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listUserTalk, getUserTalk, delUserTalk, addUserTalk, updateUserTalk } from \"@/api/system/userTalk\";\n\nexport default {\n  name: \"UserTalk\",\n  dicts: ['user_talk_msg_status'],\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 用户聊天表格数据\n      userTalkList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        fromUserId: undefined,\n        toUserId: undefined,\n        message: undefined,\n        messageStatus: undefined,\n        messageType: undefined,\n        createTime: undefined,\n        updateTime: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        talkId: [\n          { required: true, message: \"聊天ID不能为空\", trigger: \"blur\" }\n        ],\n        fromUserId: [\n          { required: true, message: \"发起方不能为空\", trigger: \"blur\" }\n        ],\n        toUserId: [\n          { required: true, message: \"接受方不能为空\", trigger: \"blur\" }\n        ],\n        message: [\n          { required: true, message: \"消息不能为空\", trigger: \"blur\" }\n        ],\n        messageStatus: [\n          { required: true, message: \"消息状态不能为空\", trigger: \"change\" }\n        ],\n        messageType: [\n          { required: true, message: \"消息类型不能为空\", trigger: \"change\" }\n        ],\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询用户聊天列表 */\n    getList() {\n      this.loading = true;\n      listUserTalk(this.queryParams).then(response => {\n        this.userTalkList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        talkId: undefined,\n        fromUserId: undefined,\n        toUserId: undefined,\n        message: undefined,\n        messageStatus: undefined,\n        messageType: undefined,\n        createTime: undefined,\n        updateTime: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.talkId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加用户聊天\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const talkId = row.talkId || this.ids\n      getUserTalk(talkId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改用户聊天\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.talkId != null) {\n            updateUserTalk(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addUserTalk(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const talkIds = row.talkId || this.ids;\n      this.$modal.confirm('是否确认删除用户聊天编号为\"' + talkIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delUserTalk(talkIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/userTalk/export', {\n        ...this.queryParams\n      }, `userTalk_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/system/viewPager/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"轮播图名称\" prop=\"name\">\n        <el-input\n          v-model=\"queryParams.name\"\n          placeholder=\"请输入轮播图名称\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"轮播图链接Url\" prop=\"linkUrl\">\n        <el-input\n          v-model=\"queryParams.linkUrl\"\n          placeholder=\"请输入轮播图链接Url\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"删除状态，默认为1表示正常状态\" prop=\"state\">\n        <el-input\n          v-model=\"queryParams.state\"\n          placeholder=\"请输入删除状态，默认为1表示正常状态\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"关联活动id  0表示不关联\" prop=\"activityId\">\n        <el-input\n          v-model=\"queryParams.activityId\"\n          placeholder=\"请输入关联活动id  0表示不关联\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-plus\"\n          size=\"mini\"\n          @click=\"handleAdd\"\n          v-hasPermi=\"['system:viewPager:add']\"\n        >新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-hasPermi=\"['system:viewPager:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['system:viewPager:remove']\"\n        >删除</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleExport\"\n          v-hasPermi=\"['system:viewPager:export']\"\n        >导出</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"viewPagerList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"轮播图id\" align=\"center\" prop=\"viewPagerId\" v-if=\"true\"/>\n      <el-table-column label=\"轮播图名称\" align=\"center\" prop=\"name\" />\n      <el-table-column label=\"轮播图图片Url\" align=\"center\" prop=\"imageUrl\" width=\"100\">\n        <template slot-scope=\"scope\">\n          <image-preview :src=\"scope.row.imageUrl\" :width=\"50\" :height=\"50\"/>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"轮播图链接Url\" align=\"center\" prop=\"linkUrl\" />\n      <el-table-column label=\"删除状态，默认为1表示正常状态\" align=\"center\" prop=\"state\" />\n      <el-table-column label=\"关联活动id  0表示不关联\" align=\"center\" prop=\"activityId\" />\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-edit\"\n            @click=\"handleUpdate(scope.row)\"\n            v-hasPermi=\"['system:viewPager:edit']\"\n          >修改</el-button>\n          <el-button\n            size=\"mini\"\n            type=\"text\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['system:viewPager:remove']\"\n          >删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n\n    <!-- 添加或修改轮播图对话框 -->\n    <el-dialog :title=\"title\" :visible.sync=\"open\" width=\"500px\" append-to-body>\n      <el-form ref=\"form\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n        <el-form-item label=\"轮播图名称\" prop=\"name\">\n          <el-input v-model=\"form.name\" placeholder=\"请输入轮播图名称\" />\n        </el-form-item>\n        <el-form-item label=\"轮播图图片Url\" prop=\"imageUrl\">\n          <image-upload v-model=\"form.imageUrl\"/>\n        </el-form-item>\n        <el-form-item label=\"轮播图链接Url\" prop=\"linkUrl\">\n          <el-input v-model=\"form.linkUrl\" placeholder=\"请输入轮播图链接Url\" />\n        </el-form-item>\n        <el-form-item label=\"删除状态，默认为1表示正常状态\" prop=\"state\">\n          <el-input v-model=\"form.state\" placeholder=\"请输入删除状态，默认为1表示正常状态\" />\n        </el-form-item>\n        <el-form-item label=\"关联活动id  0表示不关联\" prop=\"activityId\">\n          <el-input v-model=\"form.activityId\" placeholder=\"请输入关联活动id  0表示不关联\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button :loading=\"buttonLoading\" type=\"primary\" @click=\"submitForm\">确 定</el-button>\n        <el-button @click=\"cancel\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { listViewPager, getViewPager, delViewPager, addViewPager, updateViewPager } from \"@/api/system/viewPager\";\n\nexport default {\n  name: \"ViewPager\",\n  data() {\n    return {\n      // 按钮loading\n      buttonLoading: false,\n      // 遮罩层\n      loading: true,\n      // 选中数组\n      ids: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 轮播图表格数据\n      viewPagerList: [],\n      // 弹出层标题\n      title: \"\",\n      // 是否显示弹出层\n      open: false,\n      // 关联活动id  0表示不关联时间范围\n      daterangeCreateTime: [],\n      // 关联活动id  0表示不关联时间范围\n      daterangeUpdateTime: [],\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        name: undefined,\n        imageUrl: undefined,\n        linkUrl: undefined,\n        state: undefined,\n        activityId: undefined\n      },\n      // 表单参数\n      form: {},\n      // 表单校验\n      rules: {\n        viewPagerId: [\n          { required: true, message: \"轮播图id不能为空\", trigger: \"blur\" }\n        ],\n        name: [\n          { required: true, message: \"轮播图名称不能为空\", trigger: \"blur\" }\n        ],\n        imageUrl: [\n          { required: true, message: \"轮播图图片Url不能为空\", trigger: \"blur\" }\n        ],\n        linkUrl: [\n          { required: true, message: \"轮播图链接Url不能为空\", trigger: \"blur\" }\n        ],\n        createTime: [\n          { required: true, message: \"创建时间不能为空\", trigger: \"blur\" }\n        ],\n        updateTime: [\n          { required: true, message: \"更新时间不能为空\", trigger: \"blur\" }\n        ],\n        state: [\n          { required: true, message: \"删除状态，默认为1表示正常状态不能为空\", trigger: \"blur\" }\n        ],\n        activityId: [\n          { required: true, message: \"关联活动id  0表示不关联不能为空\", trigger: \"blur\" }\n        ]\n      }\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询轮播图列表 */\n    getList() {\n      this.loading = true;\n      this.queryParams.params = {};\n      if (null != this.daterangeCreateTime && '' != this.daterangeCreateTime) {\n        this.queryParams.params[\"beginCreateTime\"] = this.daterangeCreateTime[0];\n        this.queryParams.params[\"endCreateTime\"] = this.daterangeCreateTime[1];\n      }\n      if (null != this.daterangeUpdateTime && '' != this.daterangeUpdateTime) {\n        this.queryParams.params[\"beginUpdateTime\"] = this.daterangeUpdateTime[0];\n        this.queryParams.params[\"endUpdateTime\"] = this.daterangeUpdateTime[1];\n      }\n      listViewPager(this.queryParams).then(response => {\n        this.viewPagerList = response.rows;\n        this.total = response.total;\n        this.loading = false;\n      });\n    },\n    // 取消按钮\n    cancel() {\n      this.open = false;\n      this.reset();\n    },\n    // 表单重置\n    reset() {\n      this.form = {\n        viewPagerId: undefined,\n        name: undefined,\n        imageUrl: undefined,\n        linkUrl: undefined,\n        createTime: undefined,\n        updateTime: undefined,\n        state: undefined,\n        activityId: undefined\n      };\n      this.resetForm(\"form\");\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.daterangeCreateTime = [];\n      this.daterangeUpdateTime = [];\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.viewPagerId)\n      this.single = selection.length!==1\n      this.multiple = !selection.length\n    },\n    /** 新增按钮操作 */\n    handleAdd() {\n      this.reset();\n      this.open = true;\n      this.title = \"添加轮播图\";\n    },\n    /** 修改按钮操作 */\n    handleUpdate(row) {\n      this.loading = true;\n      this.reset();\n      const viewPagerId = row.viewPagerId || this.ids\n      getViewPager(viewPagerId).then(response => {\n        this.loading = false;\n        this.form = response.data;\n        this.open = true;\n        this.title = \"修改轮播图\";\n      });\n    },\n    /** 提交按钮 */\n    submitForm() {\n      this.$refs[\"form\"].validate(valid => {\n        if (valid) {\n          this.buttonLoading = true;\n          if (this.form.viewPagerId != null) {\n            updateViewPager(this.form).then(response => {\n              this.$modal.msgSuccess(\"修改成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          } else {\n            addViewPager(this.form).then(response => {\n              this.$modal.msgSuccess(\"新增成功\");\n              this.open = false;\n              this.getList();\n            }).finally(() => {\n              this.buttonLoading = false;\n            });\n          }\n        }\n      });\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const viewPagerIds = row.viewPagerId || this.ids;\n      this.$modal.confirm('是否确认删除轮播图编号为\"' + viewPagerIds + '\"的数据项？').then(() => {\n        this.loading = true;\n        return delViewPager(viewPagerIds);\n      }).then(() => {\n        this.loading = false;\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {\n      }).finally(() => {\n        this.loading = false;\n      });\n    },\n    /** 导出按钮操作 */\n    handleExport() {\n      this.download('system/viewPager/export', {\n        ...this.queryParams\n      }, `viewPager_${new Date().getTime()}.xlsx`)\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/tool/build/CodeTypeDialog.vue",
    "content": "<template>\n  <div>\n    <el-dialog\n      v-bind=\"$attrs\"\n      width=\"500px\"\n      :close-on-click-modal=\"false\"\n      :modal-append-to-body=\"false\"\n      v-on=\"$listeners\"\n      @open=\"onOpen\"\n      @close=\"onClose\"\n    >\n      <el-row :gutter=\"15\">\n        <el-form\n          ref=\"elForm\"\n          :model=\"formData\"\n          :rules=\"rules\"\n          size=\"medium\"\n          label-width=\"100px\"\n        >\n          <el-col :span=\"24\">\n            <el-form-item label=\"生成类型\" prop=\"type\">\n              <el-radio-group v-model=\"formData.type\">\n                <el-radio-button\n                  v-for=\"(item, index) in typeOptions\"\n                  :key=\"index\"\n                  :label=\"item.value\"\n                  :disabled=\"item.disabled\"\n                >\n                  {{ item.label }}\n                </el-radio-button>\n              </el-radio-group>\n            </el-form-item>\n            <el-form-item v-if=\"showFileName\" label=\"文件名\" prop=\"fileName\">\n              <el-input v-model=\"formData.fileName\" placeholder=\"请输入文件名\" clearable />\n            </el-form-item>\n          </el-col>\n        </el-form>\n      </el-row>\n\n      <div slot=\"footer\">\n        <el-button @click=\"close\">\n          取消\n        </el-button>\n        <el-button type=\"primary\" @click=\"handleConfirm\">\n          确定\n        </el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n<script>\nexport default {\n  inheritAttrs: false,\n  props: ['showFileName'],\n  data() {\n    return {\n      formData: {\n        fileName: undefined,\n        type: 'file'\n      },\n      rules: {\n        fileName: [{\n          required: true,\n          message: '请输入文件名',\n          trigger: 'blur'\n        }],\n        type: [{\n          required: true,\n          message: '生成类型不能为空',\n          trigger: 'change'\n        }]\n      },\n      typeOptions: [{\n        label: '页面',\n        value: 'file'\n      }, {\n        label: '弹窗',\n        value: 'dialog'\n      }]\n    }\n  },\n  computed: {\n  },\n  watch: {},\n  mounted() {},\n  methods: {\n    onOpen() {\n      if (this.showFileName) {\n        this.formData.fileName = `${+new Date()}.vue`\n      }\n    },\n    onClose() {\n    },\n    close(e) {\n      this.$emit('update:visible', false)\n    },\n    handleConfirm() {\n      this.$refs.elForm.validate(valid => {\n        if (!valid) return\n        this.$emit('confirm', { ...this.formData })\n        this.close()\n      })\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/tool/build/DraggableItem.vue",
    "content": "<script>\nimport draggable from 'vuedraggable'\nimport render from '@/utils/generator/render'\n\nconst components = {\n  itemBtns(h, element, index, parent) {\n    const { copyItem, deleteItem } = this.$listeners\n    return [\n      <span class=\"drawing-item-copy\" title=\"复制\" onClick={event => {\n        copyItem(element, parent); event.stopPropagation()\n      }}>\n        <i class=\"el-icon-copy-document\" />\n      </span>,\n      <span class=\"drawing-item-delete\" title=\"删除\" onClick={event => {\n        deleteItem(index, parent); event.stopPropagation()\n      }}>\n        <i class=\"el-icon-delete\" />\n      </span>\n    ]\n  }\n}\nconst layouts = {\n  colFormItem(h, element, index, parent) {\n    const { activeItem } = this.$listeners\n    let className = this.activeId === element.formId ? 'drawing-item active-from-item' : 'drawing-item'\n    if (this.formConf.unFocusedComponentBorder) className += ' unfocus-bordered'\n    return (\n      <el-col span={element.span} class={className}\n        nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>\n        <el-form-item label-width={element.labelWidth ? `${element.labelWidth}px` : null}\n          label={element.label} required={element.required}>\n          <render key={element.renderKey} conf={element} onInput={ event => {\n            this.$set(element, 'defaultValue', event)\n          }} />\n        </el-form-item>\n        {components.itemBtns.apply(this, arguments)}\n      </el-col>\n    )\n  },\n  rowFormItem(h, element, index, parent) {\n    const { activeItem } = this.$listeners\n    const className = this.activeId === element.formId ? 'drawing-row-item active-from-item' : 'drawing-row-item'\n    let child = renderChildren.apply(this, arguments)\n    if (element.type === 'flex') {\n      child = <el-row type={element.type} justify={element.justify} align={element.align}>\n              {child}\n            </el-row>\n    }\n    return (\n      <el-col span={element.span}>\n        <el-row gutter={element.gutter} class={className}\n          nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>\n          <span class=\"component-name\">{element.componentName}</span>\n          <draggable list={element.children} animation={340} group=\"componentsGroup\" class=\"drag-wrapper\">\n            {child}\n          </draggable>\n          {components.itemBtns.apply(this, arguments)}\n        </el-row>\n      </el-col>\n    )\n  }\n}\n\nfunction renderChildren(h, element, index, parent) {\n  if (!Array.isArray(element.children)) return null\n  return element.children.map((el, i) => {\n    const layout = layouts[el.layout]\n    if (layout) {\n      return layout.call(this, h, el, i, element.children)\n    }\n    return layoutIsNotFound()\n  })\n}\n\nfunction layoutIsNotFound() {\n  throw new Error(`没有与${this.element.layout}匹配的layout`)\n}\n\nexport default {\n  components: {\n    render,\n    draggable\n  },\n  props: [\n    'element',\n    'index',\n    'drawingList',\n    'activeId',\n    'formConf'\n  ],\n  render(h) {\n    const layout = layouts[this.element.layout]\n\n    if (layout) {\n      return layout.call(this, h, this.element, this.index, this.drawingList)\n    }\n    return layoutIsNotFound()\n  }\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/tool/build/IconsDialog.vue",
    "content": "<template>\n  <div class=\"icon-dialog\">\n    <el-dialog\n      v-bind=\"$attrs\"\n      width=\"980px\"\n      :modal-append-to-body=\"false\"\n      v-on=\"$listeners\"\n      @open=\"onOpen\"\n      @close=\"onClose\"\n    >\n      <div slot=\"title\">\n        选择图标\n        <el-input\n          v-model=\"key\"\n          size=\"mini\"\n          :style=\"{width: '260px'}\"\n          placeholder=\"请输入图标名称\"\n          prefix-icon=\"el-icon-search\"\n          clearable\n        />\n      </div>\n      <ul class=\"icon-ul\">\n        <li\n          v-for=\"icon in iconList\"\n          :key=\"icon\"\n          :class=\"active===icon?'active-item':''\"\n          @click=\"onSelect(icon)\"\n        >\n          <i :class=\"icon\" />\n          <div>{{ icon }}</div>\n        </li>\n      </ul>\n    </el-dialog>\n  </div>\n</template>\n<script>\nimport iconList from '@/utils/generator/icon.json'\n\nconst originList = iconList.map(name => `el-icon-${name}`)\n\nexport default {\n  inheritAttrs: false,\n  props: ['current'],\n  data() {\n    return {\n      iconList: originList,\n      active: null,\n      key: ''\n    }\n  },\n  watch: {\n    key(val) {\n      if (val) {\n        this.iconList = originList.filter(name => name.indexOf(val) > -1)\n      } else {\n        this.iconList = originList\n      }\n    }\n  },\n  methods: {\n    onOpen() {\n      this.active = this.current\n      this.key = ''\n    },\n    onClose() {},\n    onSelect(icon) {\n      this.active = icon\n      this.$emit('select', icon)\n      this.$emit('update:visible', false)\n    }\n  }\n}\n</script>\n<style lang=\"scss\" scoped>\n.icon-ul {\n  margin: 0;\n  padding: 0;\n  font-size: 0;\n  li {\n    list-style-type: none;\n    text-align: center;\n    font-size: 14px;\n    display: inline-block;\n    width: 16.66%;\n    box-sizing: border-box;\n    height: 108px;\n    padding: 15px 6px 6px 6px;\n    cursor: pointer;\n    overflow: hidden;\n    &:hover {\n      background: #f2f2f2;\n    }\n    &.active-item{\n      background: #e1f3fb;\n      color: #7a6df0\n    }\n    > i {\n      font-size: 30px;\n      line-height: 50px;\n    }\n  }\n}\n.icon-dialog {\n  ::v-deep .el-dialog {\n    border-radius: 8px;\n    margin-bottom: 0;\n    margin-top: 4vh !important;\n    display: flex;\n    flex-direction: column;\n    max-height: 92vh;\n    overflow: hidden;\n    box-sizing: border-box;\n    .el-dialog__header {\n      padding-top: 14px;\n    }\n    .el-dialog__body {\n      margin: 0 20px 20px 20px;\n      padding: 0;\n      overflow: auto;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/views/tool/build/RightPanel.vue",
    "content": "<template>\n  <div class=\"right-board\">\n    <el-tabs v-model=\"currentTab\" class=\"center-tabs\">\n      <el-tab-pane label=\"组件属性\" name=\"field\" />\n      <el-tab-pane label=\"表单属性\" name=\"form\" />\n    </el-tabs>\n    <div class=\"field-box\">\n      <a class=\"document-link\" target=\"_blank\" :href=\"documentLink\" title=\"查看组件文档\">\n        <i class=\"el-icon-link\" />\n      </a>\n      <el-scrollbar class=\"right-scrollbar\">\n        <!-- 组件属性 -->\n        <el-form v-show=\"currentTab==='field' && showField\" size=\"small\" label-width=\"90px\">\n          <el-form-item v-if=\"activeData.changeTag\" label=\"组件类型\">\n            <el-select\n              v-model=\"activeData.tagIcon\"\n              placeholder=\"请选择组件类型\"\n              :style=\"{width: '100%'}\"\n              @change=\"tagChange\"\n            >\n              <el-option-group v-for=\"group in tagList\" :key=\"group.label\" :label=\"group.label\">\n                <el-option\n                  v-for=\"item in group.options\"\n                  :key=\"item.label\"\n                  :label=\"item.label\"\n                  :value=\"item.tagIcon\"\n                >\n                  <svg-icon class=\"node-icon\" :icon-class=\"item.tagIcon\" />\n                  <span> {{ item.label }}</span>\n                </el-option>\n              </el-option-group>\n            </el-select>\n          </el-form-item>\n          <el-form-item v-if=\"activeData.vModel!==undefined\" label=\"字段名\">\n            <el-input v-model=\"activeData.vModel\" placeholder=\"请输入字段名（v-model）\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.componentName!==undefined\" label=\"组件名\">\n            {{ activeData.componentName }}\n          </el-form-item>\n          <el-form-item v-if=\"activeData.label!==undefined\" label=\"标题\">\n            <el-input v-model=\"activeData.label\" placeholder=\"请输入标题\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.placeholder!==undefined\" label=\"占位提示\">\n            <el-input v-model=\"activeData.placeholder\" placeholder=\"请输入占位提示\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData['start-placeholder']!==undefined\" label=\"开始占位\">\n            <el-input v-model=\"activeData['start-placeholder']\" placeholder=\"请输入占位提示\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData['end-placeholder']!==undefined\" label=\"结束占位\">\n            <el-input v-model=\"activeData['end-placeholder']\" placeholder=\"请输入占位提示\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.span!==undefined\" label=\"表单栅格\">\n            <el-slider v-model=\"activeData.span\" :max=\"24\" :min=\"1\" :marks=\"{12:''}\" @change=\"spanChange\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.layout==='rowFormItem'\" label=\"栅格间隔\">\n            <el-input-number v-model=\"activeData.gutter\" :min=\"0\" placeholder=\"栅格间隔\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.layout==='rowFormItem'\" label=\"布局模式\">\n            <el-radio-group v-model=\"activeData.type\">\n              <el-radio-button label=\"default\" />\n              <el-radio-button label=\"flex\" />\n            </el-radio-group>\n          </el-form-item>\n          <el-form-item v-if=\"activeData.justify!==undefined&&activeData.type==='flex'\" label=\"水平排列\">\n            <el-select v-model=\"activeData.justify\" placeholder=\"请选择水平排列\" :style=\"{width: '100%'}\">\n              <el-option\n                v-for=\"(item, index) in justifyOptions\"\n                :key=\"index\"\n                :label=\"item.label\"\n                :value=\"item.value\"\n              />\n            </el-select>\n          </el-form-item>\n          <el-form-item v-if=\"activeData.align!==undefined&&activeData.type==='flex'\" label=\"垂直排列\">\n            <el-radio-group v-model=\"activeData.align\">\n              <el-radio-button label=\"top\" />\n              <el-radio-button label=\"middle\" />\n              <el-radio-button label=\"bottom\" />\n            </el-radio-group>\n          </el-form-item>\n          <el-form-item v-if=\"activeData.labelWidth!==undefined\" label=\"标签宽度\">\n            <el-input v-model.number=\"activeData.labelWidth\" type=\"number\" placeholder=\"请输入标签宽度\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.style&&activeData.style.width!==undefined\" label=\"组件宽度\">\n            <el-input v-model=\"activeData.style.width\" placeholder=\"请输入组件宽度\" clearable />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.vModel!==undefined\" label=\"默认值\">\n            <el-input\n              :value=\"setDefaultValue(activeData.defaultValue)\"\n              placeholder=\"请输入默认值\"\n              @input=\"onDefaultValueInput\"\n            />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.tag==='el-checkbox-group'\" label=\"至少应选\">\n            <el-input-number\n              :value=\"activeData.min\"\n              :min=\"0\"\n              placeholder=\"至少应选\"\n              @input=\"$set(activeData, 'min', $event?$event:undefined)\"\n            />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.tag==='el-checkbox-group'\" label=\"最多可选\">\n            <el-input-number\n              :value=\"activeData.max\"\n              :min=\"0\"\n              placeholder=\"最多可选\"\n              @input=\"$set(activeData, 'max', $event?$event:undefined)\"\n            />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.prepend!==undefined\" label=\"前缀\">\n            <el-input v-model=\"activeData.prepend\" placeholder=\"请输入前缀\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.append!==undefined\" label=\"后缀\">\n            <el-input v-model=\"activeData.append\" placeholder=\"请输入后缀\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData['prefix-icon']!==undefined\" label=\"前图标\">\n            <el-input v-model=\"activeData['prefix-icon']\" placeholder=\"请输入前图标名称\">\n              <el-button slot=\"append\" icon=\"el-icon-thumb\" @click=\"openIconsDialog('prefix-icon')\">\n                选择\n              </el-button>\n            </el-input>\n          </el-form-item>\n          <el-form-item v-if=\"activeData['suffix-icon'] !== undefined\" label=\"后图标\">\n            <el-input v-model=\"activeData['suffix-icon']\" placeholder=\"请输入后图标名称\">\n              <el-button slot=\"append\" icon=\"el-icon-thumb\" @click=\"openIconsDialog('suffix-icon')\">\n                选择\n              </el-button>\n            </el-input>\n          </el-form-item>\n          <el-form-item v-if=\"activeData.tag === 'el-cascader'\" label=\"选项分隔符\">\n            <el-input v-model=\"activeData.separator\" placeholder=\"请输入选项分隔符\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.autosize !== undefined\" label=\"最小行数\">\n            <el-input-number v-model=\"activeData.autosize.minRows\" :min=\"1\" placeholder=\"最小行数\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.autosize !== undefined\" label=\"最大行数\">\n            <el-input-number v-model=\"activeData.autosize.maxRows\" :min=\"1\" placeholder=\"最大行数\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.min !== undefined\" label=\"最小值\">\n            <el-input-number v-model=\"activeData.min\" placeholder=\"最小值\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.max !== undefined\" label=\"最大值\">\n            <el-input-number v-model=\"activeData.max\" placeholder=\"最大值\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.step !== undefined\" label=\"步长\">\n            <el-input-number v-model=\"activeData.step\" placeholder=\"步数\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.tag === 'el-input-number'\" label=\"精度\">\n            <el-input-number v-model=\"activeData.precision\" :min=\"0\" placeholder=\"精度\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.tag === 'el-input-number'\" label=\"按钮位置\">\n            <el-radio-group v-model=\"activeData['controls-position']\">\n              <el-radio-button label=\"\">\n                默认\n              </el-radio-button>\n              <el-radio-button label=\"right\">\n                右侧\n              </el-radio-button>\n            </el-radio-group>\n          </el-form-item>\n          <el-form-item v-if=\"activeData.maxlength !== undefined\" label=\"最多输入\">\n            <el-input v-model=\"activeData.maxlength\" placeholder=\"请输入字符长度\">\n              <template slot=\"append\">\n                个字符\n              </template>\n            </el-input>\n          </el-form-item>\n          <el-form-item v-if=\"activeData['active-text'] !== undefined\" label=\"开启提示\">\n            <el-input v-model=\"activeData['active-text']\" placeholder=\"请输入开启提示\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData['inactive-text'] !== undefined\" label=\"关闭提示\">\n            <el-input v-model=\"activeData['inactive-text']\" placeholder=\"请输入关闭提示\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData['active-value'] !== undefined\" label=\"开启值\">\n            <el-input\n              :value=\"setDefaultValue(activeData['active-value'])\"\n              placeholder=\"请输入开启值\"\n              @input=\"onSwitchValueInput($event, 'active-value')\"\n            />\n          </el-form-item>\n          <el-form-item v-if=\"activeData['inactive-value'] !== undefined\" label=\"关闭值\">\n            <el-input\n              :value=\"setDefaultValue(activeData['inactive-value'])\"\n              placeholder=\"请输入关闭值\"\n              @input=\"onSwitchValueInput($event, 'inactive-value')\"\n            />\n          </el-form-item>\n          <el-form-item\n            v-if=\"activeData.type !== undefined && 'el-date-picker' === activeData.tag\"\n            label=\"时间类型\"\n          >\n            <el-select\n              v-model=\"activeData.type\"\n              placeholder=\"请选择时间类型\"\n              :style=\"{ width: '100%' }\"\n              @change=\"dateTypeChange\"\n            >\n              <el-option\n                v-for=\"(item, index) in dateOptions\"\n                :key=\"index\"\n                :label=\"item.label\"\n                :value=\"item.value\"\n              />\n            </el-select>\n          </el-form-item>\n          <el-form-item v-if=\"activeData.name !== undefined\" label=\"文件字段名\">\n            <el-input v-model=\"activeData.name\" placeholder=\"请输入上传文件字段名\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.accept !== undefined\" label=\"文件类型\">\n            <el-select\n              v-model=\"activeData.accept\"\n              placeholder=\"请选择文件类型\"\n              :style=\"{ width: '100%' }\"\n              clearable\n            >\n              <el-option label=\"图片\" value=\"image/*\" />\n              <el-option label=\"视频\" value=\"video/*\" />\n              <el-option label=\"音频\" value=\"audio/*\" />\n              <el-option label=\"excel\" value=\".xls,.xlsx\" />\n              <el-option label=\"word\" value=\".doc,.docx\" />\n              <el-option label=\"pdf\" value=\".pdf\" />\n              <el-option label=\"txt\" value=\".txt\" />\n            </el-select>\n          </el-form-item>\n          <el-form-item v-if=\"activeData.fileSize !== undefined\" label=\"文件大小\">\n            <el-input v-model.number=\"activeData.fileSize\" placeholder=\"请输入文件大小\">\n              <el-select slot=\"append\" v-model=\"activeData.sizeUnit\" :style=\"{ width: '66px' }\">\n                <el-option label=\"KB\" value=\"KB\" />\n                <el-option label=\"MB\" value=\"MB\" />\n                <el-option label=\"GB\" value=\"GB\" />\n              </el-select>\n            </el-input>\n          </el-form-item>\n          <el-form-item v-if=\"activeData.action !== undefined\" label=\"上传地址\">\n            <el-input v-model=\"activeData.action\" placeholder=\"请输入上传地址\" clearable />\n          </el-form-item>\n          <el-form-item v-if=\"activeData['list-type'] !== undefined\" label=\"列表类型\">\n            <el-radio-group v-model=\"activeData['list-type']\" size=\"small\">\n              <el-radio-button label=\"text\">\n                text\n              </el-radio-button>\n              <el-radio-button label=\"picture\">\n                picture\n              </el-radio-button>\n              <el-radio-button label=\"picture-card\">\n                picture-card\n              </el-radio-button>\n            </el-radio-group>\n          </el-form-item>\n          <el-form-item\n            v-if=\"activeData.buttonText !== undefined\"\n            v-show=\"'picture-card' !== activeData['list-type']\"\n            label=\"按钮文字\"\n          >\n            <el-input v-model=\"activeData.buttonText\" placeholder=\"请输入按钮文字\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData['range-separator'] !== undefined\" label=\"分隔符\">\n            <el-input v-model=\"activeData['range-separator']\" placeholder=\"请输入分隔符\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData['picker-options'] !== undefined\" label=\"时间段\">\n            <el-input\n              v-model=\"activeData['picker-options'].selectableRange\"\n              placeholder=\"请输入时间段\"\n            />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.format !== undefined\" label=\"时间格式\">\n            <el-input\n              :value=\"activeData.format\"\n              placeholder=\"请输入时间格式\"\n              @input=\"setTimeValue($event)\"\n            />\n          </el-form-item>\n          <template v-if=\"['el-checkbox-group', 'el-radio-group', 'el-select'].indexOf(activeData.tag) > -1\">\n            <el-divider>选项</el-divider>\n            <draggable\n              :list=\"activeData.options\"\n              :animation=\"340\"\n              group=\"selectItem\"\n              handle=\".option-drag\"\n            >\n              <div v-for=\"(item, index) in activeData.options\" :key=\"index\" class=\"select-item\">\n                <div class=\"select-line-icon option-drag\">\n                  <i class=\"el-icon-s-operation\" />\n                </div>\n                <el-input v-model=\"item.label\" placeholder=\"选项名\" size=\"small\" />\n                <el-input\n                  placeholder=\"选项值\"\n                  size=\"small\"\n                  :value=\"item.value\"\n                  @input=\"setOptionValue(item, $event)\"\n                />\n                <div class=\"close-btn select-line-icon\" @click=\"activeData.options.splice(index, 1)\">\n                  <i class=\"el-icon-remove-outline\" />\n                </div>\n              </div>\n            </draggable>\n            <div style=\"margin-left: 20px;\">\n              <el-button\n                style=\"padding-bottom: 0\"\n                icon=\"el-icon-circle-plus-outline\"\n                type=\"text\"\n                @click=\"addSelectItem\"\n              >\n                添加选项\n              </el-button>\n            </div>\n            <el-divider />\n          </template>\n\n          <template v-if=\"['el-cascader'].indexOf(activeData.tag) > -1\">\n            <el-divider>选项</el-divider>\n            <el-form-item label=\"数据类型\">\n              <el-radio-group v-model=\"activeData.dataType\" size=\"small\">\n                <el-radio-button label=\"dynamic\">\n                  动态数据\n                </el-radio-button>\n                <el-radio-button label=\"static\">\n                  静态数据\n                </el-radio-button>\n              </el-radio-group>\n            </el-form-item>\n\n            <template v-if=\"activeData.dataType === 'dynamic'\">\n              <el-form-item label=\"标签键名\">\n                <el-input v-model=\"activeData.labelKey\" placeholder=\"请输入标签键名\" />\n              </el-form-item>\n              <el-form-item label=\"值键名\">\n                <el-input v-model=\"activeData.valueKey\" placeholder=\"请输入值键名\" />\n              </el-form-item>\n              <el-form-item label=\"子级键名\">\n                <el-input v-model=\"activeData.childrenKey\" placeholder=\"请输入子级键名\" />\n              </el-form-item>\n            </template>\n\n            <el-tree\n              v-if=\"activeData.dataType === 'static'\"\n              draggable\n              :data=\"activeData.options\"\n              node-key=\"id\"\n              :expand-on-click-node=\"false\"\n              :render-content=\"renderContent\"\n            />\n            <div v-if=\"activeData.dataType === 'static'\" style=\"margin-left: 20px\">\n              <el-button\n                style=\"padding-bottom: 0\"\n                icon=\"el-icon-circle-plus-outline\"\n                type=\"text\"\n                @click=\"addTreeItem\"\n              >\n                添加父级\n              </el-button>\n            </div>\n            <el-divider />\n          </template>\n\n          <el-form-item v-if=\"activeData.optionType !== undefined\" label=\"选项样式\">\n            <el-radio-group v-model=\"activeData.optionType\">\n              <el-radio-button label=\"default\">\n                默认\n              </el-radio-button>\n              <el-radio-button label=\"button\">\n                按钮\n              </el-radio-button>\n            </el-radio-group>\n          </el-form-item>\n          <el-form-item v-if=\"activeData['active-color'] !== undefined\" label=\"开启颜色\">\n            <el-color-picker v-model=\"activeData['active-color']\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData['inactive-color'] !== undefined\" label=\"关闭颜色\">\n            <el-color-picker v-model=\"activeData['inactive-color']\" />\n          </el-form-item>\n\n          <el-form-item v-if=\"activeData['allow-half'] !== undefined\" label=\"允许半选\">\n            <el-switch v-model=\"activeData['allow-half']\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData['show-text'] !== undefined\" label=\"辅助文字\">\n            <el-switch v-model=\"activeData['show-text']\" @change=\"rateTextChange\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData['show-score'] !== undefined\" label=\"显示分数\">\n            <el-switch v-model=\"activeData['show-score']\" @change=\"rateScoreChange\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData['show-stops'] !== undefined\" label=\"显示间断点\">\n            <el-switch v-model=\"activeData['show-stops']\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.range !== undefined\" label=\"范围选择\">\n            <el-switch v-model=\"activeData.range\" @change=\"rangeChange\" />\n          </el-form-item>\n          <el-form-item\n            v-if=\"activeData.border !== undefined && activeData.optionType === 'default'\"\n            label=\"是否带边框\"\n          >\n            <el-switch v-model=\"activeData.border\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.tag === 'el-color-picker'\" label=\"颜色格式\">\n            <el-select\n              v-model=\"activeData['color-format']\"\n              placeholder=\"请选择颜色格式\"\n              :style=\"{ width: '100%' }\"\n              @change=\"colorFormatChange\"\n            >\n              <el-option\n                v-for=\"(item, index) in colorFormatOptions\"\n                :key=\"index\"\n                :label=\"item.label\"\n                :value=\"item.value\"\n              />\n            </el-select>\n          </el-form-item>\n          <el-form-item\n            v-if=\"activeData.size !== undefined &&\n              (activeData.optionType === 'button' ||\n                activeData.border ||\n                activeData.tag === 'el-color-picker')\"\n            label=\"选项尺寸\"\n          >\n            <el-radio-group v-model=\"activeData.size\">\n              <el-radio-button label=\"medium\">\n                中等\n              </el-radio-button>\n              <el-radio-button label=\"small\">\n                较小\n              </el-radio-button>\n              <el-radio-button label=\"mini\">\n                迷你\n              </el-radio-button>\n            </el-radio-group>\n          </el-form-item>\n          <el-form-item v-if=\"activeData['show-word-limit'] !== undefined\" label=\"输入统计\">\n            <el-switch v-model=\"activeData['show-word-limit']\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.tag === 'el-input-number'\" label=\"严格步数\">\n            <el-switch v-model=\"activeData['step-strictly']\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.tag === 'el-cascader'\" label=\"是否多选\">\n            <el-switch v-model=\"activeData.props.props.multiple\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.tag === 'el-cascader'\" label=\"展示全路径\">\n            <el-switch v-model=\"activeData['show-all-levels']\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.tag === 'el-cascader'\" label=\"可否筛选\">\n            <el-switch v-model=\"activeData.filterable\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.clearable !== undefined\" label=\"能否清空\">\n            <el-switch v-model=\"activeData.clearable\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.showTip !== undefined\" label=\"显示提示\">\n            <el-switch v-model=\"activeData.showTip\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.multiple !== undefined\" label=\"多选文件\">\n            <el-switch v-model=\"activeData.multiple\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData['auto-upload'] !== undefined\" label=\"自动上传\">\n            <el-switch v-model=\"activeData['auto-upload']\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.readonly !== undefined\" label=\"是否只读\">\n            <el-switch v-model=\"activeData.readonly\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.disabled !== undefined\" label=\"是否禁用\">\n            <el-switch v-model=\"activeData.disabled\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.tag === 'el-select'\" label=\"是否可搜索\">\n            <el-switch v-model=\"activeData.filterable\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.tag === 'el-select'\" label=\"是否多选\">\n            <el-switch v-model=\"activeData.multiple\" @change=\"multipleChange\" />\n          </el-form-item>\n          <el-form-item v-if=\"activeData.required !== undefined\" label=\"是否必填\">\n            <el-switch v-model=\"activeData.required\" />\n          </el-form-item>\n\n          <template v-if=\"activeData.layoutTree\">\n            <el-divider>布局结构树</el-divider>\n            <el-tree\n              :data=\"[activeData]\"\n              :props=\"layoutTreeProps\"\n              node-key=\"renderKey\"\n              default-expand-all\n              draggable\n            >\n              <span slot-scope=\"{ node, data }\">\n                <span class=\"node-label\">\n                  <svg-icon class=\"node-icon\" :icon-class=\"data.tagIcon\" />\n                  {{ node.label }}\n                </span>\n              </span>\n            </el-tree>\n          </template>\n\n          <template v-if=\"activeData.layout === 'colFormItem' && activeData.tag !== 'el-button'\">\n            <el-divider>正则校验</el-divider>\n            <div\n              v-for=\"(item, index) in activeData.regList\"\n              :key=\"index\"\n              class=\"reg-item\"\n            >\n              <span class=\"close-btn\" @click=\"activeData.regList.splice(index, 1)\">\n                <i class=\"el-icon-close\" />\n              </span>\n              <el-form-item label=\"表达式\">\n                <el-input v-model=\"item.pattern\" placeholder=\"请输入正则\" />\n              </el-form-item>\n              <el-form-item label=\"错误提示\" style=\"margin-bottom:0\">\n                <el-input v-model=\"item.message\" placeholder=\"请输入错误提示\" />\n              </el-form-item>\n            </div>\n            <div style=\"margin-left: 20px\">\n              <el-button icon=\"el-icon-circle-plus-outline\" type=\"text\" @click=\"addReg\">\n                添加规则\n              </el-button>\n            </div>\n          </template>\n        </el-form>\n        <!-- 表单属性 -->\n        <el-form v-show=\"currentTab === 'form'\" size=\"small\" label-width=\"90px\">\n          <el-form-item label=\"表单名\">\n            <el-input v-model=\"formConf.formRef\" placeholder=\"请输入表单名（ref）\" />\n          </el-form-item>\n          <el-form-item label=\"表单模型\">\n            <el-input v-model=\"formConf.formModel\" placeholder=\"请输入数据模型\" />\n          </el-form-item>\n          <el-form-item label=\"校验模型\">\n            <el-input v-model=\"formConf.formRules\" placeholder=\"请输入校验模型\" />\n          </el-form-item>\n          <el-form-item label=\"表单尺寸\">\n            <el-radio-group v-model=\"formConf.size\">\n              <el-radio-button label=\"medium\">\n                中等\n              </el-radio-button>\n              <el-radio-button label=\"small\">\n                较小\n              </el-radio-button>\n              <el-radio-button label=\"mini\">\n                迷你\n              </el-radio-button>\n            </el-radio-group>\n          </el-form-item>\n          <el-form-item label=\"标签对齐\">\n            <el-radio-group v-model=\"formConf.labelPosition\">\n              <el-radio-button label=\"left\">\n                左对齐\n              </el-radio-button>\n              <el-radio-button label=\"right\">\n                右对齐\n              </el-radio-button>\n              <el-radio-button label=\"top\">\n                顶部对齐\n              </el-radio-button>\n            </el-radio-group>\n          </el-form-item>\n          <el-form-item label=\"标签宽度\">\n            <el-input-number v-model=\"formConf.labelWidth\" placeholder=\"标签宽度\" />\n          </el-form-item>\n          <el-form-item label=\"栅格间隔\">\n            <el-input-number v-model=\"formConf.gutter\" :min=\"0\" placeholder=\"栅格间隔\" />\n          </el-form-item>\n          <el-form-item label=\"禁用表单\">\n            <el-switch v-model=\"formConf.disabled\" />\n          </el-form-item>\n          <el-form-item label=\"表单按钮\">\n            <el-switch v-model=\"formConf.formBtns\" />\n          </el-form-item>\n          <el-form-item label=\"显示未选中组件边框\">\n            <el-switch v-model=\"formConf.unFocusedComponentBorder\" />\n          </el-form-item>\n        </el-form>\n      </el-scrollbar>\n    </div>\n\n    <treeNode-dialog :visible.sync=\"dialogVisible\" title=\"添加选项\" @commit=\"addNode\" />\n    <icons-dialog :visible.sync=\"iconsVisible\" :current=\"activeData[currentIconModel]\" @select=\"setIcon\" />\n  </div>\n</template>\n\n<script>\nimport { isArray } from 'util'\nimport draggable from 'vuedraggable'\nimport TreeNodeDialog from './TreeNodeDialog'\nimport { isNumberStr } from '@/utils/index'\nimport IconsDialog from './IconsDialog'\nimport {\n  inputComponents,\n  selectComponents,\n  layoutComponents\n} from '@/utils/generator/config'\n\nconst dateTimeFormat = {\n  date: 'yyyy-MM-dd',\n  week: 'yyyy 第 WW 周',\n  month: 'yyyy-MM',\n  year: 'yyyy',\n  datetime: 'yyyy-MM-dd HH:mm:ss',\n  daterange: 'yyyy-MM-dd',\n  monthrange: 'yyyy-MM',\n  datetimerange: 'yyyy-MM-dd HH:mm:ss'\n}\n\nexport default {\n  components: {\n    draggable,\n    TreeNodeDialog,\n    IconsDialog\n  },\n  props: ['showField', 'activeData', 'formConf'],\n  data() {\n    return {\n      currentTab: 'field',\n      currentNode: null,\n      dialogVisible: false,\n      iconsVisible: false,\n      currentIconModel: null,\n      dateTypeOptions: [\n        {\n          label: '日(date)',\n          value: 'date'\n        },\n        {\n          label: '周(week)',\n          value: 'week'\n        },\n        {\n          label: '月(month)',\n          value: 'month'\n        },\n        {\n          label: '年(year)',\n          value: 'year'\n        },\n        {\n          label: '日期时间(datetime)',\n          value: 'datetime'\n        }\n      ],\n      dateRangeTypeOptions: [\n        {\n          label: '日期范围(daterange)',\n          value: 'daterange'\n        },\n        {\n          label: '月范围(monthrange)',\n          value: 'monthrange'\n        },\n        {\n          label: '日期时间范围(datetimerange)',\n          value: 'datetimerange'\n        }\n      ],\n      colorFormatOptions: [\n        {\n          label: 'hex',\n          value: 'hex'\n        },\n        {\n          label: 'rgb',\n          value: 'rgb'\n        },\n        {\n          label: 'rgba',\n          value: 'rgba'\n        },\n        {\n          label: 'hsv',\n          value: 'hsv'\n        },\n        {\n          label: 'hsl',\n          value: 'hsl'\n        }\n      ],\n      justifyOptions: [\n        {\n          label: 'start',\n          value: 'start'\n        },\n        {\n          label: 'end',\n          value: 'end'\n        },\n        {\n          label: 'center',\n          value: 'center'\n        },\n        {\n          label: 'space-around',\n          value: 'space-around'\n        },\n        {\n          label: 'space-between',\n          value: 'space-between'\n        }\n      ],\n      layoutTreeProps: {\n        label(data, node) {\n          return data.componentName || `${data.label}: ${data.vModel}`\n        }\n      }\n    }\n  },\n  computed: {\n    documentLink() {\n      return (\n        this.activeData.document\n        || 'https://element.eleme.cn/#/zh-CN/component/installation'\n      )\n    },\n    dateOptions() {\n      if (\n        this.activeData.type !== undefined\n        && this.activeData.tag === 'el-date-picker'\n      ) {\n        if (this.activeData['start-placeholder'] === undefined) {\n          return this.dateTypeOptions\n        }\n        return this.dateRangeTypeOptions\n      }\n      return []\n    },\n    tagList() {\n      return [\n        {\n          label: '输入型组件',\n          options: inputComponents\n        },\n        {\n          label: '选择型组件',\n          options: selectComponents\n        }\n      ]\n    }\n  },\n  methods: {\n    addReg() {\n      this.activeData.regList.push({\n        pattern: '',\n        message: ''\n      })\n    },\n    addSelectItem() {\n      this.activeData.options.push({\n        label: '',\n        value: ''\n      })\n    },\n    addTreeItem() {\n      ++this.idGlobal\n      this.dialogVisible = true\n      this.currentNode = this.activeData.options\n    },\n    renderContent(h, { node, data, store }) {\n      return (\n        <div class=\"custom-tree-node\">\n          <span>{node.label}</span>\n          <span class=\"node-operation\">\n            <i on-click={() => this.append(data)}\n              class=\"el-icon-plus\"\n              title=\"添加\"\n            ></i>\n            <i on-click={() => this.remove(node, data)}\n              class=\"el-icon-delete\"\n              title=\"删除\"\n            ></i>\n          </span>\n        </div>\n      )\n    },\n    append(data) {\n      if (!data.children) {\n        this.$set(data, 'children', [])\n      }\n      this.dialogVisible = true\n      this.currentNode = data.children\n    },\n    remove(node, data) {\n      const { parent } = node\n      const children = parent.data.children || parent.data\n      const index = children.findIndex(d => d.id === data.id)\n      children.splice(index, 1)\n    },\n    addNode(data) {\n      this.currentNode.push(data)\n    },\n    setOptionValue(item, val) {\n      item.value = isNumberStr(val) ? +val : val\n    },\n    setDefaultValue(val) {\n      if (Array.isArray(val)) {\n        return val.join(',')\n      }\n      if (['string', 'number'].indexOf(val) > -1) {\n        return val\n      }\n      if (typeof val === 'boolean') {\n        return `${val}`\n      }\n      return val\n    },\n    onDefaultValueInput(str) {\n      if (isArray(this.activeData.defaultValue)) {\n        // 数组\n        this.$set(\n          this.activeData,\n          'defaultValue',\n          str.split(',').map(val => (isNumberStr(val) ? +val : val))\n        )\n      } else if (['true', 'false'].indexOf(str) > -1) {\n        // 布尔\n        this.$set(this.activeData, 'defaultValue', JSON.parse(str))\n      } else {\n        // 字符串和数字\n        this.$set(\n          this.activeData,\n          'defaultValue',\n          isNumberStr(str) ? +str : str\n        )\n      }\n    },\n    onSwitchValueInput(val, name) {\n      if (['true', 'false'].indexOf(val) > -1) {\n        this.$set(this.activeData, name, JSON.parse(val))\n      } else {\n        this.$set(this.activeData, name, isNumberStr(val) ? +val : val)\n      }\n    },\n    setTimeValue(val, type) {\n      const valueFormat = type === 'week' ? dateTimeFormat.date : val\n      this.$set(this.activeData, 'defaultValue', null)\n      this.$set(this.activeData, 'value-format', valueFormat)\n      this.$set(this.activeData, 'format', val)\n    },\n    spanChange(val) {\n      this.formConf.span = val\n    },\n    multipleChange(val) {\n      this.$set(this.activeData, 'defaultValue', val ? [] : '')\n    },\n    dateTypeChange(val) {\n      this.setTimeValue(dateTimeFormat[val], val)\n    },\n    rangeChange(val) {\n      this.$set(\n        this.activeData,\n        'defaultValue',\n        val ? [this.activeData.min, this.activeData.max] : this.activeData.min\n      )\n    },\n    rateTextChange(val) {\n      if (val) this.activeData['show-score'] = false\n    },\n    rateScoreChange(val) {\n      if (val) this.activeData['show-text'] = false\n    },\n    colorFormatChange(val) {\n      this.activeData.defaultValue = null\n      this.activeData['show-alpha'] = val.indexOf('a') > -1\n      this.activeData.renderKey = +new Date() // 更新renderKey,重新渲染该组件\n    },\n    openIconsDialog(model) {\n      this.iconsVisible = true\n      this.currentIconModel = model\n    },\n    setIcon(val) {\n      this.activeData[this.currentIconModel] = val\n    },\n    tagChange(tagIcon) {\n      let target = inputComponents.find(item => item.tagIcon === tagIcon)\n      if (!target) target = selectComponents.find(item => item.tagIcon === tagIcon)\n      this.$emit('tag-change', target)\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.right-board {\n  width: 350px;\n  position: absolute;\n  right: 0;\n  top: 0;\n  padding-top: 3px;\n  .field-box {\n    position: relative;\n    height: calc(100vh - 42px);\n    box-sizing: border-box;\n    overflow: hidden;\n  }\n  .el-scrollbar {\n    height: 100%;\n  }\n}\n.select-item {\n  display: flex;\n  border: 1px dashed #fff;\n  box-sizing: border-box;\n  & .close-btn {\n    cursor: pointer;\n    color: #f56c6c;\n  }\n  & .el-input + .el-input {\n    margin-left: 4px;\n  }\n}\n.select-item + .select-item {\n  margin-top: 4px;\n}\n.select-item.sortable-chosen {\n  border: 1px dashed #409eff;\n}\n.select-line-icon {\n  line-height: 32px;\n  font-size: 22px;\n  padding: 0 4px;\n  color: #777;\n}\n.option-drag {\n  cursor: move;\n}\n.time-range {\n  .el-date-editor {\n    width: 227px;\n  }\n  ::v-deep .el-icon-time {\n    display: none;\n  }\n}\n.document-link {\n  position: absolute;\n  display: block;\n  width: 26px;\n  height: 26px;\n  top: 0;\n  left: 0;\n  cursor: pointer;\n  background: #409eff;\n  z-index: 1;\n  border-radius: 0 0 6px 0;\n  text-align: center;\n  line-height: 26px;\n  color: #fff;\n  font-size: 18px;\n}\n.node-label{\n  font-size: 14px;\n}\n.node-icon{\n  color: #bebfc3;\n}\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/views/tool/build/TreeNodeDialog.vue",
    "content": "<template>\n  <div>\n    <el-dialog\n      v-bind=\"$attrs\"\n      :close-on-click-modal=\"false\"\n      :modal-append-to-body=\"false\"\n      v-on=\"$listeners\"\n      @open=\"onOpen\"\n      @close=\"onClose\"\n    >\n      <el-row :gutter=\"0\">\n        <el-form\n          ref=\"elForm\"\n          :model=\"formData\"\n          :rules=\"rules\"\n          size=\"small\"\n          label-width=\"100px\"\n        >\n          <el-col :span=\"24\">\n            <el-form-item\n              label=\"选项名\"\n              prop=\"label\"\n            >\n              <el-input\n                v-model=\"formData.label\"\n                placeholder=\"请输入选项名\"\n                clearable\n              />\n            </el-form-item>\n          </el-col>\n          <el-col :span=\"24\">\n            <el-form-item\n              label=\"选项值\"\n              prop=\"value\"\n            >\n              <el-input\n                v-model=\"formData.value\"\n                placeholder=\"请输入选项值\"\n                clearable\n              >\n                <el-select\n                  slot=\"append\"\n                  v-model=\"dataType\"\n                  :style=\"{width: '100px'}\"\n                >\n                  <el-option\n                    v-for=\"(item, index) in dataTypeOptions\"\n                    :key=\"index\"\n                    :label=\"item.label\"\n                    :value=\"item.value\"\n                    :disabled=\"item.disabled\"\n                  />\n                </el-select>\n              </el-input>\n            </el-form-item>\n          </el-col>\n        </el-form>\n      </el-row>\n      <div slot=\"footer\">\n        <el-button\n          type=\"primary\"\n          @click=\"handleConfirm\"\n        >\n          确定\n        </el-button>\n        <el-button @click=\"close\">\n          取消\n        </el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n<script>\nimport { isNumberStr } from '@/utils/index'\n\nexport default {\n  components: {},\n  inheritAttrs: false,\n  props: [],\n  data() {\n    return {\n      id: 100,\n      formData: {\n        label: undefined,\n        value: undefined\n      },\n      rules: {\n        label: [\n          {\n            required: true,\n            message: '请输入选项名',\n            trigger: 'blur'\n          }\n        ],\n        value: [\n          {\n            required: true,\n            message: '请输入选项值',\n            trigger: 'blur'\n          }\n        ]\n      },\n      dataType: 'string',\n      dataTypeOptions: [\n        {\n          label: '字符串',\n          value: 'string'\n        },\n        {\n          label: '数字',\n          value: 'number'\n        }\n      ]\n    }\n  },\n  computed: {},\n  watch: {\n    // eslint-disable-next-line func-names\n    'formData.value': function (val) {\n      this.dataType = isNumberStr(val) ? 'number' : 'string'\n    }\n  },\n  created() {},\n  mounted() {},\n  methods: {\n    onOpen() {\n      this.formData = {\n        label: undefined,\n        value: undefined\n      }\n    },\n    onClose() {},\n    close() {\n      this.$emit('update:visible', false)\n    },\n    handleConfirm() {\n      this.$refs.elForm.validate(valid => {\n        if (!valid) return\n        if (this.dataType === 'number') {\n          this.formData.value = parseFloat(this.formData.value)\n        }\n        this.formData.id = this.id++\n        this.$emit('commit', this.formData)\n        this.close()\n      })\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/tool/build/index.vue",
    "content": "<template>\n  <div class=\"container\">\n    <div class=\"left-board\">\n      <div class=\"logo-wrapper\">\n        <div class=\"logo\">\n          <img :src=\"logo\" alt=\"logo\"> Form Generator\n        </div>\n      </div>\n      <el-scrollbar class=\"left-scrollbar\">\n        <div class=\"components-list\">\n          <div class=\"components-title\">\n            <svg-icon icon-class=\"component\" />输入型组件\n          </div>\n          <draggable\n            class=\"components-draggable\"\n            :list=\"inputComponents\"\n            :group=\"{ name: 'componentsGroup', pull: 'clone', put: false }\"\n            :clone=\"cloneComponent\"\n            draggable=\".components-item\"\n            :sort=\"false\"\n            @end=\"onEnd\"\n          >\n            <div\n              v-for=\"(element, index) in inputComponents\" :key=\"index\" class=\"components-item\"\n              @click=\"addComponent(element)\"\n            >\n              <div class=\"components-body\">\n                <svg-icon :icon-class=\"element.tagIcon\" />\n                {{ element.label }}\n              </div>\n            </div>\n          </draggable>\n          <div class=\"components-title\">\n            <svg-icon icon-class=\"component\" />选择型组件\n          </div>\n          <draggable\n            class=\"components-draggable\"\n            :list=\"selectComponents\"\n            :group=\"{ name: 'componentsGroup', pull: 'clone', put: false }\"\n            :clone=\"cloneComponent\"\n            draggable=\".components-item\"\n            :sort=\"false\"\n            @end=\"onEnd\"\n          >\n            <div\n              v-for=\"(element, index) in selectComponents\"\n              :key=\"index\"\n              class=\"components-item\"\n              @click=\"addComponent(element)\"\n            >\n              <div class=\"components-body\">\n                <svg-icon :icon-class=\"element.tagIcon\" />\n                {{ element.label }}\n              </div>\n            </div>\n          </draggable>\n          <div class=\"components-title\">\n            <svg-icon icon-class=\"component\" /> 布局型组件\n          </div>\n          <draggable\n            class=\"components-draggable\" :list=\"layoutComponents\"\n            :group=\"{ name: 'componentsGroup', pull: 'clone', put: false }\" :clone=\"cloneComponent\"\n            draggable=\".components-item\" :sort=\"false\" @end=\"onEnd\"\n          >\n            <div\n              v-for=\"(element, index) in layoutComponents\" :key=\"index\" class=\"components-item\"\n              @click=\"addComponent(element)\"\n            >\n              <div class=\"components-body\">\n                <svg-icon :icon-class=\"element.tagIcon\" />\n                {{ element.label }}\n              </div>\n            </div>\n          </draggable>\n        </div>\n      </el-scrollbar>\n    </div>\n\n    <div class=\"center-board\">\n      <div class=\"action-bar\">\n        <el-button icon=\"el-icon-download\" type=\"text\" @click=\"download\">\n          导出vue文件\n        </el-button>\n        <el-button class=\"copy-btn-main\" icon=\"el-icon-document-copy\" type=\"text\" @click=\"copy\">\n          复制代码\n        </el-button>\n        <el-button class=\"delete-btn\" icon=\"el-icon-delete\" type=\"text\" @click=\"empty\">\n          清空\n        </el-button>\n      </div>\n      <el-scrollbar class=\"center-scrollbar\">\n        <el-row class=\"center-board-row\" :gutter=\"formConf.gutter\">\n          <el-form\n            :size=\"formConf.size\"\n            :label-position=\"formConf.labelPosition\"\n            :disabled=\"formConf.disabled\"\n            :label-width=\"formConf.labelWidth + 'px'\"\n          >\n            <draggable class=\"drawing-board\" :list=\"drawingList\" :animation=\"340\" group=\"componentsGroup\">\n              <draggable-item\n                v-for=\"(element, index) in drawingList\"\n                :key=\"element.renderKey\"\n                :drawing-list=\"drawingList\"\n                :element=\"element\"\n                :index=\"index\"\n                :active-id=\"activeId\"\n                :form-conf=\"formConf\"\n                @activeItem=\"activeFormItem\"\n                @copyItem=\"drawingItemCopy\"\n                @deleteItem=\"drawingItemDelete\"\n              />\n            </draggable>\n            <div v-show=\"!drawingList.length\" class=\"empty-info\">\n              从左侧拖入或点选组件进行表单设计\n            </div>\n          </el-form>\n        </el-row>\n      </el-scrollbar>\n    </div>\n\n    <right-panel\n      :active-data=\"activeData\"\n      :form-conf=\"formConf\"\n      :show-field=\"!!drawingList.length\"\n      @tag-change=\"tagChange\"\n    />\n\n    <code-type-dialog\n      :visible.sync=\"dialogVisible\"\n      title=\"选择生成类型\"\n      :show-file-name=\"showFileName\"\n      @confirm=\"generate\"\n    />\n    <input id=\"copyNode\" type=\"hidden\">\n  </div>\n</template>\n\n<script>\nimport draggable from 'vuedraggable'\nimport beautifier from 'js-beautify'\nimport ClipboardJS from 'clipboard'\nimport render from '@/utils/generator/render'\nimport RightPanel from './RightPanel'\nimport { inputComponents, selectComponents, layoutComponents, formConf } from '@/utils/generator/config'\nimport { beautifierConf, titleCase } from '@/utils/index'\nimport { makeUpHtml, vueTemplate, vueScript, cssStyle } from '@/utils/generator/html'\nimport { makeUpJs } from '@/utils/generator/js'\nimport { makeUpCss } from '@/utils/generator/css'\nimport drawingDefault from '@/utils/generator/drawingDefault'\nimport logo from '@/assets/logo/logo.png'\nimport CodeTypeDialog from './CodeTypeDialog'\nimport DraggableItem from './DraggableItem'\n\nlet oldActiveId\nlet tempActiveData\n\nexport default {\n  components: {\n    draggable,\n    render,\n    RightPanel,\n    CodeTypeDialog,\n    DraggableItem\n  },\n  data() {\n    return {\n      logo,\n      idGlobal: 100,\n      formConf,\n      inputComponents,\n      selectComponents,\n      layoutComponents,\n      labelWidth: 100,\n      drawingList: drawingDefault,\n      drawingData: {},\n      activeId: drawingDefault[0].formId,\n      drawerVisible: false,\n      formData: {},\n      dialogVisible: false,\n      generateConf: null,\n      showFileName: false,\n      activeData: drawingDefault[0]\n    }\n  },\n  created() {\n    // 防止 firefox 下 拖拽 会新打卡一个选项卡\n    document.body.ondrop = event => {\n      event.preventDefault()\n      event.stopPropagation()\n    }\n  },\n  watch: {\n    // eslint-disable-next-line func-names\n    'activeData.label': function (val, oldVal) {\n      if (\n        this.activeData.placeholder === undefined\n        || !this.activeData.tag\n        || oldActiveId !== this.activeId\n      ) {\n        return\n      }\n      this.activeData.placeholder = this.activeData.placeholder.replace(oldVal, '') + val\n    },\n    activeId: {\n      handler(val) {\n        oldActiveId = val\n      },\n      immediate: true\n    }\n  },\n  mounted() {\n    const clipboard = new ClipboardJS('#copyNode', {\n      text: trigger => {\n        const codeStr = this.generateCode()\n        this.$notify({\n          title: '成功',\n          message: '代码已复制到剪切板，可粘贴。',\n          type: 'success'\n        })\n        return codeStr\n      }\n    })\n    clipboard.on('error', e => {\n      this.$message.error('代码复制失败')\n    })\n  },\n  methods: {\n    activeFormItem(element) {\n      this.activeData = element\n      this.activeId = element.formId\n    },\n    onEnd(obj, a) {\n      if (obj.from !== obj.to) {\n        this.activeData = tempActiveData\n        this.activeId = this.idGlobal\n      }\n    },\n    addComponent(item) {\n      const clone = this.cloneComponent(item)\n      this.drawingList.push(clone)\n      this.activeFormItem(clone)\n    },\n    cloneComponent(origin) {\n      const clone = JSON.parse(JSON.stringify(origin))\n      clone.formId = ++this.idGlobal\n      clone.span = formConf.span\n      clone.renderKey = +new Date() // 改变renderKey后可以实现强制更新组件\n      if (!clone.layout) clone.layout = 'colFormItem'\n      if (clone.layout === 'colFormItem') {\n        clone.vModel = `field${this.idGlobal}`\n        clone.placeholder !== undefined && (clone.placeholder += clone.label)\n        tempActiveData = clone\n      } else if (clone.layout === 'rowFormItem') {\n        delete clone.label\n        clone.componentName = `row${this.idGlobal}`\n        clone.gutter = this.formConf.gutter\n        tempActiveData = clone\n      }\n      return tempActiveData\n    },\n    AssembleFormData() {\n      this.formData = {\n        fields: JSON.parse(JSON.stringify(this.drawingList)),\n        ...this.formConf\n      }\n    },\n    generate(data) {\n      const func = this[`exec${titleCase(this.operationType)}`]\n      this.generateConf = data\n      func && func(data)\n    },\n    execRun(data) {\n      this.AssembleFormData()\n      this.drawerVisible = true\n    },\n    execDownload(data) {\n      const codeStr = this.generateCode()\n      const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })\n      this.$download.saveAs(blob, data.fileName)\n    },\n    execCopy(data) {\n      document.getElementById('copyNode').click()\n    },\n    empty() {\n      this.$confirm('确定要清空所有组件吗？', '提示', { type: 'warning' }).then(\n        () => {\n          this.drawingList = []\n        }\n      )\n    },\n    drawingItemCopy(item, parent) {\n      let clone = JSON.parse(JSON.stringify(item))\n      clone = this.createIdAndKey(clone)\n      parent.push(clone)\n      this.activeFormItem(clone)\n    },\n    createIdAndKey(item) {\n      item.formId = ++this.idGlobal\n      item.renderKey = +new Date()\n      if (item.layout === 'colFormItem') {\n        item.vModel = `field${this.idGlobal}`\n      } else if (item.layout === 'rowFormItem') {\n        item.componentName = `row${this.idGlobal}`\n      }\n      if (Array.isArray(item.children)) {\n        item.children = item.children.map(childItem => this.createIdAndKey(childItem))\n      }\n      return item\n    },\n    drawingItemDelete(index, parent) {\n      parent.splice(index, 1)\n      this.$nextTick(() => {\n        const len = this.drawingList.length\n        if (len) {\n          this.activeFormItem(this.drawingList[len - 1])\n        }\n      })\n    },\n    generateCode() {\n      const { type } = this.generateConf\n      this.AssembleFormData()\n      const script = vueScript(makeUpJs(this.formData, type))\n      const html = vueTemplate(makeUpHtml(this.formData, type))\n      const css = cssStyle(makeUpCss(this.formData))\n      return beautifier.html(html + script + css, beautifierConf.html)\n    },\n    download() {\n      this.dialogVisible = true\n      this.showFileName = true\n      this.operationType = 'download'\n    },\n    run() {\n      this.dialogVisible = true\n      this.showFileName = false\n      this.operationType = 'run'\n    },\n    copy() {\n      this.dialogVisible = true\n      this.showFileName = false\n      this.operationType = 'copy'\n    },\n    tagChange(newTag) {\n      newTag = this.cloneComponent(newTag)\n      newTag.vModel = this.activeData.vModel\n      newTag.formId = this.activeId\n      newTag.span = this.activeData.span\n      delete this.activeData.tag\n      delete this.activeData.tagIcon\n      delete this.activeData.document\n      Object.keys(newTag).forEach(key => {\n        if (this.activeData[key] !== undefined\n          && typeof this.activeData[key] === typeof newTag[key]) {\n          newTag[key] = this.activeData[key]\n        }\n      })\n      this.activeData = newTag\n      this.updateDrawingList(newTag, this.drawingList)\n    },\n    updateDrawingList(newTag, list) {\n      const index = list.findIndex(item => item.formId === this.activeId)\n      if (index > -1) {\n        list.splice(index, 1, newTag)\n      } else {\n        list.forEach(item => {\n          if (Array.isArray(item.children)) this.updateDrawingList(newTag, item.children)\n        })\n      }\n    }\n  }\n}\n</script>\n\n<style lang='scss'>\n\n.editor-tabs{\n  background: #121315;\n  .el-tabs__header{\n    margin: 0;\n    border-bottom-color: #121315;\n    .el-tabs__nav{\n      border-color: #121315;\n    }\n  }\n  .el-tabs__item{\n    height: 32px;\n    line-height: 32px;\n    color: #888a8e;\n    border-left: 1px solid #121315 !important;\n    background: #363636;\n    margin-right: 5px;\n    user-select: none;\n  }\n  .el-tabs__item.is-active{\n    background: #1e1e1e;\n    border-bottom-color: #1e1e1e!important;\n    color: #fff;\n  }\n  .el-icon-edit{\n    color: #f1fa8c;\n  }\n  .el-icon-document{\n    color: #a95812;\n  }\n}\n\n// home\n.right-scrollbar {\n  .el-scrollbar__view {\n    padding: 12px 18px 15px 15px;\n  }\n}\n.left-scrollbar .el-scrollbar__wrap {\n  box-sizing: border-box;\n  overflow-x: hidden !important;\n  margin-bottom: 0 !important;\n}\n.center-tabs{\n  .el-tabs__header{\n    margin-bottom: 0!important;\n  }\n  .el-tabs__item{\n    width: 50%;\n    text-align: center;\n  }\n  .el-tabs__nav{\n    width: 100%;\n  }\n}\n.reg-item{\n  padding: 12px 6px;\n  background: #f8f8f8;\n  position: relative;\n  border-radius: 4px;\n  .close-btn{\n    position: absolute;\n    right: -6px;\n    top: -6px;\n    display: block;\n    width: 16px;\n    height: 16px;\n    line-height: 16px;\n    background: rgba(0, 0, 0, 0.2);\n    border-radius: 50%;\n    color: #fff;\n    text-align: center;\n    z-index: 1;\n    cursor: pointer;\n    font-size: 12px;\n    &:hover{\n      background: rgba(210, 23, 23, 0.5)\n    }\n  }\n  & + .reg-item{\n    margin-top: 18px;\n  }\n}\n.action-bar{\n  & .el-button+.el-button {\n    margin-left: 15px;\n  }\n  & i {\n    font-size: 20px;\n    vertical-align: middle;\n    position: relative;\n    top: -1px;\n  }\n}\n\n.custom-tree-node{\n  width: 100%;\n  font-size: 14px;\n  .node-operation{\n    float: right;\n  }\n  i[class*=\"el-icon\"] + i[class*=\"el-icon\"]{\n    margin-left: 6px;\n  }\n  .el-icon-plus{\n    color: #409EFF;\n  }\n  .el-icon-delete{\n    color: #157a0c;\n  }\n}\n\n.left-scrollbar .el-scrollbar__view{\n  overflow-x: hidden;\n}\n\n.el-rate{\n  display: inline-block;\n  vertical-align: text-top;\n}\n.el-upload__tip{\n  line-height: 1.2;\n}\n\n$selectedColor: #f6f7ff;\n$lighterBlue: #409EFF;\n\n.container {\n  position: relative;\n  width: 100%;\n  height: 100%;\n}\n\n.components-list {\n  padding: 8px;\n  box-sizing: border-box;\n  height: 100%;\n  .components-item {\n    display: inline-block;\n    width: 48%;\n    margin: 1%;\n    transition: transform 0ms !important;\n  }\n}\n.components-draggable{\n  padding-bottom: 20px;\n}\n.components-title{\n  font-size: 14px;\n  color: #222;\n  margin: 6px 2px;\n  .svg-icon{\n    color: #666;\n    font-size: 18px;\n  }\n}\n\n.components-body {\n  padding: 8px 10px;\n  background: $selectedColor;\n  font-size: 12px;\n  cursor: move;\n  border: 1px dashed $selectedColor;\n  border-radius: 3px;\n  .svg-icon{\n    color: #777;\n    font-size: 15px;\n  }\n  &:hover {\n    border: 1px dashed #787be8;\n    color: #787be8;\n    .svg-icon {\n      color: #787be8;\n    }\n  }\n}\n\n.left-board {\n  width: 260px;\n  position: absolute;\n  left: 0;\n  top: 0;\n  height: 100vh;\n}\n.left-scrollbar{\n  height: calc(100vh - 42px);\n  overflow: hidden;\n}\n.center-scrollbar {\n  height: calc(100vh - 42px);\n  overflow: hidden;\n  border-left: 1px solid #f1e8e8;\n  border-right: 1px solid #f1e8e8;\n  box-sizing: border-box;\n}\n.center-board {\n  height: 100vh;\n  width: auto;\n  margin: 0 350px 0 260px;\n  box-sizing: border-box;\n}\n.empty-info{\n  position: absolute;\n  top: 46%;\n  left: 0;\n  right: 0;\n  text-align: center;\n  font-size: 18px;\n  color: #ccb1ea;\n  letter-spacing: 4px;\n}\n.action-bar{\n  position: relative;\n  height: 42px;\n  text-align: right;\n  padding: 0 15px;\n  box-sizing: border-box;;\n  border: 1px solid #f1e8e8;\n  border-top: none;\n  border-left: none;\n  .delete-btn{\n    color: #F56C6C;\n  }\n}\n.logo-wrapper{\n  position: relative;\n  height: 42px;\n  background: #fff;\n  border-bottom: 1px solid #f1e8e8;\n  box-sizing: border-box;\n}\n.logo{\n  position: absolute;\n  left: 12px;\n  top: 6px;\n  line-height: 30px;\n  color: #00afff;\n  font-weight: 600;\n  font-size: 17px;\n  white-space: nowrap;\n  > img{\n    width: 30px;\n    height: 30px;\n    vertical-align: top;\n  }\n  .github{\n    display: inline-block;\n    vertical-align: sub;\n    margin-left: 15px;\n    > img{\n      height: 22px;\n    }\n  }\n}\n\n.center-board-row {\n  padding: 12px 12px 15px 12px;\n  box-sizing: border-box;\n  & > .el-form {\n    // 69 = 12+15+42\n    height: calc(100vh - 69px);\n  }\n}\n.drawing-board {\n  height: 100%;\n  position: relative;\n  .components-body {\n    padding: 0;\n    margin: 0;\n    font-size: 0;\n  }\n  .sortable-ghost {\n    position: relative;\n    display: block;\n    overflow: hidden;\n    &::before {\n      content: \" \";\n      position: absolute;\n      left: 0;\n      right: 0;\n      top: 0;\n      height: 3px;\n      background: rgb(89, 89, 223);\n      z-index: 2;\n    }\n  }\n  .components-item.sortable-ghost {\n    width: 100%;\n    height: 60px;\n    background-color: $selectedColor;\n  }\n  .active-from-item {\n    & > .el-form-item{\n      background: $selectedColor;\n      border-radius: 6px;\n    }\n    & > .drawing-item-copy, & > .drawing-item-delete{\n      display: initial;\n    }\n    & > .component-name{\n      color: $lighterBlue;\n    }\n  }\n  .el-form-item{\n    margin-bottom: 15px;\n  }\n}\n.drawing-item{\n  position: relative;\n  cursor: move;\n  &.unfocus-bordered:not(.activeFromItem) > div:first-child  {\n    border: 1px dashed #ccc;\n  }\n  .el-form-item{\n    padding: 12px 10px;\n  }\n}\n.drawing-row-item{\n  position: relative;\n  cursor: move;\n  box-sizing: border-box;\n  border: 1px dashed #ccc;\n  border-radius: 3px;\n  padding: 0 2px;\n  margin-bottom: 15px;\n  .drawing-row-item {\n    margin-bottom: 2px;\n  }\n  .el-col{\n    margin-top: 22px;\n  }\n  .el-form-item{\n    margin-bottom: 0;\n  }\n  .drag-wrapper{\n    min-height: 80px;\n  }\n  &.active-from-item{\n    border: 1px dashed $lighterBlue;\n  }\n  .component-name{\n    position: absolute;\n    top: 0;\n    left: 0;\n    font-size: 12px;\n    color: #bbb;\n    display: inline-block;\n    padding: 0 6px;\n  }\n}\n.drawing-item, .drawing-row-item{\n  &:hover {\n    & > .el-form-item{\n      background: $selectedColor;\n      border-radius: 6px;\n    }\n    & > .drawing-item-copy, & > .drawing-item-delete{\n      display: initial;\n    }\n  }\n  & > .drawing-item-copy, & > .drawing-item-delete{\n    display: none;\n    position: absolute;\n    top: -10px;\n    width: 22px;\n    height: 22px;\n    line-height: 22px;\n    text-align: center;\n    border-radius: 50%;\n    font-size: 12px;\n    border: 1px solid;\n    cursor: pointer;\n    z-index: 1;\n  }\n  & > .drawing-item-copy{\n    right: 56px;\n    border-color: $lighterBlue;\n    color: $lighterBlue;\n    background: #fff;\n    &:hover{\n      background: $lighterBlue;\n      color: #fff;\n    }\n  }\n  & > .drawing-item-delete{\n    right: 24px;\n    border-color: #F56C6C;\n    color: #F56C6C;\n    background: #fff;\n    &:hover{\n      background: #F56C6C;\n      color: #fff;\n    }\n  }\n}\n\n</style>\n"
  },
  {
    "path": "ruoyi-ui/src/views/tool/gen/basicInfoForm.vue",
    "content": "<template>\n  <el-form ref=\"basicInfoForm\" :model=\"info\" :rules=\"rules\" label-width=\"150px\">\n    <el-row>\n      <el-col :span=\"12\">\n        <el-form-item label=\"表名称\" prop=\"tableName\">\n          <el-input placeholder=\"请输入仓库名称\" v-model=\"info.tableName\" />\n        </el-form-item>\n      </el-col>\n      <el-col :span=\"12\">\n        <el-form-item label=\"表描述\" prop=\"tableComment\">\n          <el-input placeholder=\"请输入\" v-model=\"info.tableComment\" />\n        </el-form-item>\n      </el-col>\n      <el-col :span=\"12\">\n        <el-form-item label=\"实体类名称\" prop=\"className\">\n          <el-input placeholder=\"请输入\" v-model=\"info.className\" />\n        </el-form-item>\n      </el-col>\n      <el-col :span=\"12\">\n        <el-form-item label=\"作者\" prop=\"functionAuthor\">\n          <el-input placeholder=\"请输入\" v-model=\"info.functionAuthor\" />\n        </el-form-item>\n      </el-col>\n      <el-col :span=\"24\">\n        <el-form-item label=\"备注\" prop=\"remark\">\n          <el-input type=\"textarea\" :rows=\"3\" v-model=\"info.remark\"></el-input>\n        </el-form-item>\n      </el-col>\n    </el-row>\n  </el-form>\n</template>\n\n<script>\nexport default {\n  props: {\n    info: {\n      type: Object,\n      default: null\n    }\n  },\n  data() {\n    return {\n      rules: {\n        tableName: [\n          { required: true, message: \"请输入表名称\", trigger: \"blur\" }\n        ],\n        tableComment: [\n          { required: true, message: \"请输入表描述\", trigger: \"blur\" }\n        ],\n        className: [\n          { required: true, message: \"请输入实体类名称\", trigger: \"blur\" }\n        ],\n        functionAuthor: [\n          { required: true, message: \"请输入作者\", trigger: \"blur\" }\n        ]\n      }\n    };\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/tool/gen/editTable.vue",
    "content": "<template>\n  <el-card>\n    <el-tabs v-model=\"activeName\">\n      <el-tab-pane label=\"基本信息\" name=\"basic\">\n        <basic-info-form ref=\"basicInfo\" :info=\"info\" />\n      </el-tab-pane>\n      <el-tab-pane label=\"字段信息\" name=\"columnInfo\">\n        <el-table ref=\"dragTable\" :data=\"columns\" row-key=\"columnId\" :max-height=\"tableHeight\">\n          <el-table-column label=\"序号\" type=\"index\" min-width=\"5%\" class-name=\"allowDrag\" />\n          <el-table-column\n            label=\"字段列名\"\n            prop=\"columnName\"\n            min-width=\"10%\"\n            :show-overflow-tooltip=\"true\"\n          />\n          <el-table-column label=\"字段描述\" min-width=\"10%\">\n            <template slot-scope=\"scope\">\n              <el-input v-model=\"scope.row.columnComment\"></el-input>\n            </template>\n          </el-table-column>\n          <el-table-column\n            label=\"物理类型\"\n            prop=\"columnType\"\n            min-width=\"10%\"\n            :show-overflow-tooltip=\"true\"\n          />\n          <el-table-column label=\"Java类型\" min-width=\"11%\">\n            <template slot-scope=\"scope\">\n              <el-select v-model=\"scope.row.javaType\">\n                <el-option label=\"Long\" value=\"Long\" />\n                <el-option label=\"String\" value=\"String\" />\n                <el-option label=\"Integer\" value=\"Integer\" />\n                <el-option label=\"Double\" value=\"Double\" />\n                <el-option label=\"BigDecimal\" value=\"BigDecimal\" />\n                <el-option label=\"Date\" value=\"Date\" />\n                <el-option label=\"Boolean\" value=\"Boolean\" />\n              </el-select>\n            </template>\n          </el-table-column>\n          <el-table-column label=\"java属性\" min-width=\"10%\">\n            <template slot-scope=\"scope\">\n              <el-input v-model=\"scope.row.javaField\"></el-input>\n            </template>\n          </el-table-column>\n\n          <el-table-column label=\"插入\" min-width=\"5%\">\n            <template slot-scope=\"scope\">\n              <el-checkbox true-label=\"1\" false-label=\"0\" v-model=\"scope.row.isInsert\"></el-checkbox>\n            </template>\n          </el-table-column>\n          <el-table-column label=\"编辑\" min-width=\"5%\">\n            <template slot-scope=\"scope\">\n              <el-checkbox true-label=\"1\" false-label=\"0\" v-model=\"scope.row.isEdit\"></el-checkbox>\n            </template>\n          </el-table-column>\n          <el-table-column label=\"列表\" min-width=\"5%\">\n            <template slot-scope=\"scope\">\n              <el-checkbox true-label=\"1\" false-label=\"0\" v-model=\"scope.row.isList\"></el-checkbox>\n            </template>\n          </el-table-column>\n          <el-table-column label=\"查询\" min-width=\"5%\">\n            <template slot-scope=\"scope\">\n              <el-checkbox true-label=\"1\" false-label=\"0\" v-model=\"scope.row.isQuery\"></el-checkbox>\n            </template>\n          </el-table-column>\n          <el-table-column label=\"查询方式\" min-width=\"10%\">\n            <template slot-scope=\"scope\">\n              <el-select v-model=\"scope.row.queryType\">\n                <el-option label=\"=\" value=\"EQ\" />\n                <el-option label=\"!=\" value=\"NE\" />\n                <el-option label=\">\" value=\"GT\" />\n                <el-option label=\">=\" value=\"GE\" />\n                <el-option label=\"<\" value=\"LT\" />\n                <el-option label=\"<=\" value=\"LE\" />\n                <el-option label=\"LIKE\" value=\"LIKE\" />\n                <el-option label=\"BETWEEN\" value=\"BETWEEN\" />\n              </el-select>\n            </template>\n          </el-table-column>\n          <el-table-column label=\"必填\" min-width=\"5%\">\n            <template slot-scope=\"scope\">\n              <el-checkbox true-label=\"1\" false-label=\"0\" v-model=\"scope.row.isRequired\"></el-checkbox>\n            </template>\n          </el-table-column>\n          <el-table-column label=\"显示类型\" min-width=\"12%\">\n            <template slot-scope=\"scope\">\n              <el-select v-model=\"scope.row.htmlType\">\n                <el-option label=\"文本框\" value=\"input\" />\n                <el-option label=\"文本域\" value=\"textarea\" />\n                <el-option label=\"下拉框\" value=\"select\" />\n                <el-option label=\"单选框\" value=\"radio\" />\n                <el-option label=\"复选框\" value=\"checkbox\" />\n                <el-option label=\"日期控件\" value=\"datetime\" />\n                <el-option label=\"图片上传\" value=\"imageUpload\" />\n                <el-option label=\"文件上传\" value=\"fileUpload\" />\n                <el-option label=\"富文本控件\" value=\"editor\" />\n              </el-select>\n            </template>\n          </el-table-column>\n          <el-table-column label=\"字典类型\" min-width=\"12%\">\n            <template slot-scope=\"scope\">\n              <el-select v-model=\"scope.row.dictType\" clearable filterable placeholder=\"请选择\">\n                <el-option\n                  v-for=\"dict in dictOptions\"\n                  :key=\"dict.dictType\"\n                  :label=\"dict.dictName\"\n                  :value=\"dict.dictType\">\n                  <span style=\"float: left\">{{ dict.dictName }}</span>\n                  <span style=\"float: right; color: #8492a6; font-size: 13px\">{{ dict.dictType }}</span>\n              </el-option>\n              </el-select>\n            </template>\n          </el-table-column>\n        </el-table>\n      </el-tab-pane>\n      <el-tab-pane label=\"生成信息\" name=\"genInfo\">\n        <gen-info-form ref=\"genInfo\" :info=\"info\" :tables=\"tables\" :menus=\"menus\"/>\n      </el-tab-pane>\n    </el-tabs>\n    <el-form label-width=\"100px\">\n      <el-form-item style=\"text-align: center;margin-left:-100px;margin-top:10px;\">\n        <el-button type=\"primary\" @click=\"submitForm()\">提交</el-button>\n        <el-button @click=\"close()\">返回</el-button>\n      </el-form-item>\n    </el-form>\n  </el-card>\n</template>\n\n<script>\nimport { getGenTable, updateGenTable } from \"@/api/tool/gen\";\nimport { optionselect as getDictOptionselect } from \"@/api/system/dict/type\";\nimport { listMenu as getMenuTreeselect } from \"@/api/system/menu\";\nimport basicInfoForm from \"./basicInfoForm\";\nimport genInfoForm from \"./genInfoForm\";\nimport Sortable from 'sortablejs'\n\nexport default {\n  name: \"GenEdit\",\n  components: {\n    basicInfoForm,\n    genInfoForm\n  },\n  data() {\n    return {\n      // 选中选项卡的 name\n      activeName: \"columnInfo\",\n      // 表格的高度\n      tableHeight: document.documentElement.scrollHeight - 245 + \"px\",\n      // 表信息\n      tables: [],\n      // 表列信息\n      columns: [],\n      // 字典信息\n      dictOptions: [],\n      // 菜单信息\n      menus: [],\n      // 表详细信息\n      info: {}\n    };\n  },\n  created() {\n    const tableId = this.$route.params && this.$route.params.tableId;\n    if (tableId) {\n      // 获取表详细信息\n      getGenTable(tableId).then(res => {\n        this.columns = res.data.rows;\n        this.info = res.data.info;\n        this.tables = res.data.tables;\n      });\n      /** 查询字典下拉列表 */\n      getDictOptionselect().then(response => {\n        this.dictOptions = response.data;\n      });\n      /** 查询菜单下拉列表 */\n      getMenuTreeselect().then(response => {\n        this.menus = this.handleTree(response.data, \"menuId\");\n      });\n    }\n  },\n  methods: {\n    /** 提交按钮 */\n    submitForm() {\n      const basicForm = this.$refs.basicInfo.$refs.basicInfoForm;\n      const genForm = this.$refs.genInfo.$refs.genInfoForm;\n      Promise.all([basicForm, genForm].map(this.getFormPromise)).then(res => {\n        const validateResult = res.every(item => !!item);\n        if (validateResult) {\n          const genTable = Object.assign({}, basicForm.model, genForm.model);\n          genTable.columns = this.columns;\n          genTable.params = {\n            treeCode: genTable.treeCode,\n            treeName: genTable.treeName,\n            treeParentCode: genTable.treeParentCode,\n            parentMenuId: genTable.parentMenuId\n          };\n          updateGenTable(genTable).then(res => {\n            this.$modal.msgSuccess(res.msg);\n            if (res.code === 200) {\n              this.close();\n            }\n          });\n        } else {\n          this.$modal.msgError(\"表单校验未通过，请重新检查提交内容\");\n        }\n      });\n    },\n    getFormPromise(form) {\n      return new Promise(resolve => {\n        form.validate(res => {\n          resolve(res);\n        });\n      });\n    },\n    /** 关闭按钮 */\n    close() {\n      const obj = { path: \"/tool/gen\", query: { t: Date.now(), pageNum: this.$route.query.pageNum } };\n      this.$tab.closeOpenPage(obj);\n    }\n  },\n  mounted() {\n    const el = this.$refs.dragTable.$el.querySelectorAll(\".el-table__body-wrapper > table > tbody\")[0];\n    const sortable = Sortable.create(el, {\n      handle: \".allowDrag\",\n      onEnd: evt => {\n        const targetRow = this.columns.splice(evt.oldIndex, 1)[0];\n        this.columns.splice(evt.newIndex, 0, targetRow);\n        for (let index in this.columns) {\n          this.columns[index].sort = parseInt(index) + 1;\n        }\n      }\n    });\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/tool/gen/genInfoForm.vue",
    "content": "<template>\n  <el-form ref=\"genInfoForm\" :model=\"info\" :rules=\"rules\" label-width=\"150px\">\n    <el-row>\n      <el-col :span=\"12\">\n        <el-form-item prop=\"tplCategory\">\n          <span slot=\"label\">生成模板</span>\n          <el-select v-model=\"info.tplCategory\" @change=\"tplSelectChange\">\n            <el-option label=\"单表（增删改查）\" value=\"crud\" />\n            <el-option label=\"树表（增删改查）\" value=\"tree\" />\n<!--            <el-option label=\"主子表（增删改查）\" value=\"sub\" />-->\n          </el-select>\n        </el-form-item>\n      </el-col>\n      <el-col :span=\"12\">\n        <el-form-item prop=\"packageName\">\n          <span slot=\"label\">\n            生成包路径\n            <el-tooltip content=\"生成在哪个java包下，例如 top.flya.system\" placement=\"top\">\n              <i class=\"el-icon-question\"></i>\n            </el-tooltip>\n          </span>\n          <el-input v-model=\"info.packageName\" />\n        </el-form-item>\n      </el-col>\n\n      <el-col :span=\"12\">\n        <el-form-item prop=\"moduleName\">\n          <span slot=\"label\">\n            生成模块名\n            <el-tooltip content=\"可理解为子系统名，例如 system\" placement=\"top\">\n              <i class=\"el-icon-question\"></i>\n            </el-tooltip>\n          </span>\n          <el-input v-model=\"info.moduleName\" />\n        </el-form-item>\n      </el-col>\n\n      <el-col :span=\"12\">\n        <el-form-item prop=\"businessName\">\n          <span slot=\"label\">\n            生成业务名\n            <el-tooltip content=\"可理解为功能英文名，例如 user\" placement=\"top\">\n              <i class=\"el-icon-question\"></i>\n            </el-tooltip>\n          </span>\n          <el-input v-model=\"info.businessName\" />\n        </el-form-item>\n      </el-col>\n\n      <el-col :span=\"12\">\n        <el-form-item prop=\"functionName\">\n          <span slot=\"label\">\n            生成功能名\n            <el-tooltip content=\"用作类描述，例如 用户\" placement=\"top\">\n              <i class=\"el-icon-question\"></i>\n            </el-tooltip>\n          </span>\n          <el-input v-model=\"info.functionName\" />\n        </el-form-item>\n      </el-col>\n\n      <el-col :span=\"12\">\n        <el-form-item>\n          <span slot=\"label\">\n            上级菜单\n            <el-tooltip content=\"分配到指定菜单下，例如 系统管理\" placement=\"top\">\n              <i class=\"el-icon-question\"></i>\n            </el-tooltip>\n          </span>\n          <treeselect\n            :append-to-body=\"true\"\n            v-model=\"info.parentMenuId\"\n            :options=\"menus\"\n            :normalizer=\"normalizer\"\n            :show-count=\"true\"\n            placeholder=\"请选择系统菜单\"\n          />\n        </el-form-item>\n      </el-col>\n\n      <el-col :span=\"12\">\n        <el-form-item prop=\"genType\">\n          <span slot=\"label\">\n            生成代码方式\n            <el-tooltip content=\"默认为zip压缩包下载，也可以自定义生成路径\" placement=\"top\">\n              <i class=\"el-icon-question\"></i>\n            </el-tooltip>\n          </span>\n          <el-radio v-model=\"info.genType\" label=\"0\">zip压缩包</el-radio>\n          <el-radio v-model=\"info.genType\" label=\"1\">自定义路径</el-radio>\n        </el-form-item>\n      </el-col>\n\n      <el-col :span=\"24\" v-if=\"info.genType == '1'\">\n        <el-form-item prop=\"genPath\">\n          <span slot=\"label\">\n            自定义路径\n            <el-tooltip content=\"填写磁盘绝对路径，若不填写，则生成到当前Web项目下\" placement=\"top\">\n              <i class=\"el-icon-question\"></i>\n            </el-tooltip>\n          </span>\n          <el-input v-model=\"info.genPath\">\n            <el-dropdown slot=\"append\">\n              <el-button type=\"primary\">\n                最近路径快速选择\n                <i class=\"el-icon-arrow-down el-icon--right\"></i>\n              </el-button>\n              <el-dropdown-menu slot=\"dropdown\">\n                <el-dropdown-item @click.native=\"info.genPath = '/'\">恢复默认的生成基础路径</el-dropdown-item>\n              </el-dropdown-menu>\n            </el-dropdown>\n          </el-input>\n        </el-form-item>\n      </el-col>\n    </el-row>\n\n    <el-row v-show=\"info.tplCategory == 'tree'\">\n      <h4 class=\"form-header\">其他信息</h4>\n      <el-col :span=\"12\">\n        <el-form-item>\n          <span slot=\"label\">\n            树编码字段\n            <el-tooltip content=\"树显示的编码字段名， 如：dept_id\" placement=\"top\">\n              <i class=\"el-icon-question\"></i>\n            </el-tooltip>\n          </span>\n          <el-select v-model=\"info.treeCode\" placeholder=\"请选择\">\n            <el-option\n              v-for=\"(column, index) in info.columns\"\n              :key=\"index\"\n              :label=\"column.columnName + '：' + column.columnComment\"\n              :value=\"column.columnName\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n      </el-col>\n      <el-col :span=\"12\">\n        <el-form-item>\n          <span slot=\"label\">\n            树父编码字段\n            <el-tooltip content=\"树显示的父编码字段名， 如：parent_Id\" placement=\"top\">\n              <i class=\"el-icon-question\"></i>\n            </el-tooltip>\n          </span>\n          <el-select v-model=\"info.treeParentCode\" placeholder=\"请选择\">\n            <el-option\n              v-for=\"(column, index) in info.columns\"\n              :key=\"index\"\n              :label=\"column.columnName + '：' + column.columnComment\"\n              :value=\"column.columnName\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n      </el-col>\n      <el-col :span=\"12\">\n        <el-form-item>\n          <span slot=\"label\">\n            树名称字段\n            <el-tooltip content=\"树节点的显示名称字段名， 如：dept_name\" placement=\"top\">\n              <i class=\"el-icon-question\"></i>\n            </el-tooltip>\n          </span>\n          <el-select v-model=\"info.treeName\" placeholder=\"请选择\">\n            <el-option\n              v-for=\"(column, index) in info.columns\"\n              :key=\"index\"\n              :label=\"column.columnName + '：' + column.columnComment\"\n              :value=\"column.columnName\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n      </el-col>\n    </el-row>\n    <el-row v-show=\"info.tplCategory == 'sub'\">\n      <h4 class=\"form-header\">关联信息</h4>\n      <el-col :span=\"12\">\n        <el-form-item>\n          <span slot=\"label\">\n            关联子表的表名\n            <el-tooltip content=\"关联子表的表名， 如：sys_user\" placement=\"top\">\n              <i class=\"el-icon-question\"></i>\n            </el-tooltip>\n          </span>\n          <el-select v-model=\"info.subTableName\" placeholder=\"请选择\" @change=\"subSelectChange\">\n            <el-option\n              v-for=\"(table, index) in tables\"\n              :key=\"index\"\n              :label=\"table.tableName + '：' + table.tableComment\"\n              :value=\"table.tableName\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n      </el-col>\n      <el-col :span=\"12\">\n        <el-form-item>\n          <span slot=\"label\">\n            子表关联的外键名\n            <el-tooltip content=\"子表关联的外键名， 如：user_id\" placement=\"top\">\n              <i class=\"el-icon-question\"></i>\n            </el-tooltip>\n          </span>\n          <el-select v-model=\"info.subTableFkName\" placeholder=\"请选择\">\n            <el-option\n              v-for=\"(column, index) in subColumns\"\n              :key=\"index\"\n              :label=\"column.columnName + '：' + column.columnComment\"\n              :value=\"column.columnName\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n      </el-col>\n    </el-row>\n  </el-form>\n</template>\n\n<script>\nimport Treeselect from \"@riophae/vue-treeselect\";\nimport \"@riophae/vue-treeselect/dist/vue-treeselect.css\";\n\nexport default {\n  components: { Treeselect },\n  props: {\n    info: {\n      type: Object,\n      default: null\n    },\n    tables: {\n      type: Array,\n      default: null\n    },\n    menus: {\n      type: Array,\n      default: []\n    },\n  },\n  data() {\n    return {\n      subColumns: [],\n      rules: {\n        tplCategory: [\n          { required: true, message: \"请选择生成模板\", trigger: \"blur\" }\n        ],\n        packageName: [\n          { required: true, message: \"请输入生成包路径\", trigger: \"blur\" }\n        ],\n        moduleName: [\n          { required: true, message: \"请输入生成模块名\", trigger: \"blur\" }\n        ],\n        businessName: [\n          { required: true, message: \"请输入生成业务名\", trigger: \"blur\" }\n        ],\n        functionName: [\n          { required: true, message: \"请输入生成功能名\", trigger: \"blur\" }\n        ],\n      }\n    };\n  },\n  created() {},\n  watch: {\n    'info.subTableName': function(val) {\n      this.setSubTableColumns(val);\n    }\n  },\n  methods: {\n    /** 转换菜单数据结构 */\n    normalizer(node) {\n      if (node.children && !node.children.length) {\n        delete node.children;\n      }\n      return {\n        id: node.menuId,\n        label: node.menuName,\n        children: node.children\n      };\n    },\n    /** 选择子表名触发 */\n    subSelectChange(value) {\n      this.info.subTableFkName = '';\n    },\n    /** 选择生成模板触发 */\n    tplSelectChange(value) {\n      if(value !== 'sub') {\n        this.info.subTableName = '';\n        this.info.subTableFkName = '';\n      }\n    },\n    /** 设置关联外键 */\n    setSubTableColumns(value) {\n      for (var item in this.tables) {\n        const name = this.tables[item].tableName;\n        if (value === name) {\n          this.subColumns = this.tables[item].columns;\n          break;\n        }\n      }\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/tool/gen/importTable.vue",
    "content": "<template>\n  <!-- 导入表 -->\n  <el-dialog title=\"导入表\" :visible.sync=\"visible\" width=\"800px\" top=\"5vh\" append-to-body>\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\">\n      <el-form-item label=\"表名称\" prop=\"tableName\">\n        <el-input\n          v-model=\"queryParams.tableName\"\n          placeholder=\"请输入表名称\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"表描述\" prop=\"tableComment\">\n        <el-input\n          v-model=\"queryParams.tableComment\"\n          placeholder=\"请输入表描述\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n    <el-row>\n      <el-table @row-click=\"clickRow\" ref=\"table\" :data=\"dbTableList\" @selection-change=\"handleSelectionChange\" height=\"260px\">\n        <el-table-column type=\"selection\" width=\"55\"></el-table-column>\n        <el-table-column prop=\"tableName\" label=\"表名称\" :show-overflow-tooltip=\"true\"></el-table-column>\n        <el-table-column prop=\"tableComment\" label=\"表描述\" :show-overflow-tooltip=\"true\"></el-table-column>\n        <el-table-column prop=\"createTime\" label=\"创建时间\"></el-table-column>\n        <el-table-column prop=\"updateTime\" label=\"更新时间\"></el-table-column>\n      </el-table>\n      <pagination\n        v-show=\"total>0\"\n        :total=\"total\"\n        :page.sync=\"queryParams.pageNum\"\n        :limit.sync=\"queryParams.pageSize\"\n        @pagination=\"getList\"\n      />\n    </el-row>\n    <div slot=\"footer\" class=\"dialog-footer\">\n      <el-button type=\"primary\" @click=\"handleImportTable\">确 定</el-button>\n      <el-button @click=\"visible = false\">取 消</el-button>\n    </div>\n  </el-dialog>\n</template>\n\n<script>\nimport { listDbTable, importTable } from \"@/api/tool/gen\";\nexport default {\n  data() {\n    return {\n      // 遮罩层\n      visible: false,\n      // 选中数组值\n      tables: [],\n      // 总条数\n      total: 0,\n      // 表数据\n      dbTableList: [],\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        tableName: undefined,\n        tableComment: undefined\n      }\n    };\n  },\n  methods: {\n    // 显示弹框\n    show() {\n      this.getList();\n      this.visible = true;\n    },\n    clickRow(row) {\n      this.$refs.table.toggleRowSelection(row);\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.tables = selection.map(item => item.tableName);\n    },\n    // 查询表数据\n    getList() {\n      listDbTable(this.queryParams).then(res => {\n        if (res.code === 200) {\n          this.dbTableList = res.rows;\n          this.total = res.total;\n        }\n      });\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 导入按钮操作 */\n    handleImportTable() {\n      const tableNames = this.tables.join(\",\");\n      if (tableNames == \"\") {\n        this.$modal.msgError(\"请选择要导入的表\");\n        return;\n      }\n      importTable({ tables: tableNames }).then(res => {\n        this.$modal.msgSuccess(res.msg);\n        if (res.code === 200) {\n          this.visible = false;\n          this.$emit(\"ok\");\n        }\n      });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/src/views/tool/gen/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"数据源\" prop=\"dataName\">\n        <el-input\n          v-model=\"queryParams.dataName\"\n          placeholder=\"请输入数据源名称\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"表名称\" prop=\"tableName\">\n        <el-input\n          v-model=\"queryParams.tableName\"\n          placeholder=\"请输入表名称\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"表描述\" prop=\"tableComment\">\n        <el-input\n          v-model=\"queryParams.tableComment\"\n          placeholder=\"请输入表描述\"\n          clearable\n          @keyup.enter.native=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"创建时间\">\n        <el-date-picker\n          v-model=\"dateRange\"\n          style=\"width: 240px\"\n          value-format=\"yyyy-MM-dd\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n        ></el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          plain\n          icon=\"el-icon-download\"\n          size=\"mini\"\n          @click=\"handleGenTable\"\n          v-hasPermi=\"['tool:gen:code']\"\n        >生成</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"info\"\n          plain\n          icon=\"el-icon-upload\"\n          size=\"mini\"\n          @click=\"openImportTable\"\n          v-hasPermi=\"['tool:gen:import']\"\n        >导入</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"success\"\n          plain\n          icon=\"el-icon-edit\"\n          size=\"mini\"\n          :disabled=\"single\"\n          @click=\"handleEditTable\"\n          v-hasPermi=\"['tool:gen:edit']\"\n        >修改</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          plain\n          icon=\"el-icon-delete\"\n          size=\"mini\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-hasPermi=\"['tool:gen:remove']\"\n        >删除</el-button>\n      </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n    <el-table v-loading=\"loading\" :data=\"tableList\" @selection-change=\"handleSelectionChange\">\n      <el-table-column type=\"selection\" align=\"center\" width=\"55\"></el-table-column>\n      <el-table-column label=\"序号\" type=\"index\" width=\"50\" align=\"center\">\n        <template slot-scope=\"scope\">\n          <span>{{(queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1}}</span>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"表名称\"\n        align=\"center\"\n        prop=\"tableName\"\n        :show-overflow-tooltip=\"true\"\n        width=\"120\"\n      />\n      <el-table-column\n        label=\"表描述\"\n        align=\"center\"\n        prop=\"tableComment\"\n        :show-overflow-tooltip=\"true\"\n        width=\"120\"\n      />\n      <el-table-column\n        label=\"实体\"\n        align=\"center\"\n        prop=\"className\"\n        :show-overflow-tooltip=\"true\"\n        width=\"120\"\n      />\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"160\" />\n      <el-table-column label=\"更新时间\" align=\"center\" prop=\"updateTime\" width=\"160\" />\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"scope\">\n          <el-button\n            type=\"text\"\n            size=\"small\"\n            icon=\"el-icon-view\"\n            @click=\"handlePreview(scope.row)\"\n            v-hasPermi=\"['tool:gen:preview']\"\n          >预览</el-button>\n          <el-button\n            type=\"text\"\n            size=\"small\"\n            icon=\"el-icon-edit\"\n            @click=\"handleEditTable(scope.row)\"\n            v-hasPermi=\"['tool:gen:edit']\"\n          >编辑</el-button>\n          <el-button\n            type=\"text\"\n            size=\"small\"\n            icon=\"el-icon-delete\"\n            @click=\"handleDelete(scope.row)\"\n            v-hasPermi=\"['tool:gen:remove']\"\n          >删除</el-button>\n          <el-button\n            type=\"text\"\n            size=\"small\"\n            icon=\"el-icon-refresh\"\n            @click=\"handleSynchDb(scope.row)\"\n            v-hasPermi=\"['tool:gen:edit']\"\n          >同步</el-button>\n          <el-button\n            type=\"text\"\n            size=\"small\"\n            icon=\"el-icon-download\"\n            @click=\"handleGenTable(scope.row)\"\n            v-hasPermi=\"['tool:gen:code']\"\n          >生成代码</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n    <pagination\n      v-show=\"total>0\"\n      :total=\"total\"\n      :page.sync=\"queryParams.pageNum\"\n      :limit.sync=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n    <!-- 预览界面 -->\n    <el-dialog :title=\"preview.title\" :visible.sync=\"preview.open\" width=\"80%\" top=\"5vh\" append-to-body class=\"scrollbar\">\n      <el-tabs v-model=\"preview.activeName\">\n        <el-tab-pane\n          v-for=\"(value, key) in preview.data\"\n          :label=\"key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))\"\n          :name=\"key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))\"\n          :key=\"key\"\n        >\n          <el-link :underline=\"false\" icon=\"el-icon-document-copy\" v-clipboard:copy=\"value\" v-clipboard:success=\"clipboardSuccess\" style=\"float:right\">复制</el-link>\n          <pre><code class=\"hljs\" v-html=\"highlightedCode(value, key)\"></code></pre>\n        </el-tab-pane>\n      </el-tabs>\n    </el-dialog>\n    <import-table ref=\"import\" @ok=\"handleQuery\" />\n  </div>\n</template>\n\n<script>\nimport { listTable, previewTable, delTable, genCode, synchDb } from \"@/api/tool/gen\";\nimport importTable from \"./importTable\";\nimport hljs from \"highlight.js/lib/highlight\";\nimport \"highlight.js/styles/github-gist.css\";\nhljs.registerLanguage(\"java\", require(\"highlight.js/lib/languages/java\"));\nhljs.registerLanguage(\"xml\", require(\"highlight.js/lib/languages/xml\"));\nhljs.registerLanguage(\"html\", require(\"highlight.js/lib/languages/xml\"));\nhljs.registerLanguage(\"vue\", require(\"highlight.js/lib/languages/xml\"));\nhljs.registerLanguage(\"javascript\", require(\"highlight.js/lib/languages/javascript\"));\nhljs.registerLanguage(\"sql\", require(\"highlight.js/lib/languages/sql\"));\n\nexport default {\n  name: \"Gen\",\n  components: { importTable },\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 唯一标识符\n      uniqueId: \"\",\n      // 选中数组\n      ids: [],\n      // 选中表数组\n      tableNames: [],\n      // 非单个禁用\n      single: true,\n      // 非多个禁用\n      multiple: true,\n      // 显示搜索条件\n      showSearch: true,\n      // 总条数\n      total: 0,\n      // 表数据\n      tableList: [],\n      // 日期范围\n      dateRange: \"\",\n      // 查询参数\n      queryParams: {\n        pageNum: 1,\n        pageSize: 10,\n        tableName: undefined,\n        tableComment: undefined,\n        dataName: \"master\"\n      },\n      // 预览参数\n      preview: {\n        open: false,\n        title: \"代码预览\",\n        data: {},\n        activeName: \"domain.java\"\n      }\n    };\n  },\n  created() {\n    localStorage.setItem(\"dataName\", this.queryParams.dataName);\n    this.getList();\n  },\n  activated() {\n    const time = this.$route.query.t;\n    if (time != null && time != this.uniqueId) {\n      this.uniqueId = time;\n      this.queryParams.pageNum = Number(this.$route.query.pageNum);\n      this.getList();\n    }\n  },\n  methods: {\n    /** 查询表集合 */\n    getList() {\n      this.loading = true;\n      listTable(this.addDateRange(this.queryParams, this.dateRange)).then(response => {\n          this.tableList = response.rows;\n          this.total = response.total;\n          this.loading = false;\n        }\n      );\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      localStorage.setItem(\"dataName\", this.queryParams.dataName);\n      this.queryParams.pageNum = 1;\n      this.getList();\n    },\n    /** 生成代码操作 */\n    handleGenTable(row) {\n      const tableNames = row.tableName || this.tableNames;\n      if (tableNames == \"\") {\n        this.$modal.msgError(\"请选择要生成的数据\");\n        return;\n      }\n      if(row.genType === \"1\") {\n        genCode(row.tableName).then(response => {\n          this.$modal.msgSuccess(\"成功生成到自定义路径：\" + row.genPath);\n        });\n      } else {\n        this.$download.zip(\"/tool/gen/batchGenCode?tables=\" + tableNames, \"ruoyi.zip\");\n      }\n    },\n    /** 同步数据库操作 */\n    handleSynchDb(row) {\n      const tableName = row.tableName;\n      this.$modal.confirm('确认要强制同步\"' + tableName + '\"表结构吗？').then(function() {\n        return synchDb(tableName);\n      }).then(() => {\n        this.$modal.msgSuccess(\"同步成功\");\n      }).catch(() => {});\n    },\n    /** 打开导入表弹窗 */\n    openImportTable() {\n      this.$refs.import.show();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.dateRange = [];\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 预览按钮 */\n    handlePreview(row) {\n      previewTable(row.tableId).then(response => {\n        this.preview.data = response.data;\n        this.preview.open = true;\n        this.preview.activeName = \"domain.java\";\n      });\n    },\n    /** 高亮显示 */\n    highlightedCode(code, key) {\n      const vmName = key.substring(key.lastIndexOf(\"/\") + 1, key.indexOf(\".vm\"));\n      var language = vmName.substring(vmName.indexOf(\".\") + 1, vmName.length);\n      const result = hljs.highlight(language, code || \"\", true);\n      return result.value || '&nbsp;';\n    },\n    /** 复制代码成功 */\n    clipboardSuccess() {\n      this.$modal.msgSuccess(\"复制成功\");\n    },\n    // 多选框选中数据\n    handleSelectionChange(selection) {\n      this.ids = selection.map(item => item.tableId);\n      this.tableNames = selection.map(item => item.tableName);\n      this.single = selection.length != 1;\n      this.multiple = !selection.length;\n    },\n    /** 修改按钮操作 */\n    handleEditTable(row) {\n      const tableId = row.tableId || this.ids[0];\n      const tableName = row.tableName || this.tableNames[0];\n      const params = { pageNum: this.queryParams.pageNum };\n      this.$tab.openPage(\"修改[\" + tableName + \"]生成配置\", '/tool/gen-edit/index/' + tableId, params);\n    },\n    /** 删除按钮操作 */\n    handleDelete(row) {\n      const tableIds = row.tableId || this.ids;\n      this.$modal.confirm('是否确认删除表编号为\"' + tableIds + '\"的数据项？').then(function() {\n        return delTable(tableIds);\n      }).then(() => {\n        this.getList();\n        this.$modal.msgSuccess(\"删除成功\");\n      }).catch(() => {});\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "ruoyi-ui/vue.config.js",
    "content": "'use strict'\nconst path = require('path')\n\nfunction resolve(dir) {\n  return path.join(__dirname, dir)\n}\n\nconst CompressionPlugin = require('compression-webpack-plugin')\n\nconst name = process.env.VUE_APP_TITLE || '派之城后台管理系统' // 网页标题\n\nconst port = process.env.port || process.env.npm_config_port || 80 // 端口\n\n// vue.config.js 配置说明\n//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions\n// 这里只列一部分，具体配置参考文档\nmodule.exports = {\n  // 部署生产环境和开发环境下的URL。\n  // 默认情况下，Vue CLI 会假设你的应用是被部署在一个域名的根路径上\n  // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上，你就需要用这个选项指定这个子路径。例如，如果你的应用被部署在 https://www.ruoyi.vip/admin/，则设置 baseUrl 为 /admin/。\n  publicPath: process.env.VUE_APP_CONTEXT_PATH,\n  // 在npm run build 或 yarn build 时 ，生成文件的目录名称（要和baseUrl的生产环境路径一致）（默认dist）\n  outputDir: 'dist',\n  // 用于放置生成的静态资源 (js、css、img、fonts) 的；（项目打包之后，静态资源会放在这个文件夹下）\n  assetsDir: 'static',\n  // 是否开启eslint保存检测，有效值：ture | false | 'error'\n  lintOnSave: false, //process.env.NODE_ENV === 'development',\n  // 如果你不需要生产环境的 source map，可以将其设置为 false 以加速生产环境构建。\n  productionSourceMap: false,\n  // webpack-dev-server 相关配置\n  devServer: {\n    host: '0.0.0.0',\n    port: port,\n    open: true,\n    proxy: {\n      // detail: https://cli.vuejs.org/config/#devserver-proxy\n      [process.env.VUE_APP_BASE_API]: {\n        target:   `http://localhost:9393`, //`https://flya.mynatapp.cc`,\n        changeOrigin: true,\n        pathRewrite: {\n          ['^' + process.env.VUE_APP_BASE_API]: ''\n        }\n      }\n    },\n    disableHostCheck: true\n  },\n  css: {\n    loaderOptions: {\n      sass: {\n        sassOptions: { outputStyle: \"expanded\" }\n      }\n    }\n  },\n  configureWebpack: {\n    name: name,\n    resolve: {\n      alias: {\n        '@': resolve('src')\n      }\n    },\n    plugins: [\n      // http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件\n      new CompressionPlugin({\n        cache: false,                   // 不启用文件缓存\n        test: /\\.(js|css|html)?$/i,     // 压缩文件格式\n        filename: '[path].gz[query]',   // 压缩后的文件名\n        algorithm: 'gzip',              // 使用gzip压缩\n        minRatio: 0.8                   // 压缩率小于1才会压缩\n      })\n    ],\n  },\n  chainWebpack(config) {\n    config.plugins.delete('preload') // TODO: need test\n    config.plugins.delete('prefetch') // TODO: need test\n\n    // set svg-sprite-loader\n    config.module\n      .rule('svg')\n      .exclude.add(resolve('src/assets/icons'))\n      .end()\n    config.module\n      .rule('icons')\n      .test(/\\.svg$/)\n      .include.add(resolve('src/assets/icons'))\n      .end()\n      .use('svg-sprite-loader')\n      .loader('svg-sprite-loader')\n      .options({\n        symbolId: 'icon-[name]'\n      })\n      .end()\n\n    config\n      .when(process.env.NODE_ENV !== 'development',\n        config => {\n          config\n            .plugin('ScriptExtHtmlWebpackPlugin')\n            .after('html')\n            .use('script-ext-html-webpack-plugin', [{\n            // `runtime` must same as runtimeChunk name. default is `runtime`\n              inline: /runtime\\..*\\.js$/\n            }])\n            .end()\n          config\n            .optimization.splitChunks({\n              chunks: 'all',\n              cacheGroups: {\n                libs: {\n                  name: 'chunk-libs',\n                  test: /[\\\\/]node_modules[\\\\/]/,\n                  priority: 10,\n                  chunks: 'initial' // only package third parties that are initially dependent\n                },\n                elementUI: {\n                  name: 'chunk-elementUI', // split elementUI into a single package\n                  priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app\n                  test: /[\\\\/]node_modules[\\\\/]_?element-ui(.*)/ // in order to adapt to cnpm\n                },\n                commons: {\n                  name: 'chunk-commons',\n                  test: resolve('src/components'), // can customize your rules\n                  minChunks: 3, //  minimum common number\n                  priority: 5,\n                  reuseExistingChunk: true\n                }\n              }\n            })\n          config.optimization.runtimeChunk('single'),\n          {\n             from: path.resolve(__dirname, './public/robots.txt'), //防爬虫文件\n             to: './' //到根目录下\n          }\n        }\n      )\n  }\n}\n"
  },
  {
    "path": "script/bin/ry.bat",
    "content": "rem 使用者应根据自身平台编码自行转换 防止乱码 例如 win使用gbk编码\n@echo off\n\nrem jar平级目录\nset AppName=ruoyi-admin.jar\n\nrem JVM参数\nset JVM_OPTS=\"-Dname=%AppName%  -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps  -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC\"\n\n\nECHO.\n\tECHO.  [1] 启动%AppName%\n\tECHO.  [2] 关闭%AppName%\n\tECHO.  [3] 重启%AppName%\n\tECHO.  [4] 启动状态 %AppName%\n\tECHO.  [5] 退 出\nECHO.\n\nECHO.请输入选择项目的序号:\nset /p ID=\n\tIF \"%id%\"==\"1\" GOTO start\n\tIF \"%id%\"==\"2\" GOTO stop\n\tIF \"%id%\"==\"3\" GOTO restart\n\tIF \"%id%\"==\"4\" GOTO status\n\tIF \"%id%\"==\"5\" EXIT\nPAUSE\n:start\n    for /f \"usebackq tokens=1-2\" %%a in (`jps -l ^| findstr %AppName%`) do (\n\t\tset pid=%%a\n\t\tset image_name=%%b\n\t)\n\tif  defined pid (\n\t\techo %%is running\n\t\tPAUSE\n\t)\n\nstart javaw %JVM_OPTS% -jar %AppName%\n\necho  starting……\necho  Start %AppName% success...\ngoto:eof\n\nrem 函数stop通过jps命令查找pid并结束进程\n:stop\n\tfor /f \"usebackq tokens=1-2\" %%a in (`jps -l ^| findstr %AppName%`) do (\n\t\tset pid=%%a\n\t\tset image_name=%%b\n\t)\n\tif not defined pid (echo process %AppName% does not exists) else (\n\t\techo prepare to kill %image_name%\n\t\techo start kill %pid% ...\n\t\trem 根据进程ID，kill进程\n\t\ttaskkill /f /pid %pid%\n\t)\ngoto:eof\n:restart\n\tcall :stop\n    call :start\ngoto:eof\n:status\n\tfor /f \"usebackq tokens=1-2\" %%a in (`jps -l ^| findstr %AppName%`) do (\n\t\tset pid=%%a\n\t\tset image_name=%%b\n\t)\n\tif not defined pid (echo process %AppName% is dead ) else (\n\t\techo %image_name% is running\n\t)\ngoto:eof\n"
  },
  {
    "path": "script/bin/ry.sh",
    "content": "#!/bin/sh\n# ./ry.sh start 启动 stop 停止 restart 重启 status 状态   -Dserver.port=9393\nAppName=ruoyi-admin.jar\n\n# JVM参数\nJVM_OPTS=\" -Dname=$AppName  -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC\"\n\nAPP_HOME=`pwd`\nLOG_PATH=$APP_HOME/logs/$AppName.log\n\nif [ \"$1\" = \"\" ];\nthen\n    echo -e \"\\033[0;31m 未输入操作名 \\033[0m  \\033[0;34m {start|stop|restart|status} \\033[0m\"\n    exit 1\nfi\n\nif [ \"$AppName\" = \"\" ];\nthen\n    echo -e \"\\033[0;31m 未输入应用名 \\033[0m\"\n    exit 1\nfi\n\nfunction start()\n{\n    PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'`\n\n    if [ x\"$PID\" != x\"\" ]; then\n        echo \"$AppName is running...\"\n    else\n        nohup java $JVM_OPTS -jar $AppName > /dev/null 2>&1 &\n        echo \"Start $AppName success...\"\n    fi\n}\n\nfunction stop()\n{\n    echo \"Stop $AppName\"\n\n    PID=\"\"\n    query(){\n        PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'`\n    }\n\n    query\n    if [ x\"$PID\" != x\"\" ]; then\n        kill -TERM $PID\n        echo \"$AppName (pid:$PID) exiting...\"\n        while [ x\"$PID\" != x\"\" ]\n        do\n            sleep 1\n            query\n        done\n        echo \"$AppName exited.\"\n    else\n        echo \"$AppName already stopped.\"\n    fi\n}\n\nfunction restart()\n{\n    stop\n    sleep 2\n    start\n}\n\nfunction status()\n{\n    PID=`ps -ef |grep java|grep $AppName|grep -v grep|wc -l`\n    if [ $PID != 0 ];then\n        echo \"$AppName is running...\"\n    else\n        echo \"$AppName is not running...\"\n    fi\n}\n\ncase $1 in\n    start)\n    start;;\n    stop)\n    stop;;\n    restart)\n    restart;;\n    status)\n    status;;\n    *)\n\nesac\n"
  },
  {
    "path": "script/docker/database.yml",
    "content": "version: '3'\n\nservices:\n  # 此镜像仅用于测试 正式环境需自行安装数据库\n  # SID: XE user: system password: oracle\n  oracle:\n    image: tekintian/oracle12c:latest\n    container_name: oracle\n    environment:\n      # 时区上海\n      TZ: Asia/Shanghai\n      DBCA_TOTAL_MEMORY: 16192\n    ports:\n      - \"18080:8080\"\n      - \"1521:1521\"\n    volumes:\n      # 数据挂载\n      - \"/docker/oracle/data:/u01/app/oracle\"\n    network_mode: \"host\"\n\n  # 此镜像仅用于测试 正式环境需自行安装数据库\n  sqlserver:\n    image: mcr.microsoft.com/mssql/server:2017-latest\n    container_name: sqlserver\n    environment:\n      # 时区上海\n      TZ: Asia/Shanghai\n      ACCEPT_EULA: \"Y\"\n      SA_PASSWORD: \"Ruoyi@123\"\n    ports:\n      - \"1433:1433\"\n    volumes:\n      # 数据挂载\n      - \"/docker/sqlserver/data:/var/opt/mssql\"\n    network_mode: \"host\"\n\n  postgres:\n    image: postgres:14.2\n    container_name: postgres\n    environment:\n      POSTGRES_USER: root\n      POSTGRES_PASSWORD: root\n      POSTGRES_DB: postgres\n    ports:\n      - \"5432:5432\"\n    volumes:\n      - /docker/postgres/data:/var/lib/postgresql/data\n    network_mode: \"host\"\n\n  postgres13:\n    image: postgres:13.6\n    container_name: postgres13\n    environment:\n      POSTGRES_USER: root\n      POSTGRES_PASSWORD: root\n      POSTGRES_DB: postgres\n    ports:\n      - \"5433:5432\"\n    volumes:\n      - /docker/postgres13/data:/var/lib/postgresql/data\n    network_mode: \"host\"\n"
  },
  {
    "path": "script/docker/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  nginx-web:\n    image: nginx:1.22.1\n    container_name: nginx-web\n    environment:\n      # 时区上海\n      TZ: Asia/Shanghai\n    ports:\n      - \"80:80\"\n      - \"443:443\"\n    volumes:\n      # 证书映射\n      - /mnt/hiwoo/nginx/cert:/etc/nginx/cert\n      # 配置文件映射\n      - /mnt/hiwoo/nginx/conf/nginx.conf:/etc/nginx/nginx.conf\n      # 页面目录\n      - /mnt/hiwoo/nginx/html:/usr/share/nginx/html\n      # 日志目录\n      - /mnt/hiwoo/nginx/logs:/var/log/nginx\n    privileged: true\n    network_mode: \"host\"\n\n\n"
  },
  {
    "path": "script/docker/nginx/conf/nginx.conf",
    "content": "worker_processes  1;\n\nerror_log  /var/log/nginx/error.log warn;\npid        /var/run/nginx.pid;\n\nevents {\n    worker_connections  1024;\n}\n\nhttp {\n    include       mime.types;\n    default_type  application/octet-stream;\n    sendfile        on;\n    keepalive_timeout  65;\n    # 限制body大小\n    client_max_body_size 100m;\n\n    log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n                          '$status $body_bytes_sent \"$http_referer\" '\n                          '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n    access_log  /var/log/nginx/access.log  main;\n\n    upstream server {\n        ip_hash;\n        server 127.0.0.1:8080;\n        server 127.0.0.1:8081;\n    }\n\n    upstream monitor-admin {\n        server 127.0.0.1:9090;\n    }\n\n    upstream xxljob-admin {\n        server 127.0.0.1:9100;\n    }\n\n    server {\n        listen       80;\n        server_name  localhost;\n\n        # https配置参考 start\n        #listen       443 ssl;\n\n        # 证书直接存放 /docker/nginx/cert/ 目录下即可 更改证书名称即可 无需更改证书路径\n        #ssl on;\n        #ssl_certificate      /etc/nginx/cert/xxx.local.crt; # /etc/nginx/cert/ 为docker映射路径 不允许更改\n        #ssl_certificate_key  /etc/nginx/cert/xxx.local.key; # /etc/nginx/cert/ 为docker映射路径 不允许更改\n        #ssl_session_timeout 5m;\n        #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;\n        #ssl_protocols TLSv1 TLSv1.1 TLSv1.2;\n        #ssl_prefer_server_ciphers on;\n        # https配置参考 end\n\n        # 演示环境配置 拦截除 GET POST 之外的所有请求\n        # if ($request_method !~* GET|POST) {\n        #     rewrite  ^/(.*)$  /403;\n        # }\n\n        # location = /403 {\n        #     default_type application/json;\n        #     return 200 '{\"msg\":\"演示模式，不允许操作\",\"code\":500}';\n        # }\n\n        # 限制外网访问内网 actuator 相关路径\n        location ~ ^(/[^/]*)?/actuator(/.*)?$ {\n            return 403;\n        }\n\n        location / {\n            root   /usr/share/nginx/html;\n            try_files $uri $uri/ /index.html;\n            index  index.html index.htm;\n        }\n\n        location /prod-api/ {\n            proxy_set_header Host $http_host;\n            proxy_set_header X-Real-IP $remote_addr;\n            proxy_set_header REMOTE-HOST $remote_addr;\n            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n            proxy_pass http://server/;\n        }\n\n        # https 会拦截内链所有的 http 请求 造成功能无法使用\n        # 解决方案1 将 admin 服务 也配置成 https\n        # 解决方案2 将菜单配置为外链访问 走独立页面 http 访问\n        location /admin/ {\n            proxy_set_header Host $http_host;\n            proxy_set_header X-Real-IP $remote_addr;\n            proxy_set_header REMOTE-HOST $remote_addr;\n            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n            proxy_pass http://monitor-admin/admin/;\n        }\n\n        # https 会拦截内链所有的 http 请求 造成功能无法使用\n        # 解决方案1 将 xxljob 服务 也配置成 https\n        # 解决方案2 将菜单配置为外链访问 走独立页面 http 访问\n        location /xxl-job-admin/ {\n            proxy_set_header Host $http_host;\n            proxy_set_header X-Real-IP $remote_addr;\n            proxy_set_header REMOTE-HOST $remote_addr;\n            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n            proxy_pass http://xxljob-admin/xxl-job-admin/;\n        }\n\n        error_page   500 502 503 504  /50x.html;\n        location = /50x.html {\n            root   html;\n        }\n    }\n}\n"
  },
  {
    "path": "script/docker/redis/conf/redis.conf",
    "content": "# redis 密码\nrequirepass ruoyi123\n\n# key 监听器配置\n# notify-keyspace-events Ex\n\n# 配置持久化文件存储路径\ndir /redis/data\n# 配置rdb\n# 15分钟内有至少1个key被更改则进行快照\nsave 900 1\n# 5分钟内有至少10个key被更改则进行快照\nsave 300 10\n# 1分钟内有至少10000个key被更改则进行快照\nsave 60 10000\n# 开启压缩\nrdbcompression yes\n# rdb文件名 用默认的即可\ndbfilename dump.rdb\n\n# 开启aof\nappendonly yes\n# 文件名\nappendfilename \"appendonly.aof\"\n# 持久化策略,no:不同步,everysec:每秒一次,always:总是同步,速度比较慢\n# appendfsync always\nappendfsync everysec\n# appendfsync no\n"
  },
  {
    "path": "script/docker/redis/data/README.md",
    "content": "数据目录 请执行 `chmod 777 /docker/redis/data` 赋予读写权限 否则将无法写入数据"
  },
  {
    "path": "script/sql/oracle/oracle_ry_vue_4.X.sql",
    "content": "-- ----------------------------\n-- 1、部门表\n-- ----------------------------\ncreate table sys_dept (\n  dept_id           number(20)      not null,\n  parent_id         number(20)      default 0,\n  ancestors         varchar2(500)   default '',\n  dept_name         varchar2(30)    default '',\n  order_num         number(4)       default 0,\n  leader            varchar2(20)    default null,\n  phone             varchar2(11)    default null,\n  email             varchar2(50)    default null,\n  status            char(1)         default '0',\n  del_flag          char(1)         default '0',\n  create_by         varchar2(64)    default '',\n  create_time       date,\n  update_by         varchar2(64)    default '',\n  update_time       date\n);\n\nalter table sys_dept add constraint pk_sys_dept primary key (dept_id);\n\ncomment on table  sys_dept              is '部门表';\ncomment on column sys_dept.dept_id      is '部门id';\ncomment on column sys_dept.parent_id    is '父部门id';\ncomment on column sys_dept.ancestors    is '祖级列表';\ncomment on column sys_dept.dept_name    is '部门名称';\ncomment on column sys_dept.order_num    is '显示顺序';\ncomment on column sys_dept.leader       is '负责人';\ncomment on column sys_dept.phone        is '联系电话';\ncomment on column sys_dept.email        is '邮箱';\ncomment on column sys_dept.status       is '部门状态（0正常 1停用）';\ncomment on column sys_dept.del_flag     is '删除标志（0代表存在 2代表删除）';\ncomment on column sys_dept.create_by    is '创建者';\ncomment on column sys_dept.create_time  is '创建时间';\ncomment on column sys_dept.update_by    is '更新者';\ncomment on column sys_dept.update_time  is '更新时间';\n\n-- ----------------------------\n-- 初始化-部门表数据\n-- ----------------------------\ninsert into sys_dept values(100,  0,   '0',          '若依科技',   0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null);\ninsert into sys_dept values(101,  100, '0,100',      '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null);\ninsert into sys_dept values(102,  100, '0,100',      '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null);\ninsert into sys_dept values(103,  101, '0,100,101',  '研发部门',   1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null);\ninsert into sys_dept values(104,  101, '0,100,101',  '市场部门',   2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null);\ninsert into sys_dept values(105,  101, '0,100,101',  '测试部门',   3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null);\ninsert into sys_dept values(106,  101, '0,100,101',  '财务部门',   4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null);\ninsert into sys_dept values(107,  101, '0,100,101',  '运维部门',   5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null);\ninsert into sys_dept values(108,  102, '0,100,102',  '市场部门',   1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null);\ninsert into sys_dept values(109,  102, '0,100,102',  '财务部门',   2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null);\n\n\n-- ----------------------------\n-- 2、用户信息表\n-- ----------------------------\ncreate table sys_user (\n  user_id           number(20)      not null,\n  dept_id           number(20)      default null,\n  user_name         varchar2(40)    not null,\n  nick_name         varchar2(40)    not null,\n  user_type         varchar2(10)    default 'sys_user',\n  email             varchar2(50)    default '',\n  phonenumber       varchar2(11)    default '',\n  sex               char(1)         default '0',\n  avatar            varchar2(100)   default '',\n  password          varchar2(100)   default '',\n  status            char(1)         default '0',\n  del_flag          char(1)         default '0',\n  login_ip          varchar2(128)   default '',\n  login_date        date,\n  create_by         varchar2(64),\n  create_time       date,\n  update_by         varchar2(64)    default '',\n  update_time       date,\n  remark            varchar2(500)   default ''\n);\n\nalter table sys_user add constraint pk_sys_user primary key (user_id);\n\ncomment on table  sys_user              is '用户信息表';\ncomment on column sys_user.user_id      is '用户ID';\ncomment on column sys_user.dept_id      is '部门ID';\ncomment on column sys_user.user_name    is '用户账号';\ncomment on column sys_user.nick_name    is '用户昵称';\ncomment on column sys_user.user_type    is '用户类型（sys_user系统用户）';\ncomment on column sys_user.email        is '用户邮箱';\ncomment on column sys_user.phonenumber  is '手机号码';\ncomment on column sys_user.sex          is '用户性别（0男 1女 2未知）';\ncomment on column sys_user.avatar       is '头像路径';\ncomment on column sys_user.password     is '密码';\ncomment on column sys_user.status       is '帐号状态（0正常 1停用）';\ncomment on column sys_user.del_flag     is '删除标志（0代表存在 2代表删除）';\ncomment on column sys_user.login_ip     is '最后登录IP';\ncomment on column sys_user.login_date   is '最后登录时间';\ncomment on column sys_user.create_by    is '创建者';\ncomment on column sys_user.create_time  is '创建时间';\ncomment on column sys_user.update_by    is '更新者';\ncomment on column sys_user.update_time  is '更新时间';\ncomment on column sys_user.remark       is '备注';\n\n-- ----------------------------\n-- 初始化-用户信息表数据\n-- ----------------------------\ninsert into sys_user values(1,  103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate, 'admin', sysdate, '', null, '管理员');\ninsert into sys_user values(2,  105, 'lionli', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@qq.com',  '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate, 'admin', sysdate, '', null, '测试员');\n\n\n-- ----------------------------\n-- 3、岗位信息表\n-- ----------------------------\ncreate table sys_post (\n  post_id           number(20)      not null,\n  post_code         varchar2(64)    not null,\n  post_name         varchar2(50)    not null,\n  post_sort         number(4)       not null,\n  status            char(1)         not null,\n  create_by         varchar2(64)    default '',\n  create_time       date,\n  update_by         varchar2(64)    default '',\n  update_time       date,\n  remark            varchar2(500)\n);\n\nalter table sys_post add constraint pk_sys_post primary key (post_id);\n\ncomment on table  sys_post              is '岗位信息表';\ncomment on column sys_post.post_id      is '岗位ID';\ncomment on column sys_post.post_code    is '岗位编码';\ncomment on column sys_post.post_name    is '岗位名称';\ncomment on column sys_post.post_sort    is '显示顺序';\ncomment on column sys_post.status       is '状态（0正常 1停用）';\ncomment on column sys_post.create_by    is '创建者';\ncomment on column sys_post.create_time  is '创建时间';\ncomment on column sys_post.update_by    is '更新者';\ncomment on column sys_post.update_time  is '更新时间';\ncomment on column sys_post.remark       is '备注';\n\n-- ----------------------------\n-- 初始化-岗位信息表数据\n-- ----------------------------\ninsert into sys_post values(1, 'ceo',  '董事长',    1, '0', 'admin', sysdate, '', null, '');\ninsert into sys_post values(2, 'se',   '项目经理',  2, '0', 'admin', sysdate, '', null, '');\ninsert into sys_post values(3, 'hr',   '人力资源',  3, '0', 'admin', sysdate, '', null, '');\ninsert into sys_post values(4, 'user', '普通员工',  4, '0', 'admin', sysdate, '', null, '');\n\n\n-- ----------------------------\n-- 4、角色信息表\n-- ----------------------------\ncreate table sys_role (\n  role_id              number(20)      not null,\n  role_name            varchar2(30)    not null,\n  role_key             varchar2(100)   not null,\n  role_sort            number(4)       not null,\n  data_scope           char(1)         default '1',\n  menu_check_strictly  number(1)       default 1,\n  dept_check_strictly  number(1)       default 1,\n  status               char(1)         not null,\n  del_flag             char(1)         default '0',\n  create_by            varchar2(64)    default '',\n  create_time          date,\n  update_by            varchar2(64)    default '',\n  update_time          date,\n  remark               varchar2(500)   default null\n);\n\nalter table sys_role add constraint pk_sys_role primary key (role_id);\n\ncomment on table  sys_role                       is '角色信息表';\ncomment on column sys_role.role_id               is '角色ID';\ncomment on column sys_role.role_name             is '角色名称';\ncomment on column sys_role.role_key              is '角色权限字符串';\ncomment on column sys_role.role_sort             is '显示顺序';\ncomment on column sys_role.data_scope            is '数据范围（1：全部数据权限 2：自定数据权限 3：本部门数据权限 4：本部门及以下数据权限）';\ncomment on column sys_role.menu_check_strictly   is '菜单树选择项是否关联显示';\ncomment on column sys_role.dept_check_strictly   is '部门树选择项是否关联显示';\ncomment on column sys_role.status                is '角色状态（0正常 1停用）';\ncomment on column sys_role.del_flag              is '删除标志（0代表存在 2代表删除）';\ncomment on column sys_role.create_by             is '创建者';\ncomment on column sys_role.create_time           is '创建时间';\ncomment on column sys_role.update_by             is '更新者';\ncomment on column sys_role.update_time           is '更新时间';\ncomment on column sys_role.remark                is '备注';\n\n-- ----------------------------\n-- 初始化-角色信息表数据\n-- ----------------------------\ninsert into sys_role values('1', '超级管理员',  'admin',  1, 1, 1, 1, '0', '0', 'admin', sysdate, '', null, '超级管理员');\ninsert into sys_role values('2', '普通角色',    'common', 2, 2, 1, 1, '0', '0', 'admin', sysdate, '', null, '普通角色');\n\n\n-- ----------------------------\n-- 5、菜单权限表\n-- ----------------------------\ncreate table sys_menu (\n  menu_id           number(20)      not null,\n  menu_name         varchar2(50)    not null,\n  parent_id         number(20)      default 0,\n  order_num         number(4)       default 0,\n  path              varchar(200)    default '',\n  component         varchar(255)    default null,\n  query_param       varchar(255)    default null,\n  is_frame          number(1)       default 1,\n  is_cache          number(1)       default 0,\n  menu_type         char(1)         default '',\n  visible           char(1)         default 0,\n  status            char(1)         default 0,\n  perms             varchar2(100)   default null,\n  icon              varchar2(100)   default '#',\n  create_by         varchar2(64)    default '',\n  create_time       date,\n  update_by         varchar2(64)    default '',\n  update_time       date ,\n  remark            varchar2(500)   default ''\n);\n\nalter table sys_menu add constraint pk_sys_menu primary key (menu_id);\n\ncomment on table  sys_menu              is '菜单权限表';\ncomment on column sys_menu.menu_id      is '菜单ID';\ncomment on column sys_menu.menu_name    is '菜单名称';\ncomment on column sys_menu.parent_id    is '父菜单ID';\ncomment on column sys_menu.order_num    is '显示顺序';\ncomment on column sys_menu.path         is '请求地址';\ncomment on column sys_menu.component    is '路由地址';\ncomment on column sys_menu.query_param  is '路由参数';\ncomment on column sys_menu.is_frame     is '是否为外链（0是 1否）';\ncomment on column sys_menu.is_cache     is '是否缓存（0缓存 1不缓存）';\ncomment on column sys_menu.menu_type    is '菜单类型（M目录 C菜单 F按钮）';\ncomment on column sys_menu.visible      is '显示状态（0显示 1隐藏）';\ncomment on column sys_menu.status       is '菜单状态（0正常 1停用）';\ncomment on column sys_menu.perms        is '权限标识';\ncomment on column sys_menu.icon         is '菜单图标';\ncomment on column sys_menu.create_by    is '创建者';\ncomment on column sys_menu.create_time  is '创建时间';\ncomment on column sys_menu.update_by    is '更新者';\ncomment on column sys_menu.update_time  is '更新时间';\ncomment on column sys_menu.remark       is '备注';\n\n-- ----------------------------\n-- 初始化-菜单信息表数据\n-- ----------------------------\n-- 一级菜单\ninsert into sys_menu values('1', '系统管理', '0', '1', 'system',           null, '', 1, 0, 'M', '0', '0', '', 'system',   'admin', sysdate, '', null, '系统管理目录');\ninsert into sys_menu values('2', '系统监控', '0', '2', 'monitor',          null, '', 1, 0, 'M', '0', '0', '', 'monitor',  'admin', sysdate, '', null, '系统监控目录');\ninsert into sys_menu values('3', '系统工具', '0', '3', 'tool',             null, '', 1, 0, 'M', '0', '0', '', 'tool',     'admin', sysdate, '', null, '系统工具目录');\ninsert into sys_menu values('4', 'PLUS官网', '0', '4', 'https://gitee.com/dromara/RuoYi-Vue-Plus', null, '', 0, 0, 'M', '0', '0', '', 'guide',    'admin', sysdate, '', null, 'RuoYi-Vue-Plus官网地址');\n-- 二级菜单\ninsert into sys_menu values('100',  '用户管理', '1',   '1', 'user',       'system/user/index',        '', 1, 0, 'C', '0', '0', 'system:user:list',        'user',          'admin', sysdate, '', null, '用户管理菜单');\ninsert into sys_menu values('101',  '角色管理', '1',   '2', 'role',       'system/role/index',        '', 1, 0, 'C', '0', '0', 'system:role:list',        'peoples',       'admin', sysdate, '', null, '角色管理菜单');\ninsert into sys_menu values('102',  '菜单管理', '1',   '3', 'menu',       'system/menu/index',        '', 1, 0, 'C', '0', '0', 'system:menu:list',        'tree-table',    'admin', sysdate, '', null, '菜单管理菜单');\ninsert into sys_menu values('103',  '部门管理', '1',   '4', 'dept',       'system/dept/index',        '', 1, 0, 'C', '0', '0', 'system:dept:list',        'tree',          'admin', sysdate, '', null, '部门管理菜单');\ninsert into sys_menu values('104',  '岗位管理', '1',   '5', 'post',       'system/post/index',        '', 1, 0, 'C', '0', '0', 'system:post:list',        'post',          'admin', sysdate, '', null, '岗位管理菜单');\ninsert into sys_menu values('105',  '字典管理', '1',   '6', 'dict',       'system/dict/index',        '', 1, 0, 'C', '0', '0', 'system:dict:list',        'dict',          'admin', sysdate, '', null, '字典管理菜单');\ninsert into sys_menu values('106',  '参数设置', '1',   '7', 'config',     'system/config/index',      '', 1, 0, 'C', '0', '0', 'system:config:list',      'edit',          'admin', sysdate, '', null, '参数设置菜单');\ninsert into sys_menu values('107',  '通知公告', '1',   '8', 'notice',     'system/notice/index',      '', 1, 0, 'C', '0', '0', 'system:notice:list',      'message',       'admin', sysdate, '', null, '通知公告菜单');\ninsert into sys_menu values('108',  '日志管理', '1',   '9', 'log',        '',                         '', 1, 0, 'M', '0', '0', '',                        'log',           'admin', sysdate, '', null, '日志管理菜单');\ninsert into sys_menu values('109',  '在线用户', '2',   '1', 'online',     'monitor/online/index',     '', 1, 0, 'C', '0', '0', 'monitor:online:list',     'online',        'admin', sysdate, '', null, '在线用户菜单');\ninsert into sys_menu values('112',  '缓存列表', '2',   '6', 'cacheList',  'monitor/cache/list',       '', 1, 0, 'C', '0', '0', 'monitor:cache:list',      'redis-list',    'admin', sysdate, '', null, '缓存列表菜单');\ninsert into sys_menu values('113',  '缓存监控', '2',   '5', 'cache',      'monitor/cache/index',      '', 1, 0, 'C', '0', '0', 'monitor:cache:list',      'redis',         'admin', sysdate, '', null, '缓存监控菜单');\ninsert into sys_menu values('114',  '表单构建', '3',   '1', 'build',      'tool/build/index',         '', 1, 0, 'C', '0', '0', 'tool:build:list',         'build',         'admin', sysdate, '', null, '表单构建菜单');\ninsert into sys_menu values('115',  '代码生成', '3',   '2', 'gen',        'tool/gen/index',           '', 1, 0, 'C', '0', '0', 'tool:gen:list',           'code',          'admin', sysdate, '', null, '代码生成菜单');\n-- springboot-admin监控\ninsert into sys_menu values('117',  'Admin监控', '2',  '5', 'Admin',      'monitor/admin/index',      '', 1, 0, 'C', '0', '0', 'monitor:admin:list',      'dashboard',     'admin', sysdate, '', null, 'Admin监控菜单');\n-- oss菜单\ninsert into sys_menu values('118',  '文件管理', '1', '10', 'oss', 'system/oss/index', '', 1, 0, 'C', '0', '0', 'system:oss:list', 'upload', 'admin', sysdate, '', null, '文件管理菜单');\n-- xxl-job-admin控制台\ninsert into sys_menu values('120',  '任务调度中心', '2',  '5', 'XxlJob',      'monitor/xxljob/index',      '', 1, 0, 'C', '0', '0', 'monitor:xxljob:list',      'job',     'admin', sysdate, '', null, 'Xxl-Job控制台菜单');\n\n-- 三级菜单\ninsert into sys_menu values('500',  '操作日志', '108', '1', 'operlog',    'monitor/operlog/index',    '', 1, 0, 'C', '0', '0', 'monitor:operlog:list',    'form',          'admin', sysdate, '', null, '操作日志菜单');\ninsert into sys_menu values('501',  '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor',    'admin', sysdate, '', null, '登录日志菜单');\n-- 用户管理按钮\ninsert into sys_menu values('1001', '用户查询', '100', '1',  '', '', '', 1, 0, 'F', '0', '0', 'system:user:query',          '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1002', '用户新增', '100', '2',  '', '', '', 1, 0, 'F', '0', '0', 'system:user:add',            '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1003', '用户修改', '100', '3',  '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit',           '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1004', '用户删除', '100', '4',  '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove',         '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1005', '用户导出', '100', '5',  '', '', '', 1, 0, 'F', '0', '0', 'system:user:export',         '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1006', '用户导入', '100', '6',  '', '', '', 1, 0, 'F', '0', '0', 'system:user:import',         '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1007', '重置密码', '100', '7',  '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd',       '#', 'admin', sysdate, '', null, '');\n-- 角色管理按钮\ninsert into sys_menu values('1008', '角色查询', '101', '1',  '', '', '', 1, 0, 'F', '0', '0', 'system:role:query',          '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1009', '角色新增', '101', '2',  '', '', '', 1, 0, 'F', '0', '0', 'system:role:add',            '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1010', '角色修改', '101', '3',  '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit',           '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1011', '角色删除', '101', '4',  '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove',         '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1012', '角色导出', '101', '5',  '', '', '', 1, 0, 'F', '0', '0', 'system:role:export',         '#', 'admin', sysdate, '', null, '');\n-- 菜单管理按钮\ninsert into sys_menu values('1013', '菜单查询', '102', '1',  '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query',          '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1014', '菜单新增', '102', '2',  '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add',            '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1015', '菜单修改', '102', '3',  '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit',           '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1016', '菜单删除', '102', '4',  '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove',         '#', 'admin', sysdate, '', null, '');\n-- 部门管理按钮\ninsert into sys_menu values('1017', '部门查询', '103', '1',  '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query',          '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1018', '部门新增', '103', '2',  '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add',            '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1019', '部门修改', '103', '3',  '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit',           '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1020', '部门删除', '103', '4',  '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove',         '#', 'admin', sysdate, '', null, '');\n-- 岗位管理按钮\ninsert into sys_menu values('1021', '岗位查询', '104', '1',  '', '', '', 1, 0, 'F', '0', '0', 'system:post:query',          '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1022', '岗位新增', '104', '2',  '', '', '', 1, 0, 'F', '0', '0', 'system:post:add',            '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1023', '岗位修改', '104', '3',  '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit',           '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1024', '岗位删除', '104', '4',  '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove',         '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1025', '岗位导出', '104', '5',  '', '', '', 1, 0, 'F', '0', '0', 'system:post:export',         '#', 'admin', sysdate, '', null, '');\n-- 字典管理按钮\ninsert into sys_menu values('1026', '字典查询', '105', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:query',          '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1027', '字典新增', '105', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:add',            '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1028', '字典修改', '105', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit',           '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1029', '字典删除', '105', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove',         '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1030', '字典导出', '105', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:export',         '#', 'admin', sysdate, '', null, '');\n-- 参数设置按钮\ninsert into sys_menu values('1031', '参数查询', '106', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:query',        '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1032', '参数新增', '106', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:add',          '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1033', '参数修改', '106', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:edit',         '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1034', '参数删除', '106', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:remove',       '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1035', '参数导出', '106', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:export',       '#', 'admin', sysdate, '', null, '');\n-- 通知公告按钮\ninsert into sys_menu values('1036', '公告查询', '107', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:query',        '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1037', '公告新增', '107', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:add',          '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1038', '公告修改', '107', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit',         '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1039', '公告删除', '107', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove',       '#', 'admin', sysdate, '', null, '');\n-- 操作日志按钮\ninsert into sys_menu values('1040', '操作查询', '500', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query',      '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1041', '操作删除', '500', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove',     '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export',     '#', 'admin', sysdate, '', null, '');\n-- 登录日志按钮\ninsert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query',   '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove',  '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export',  '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock',  '#', 'admin', sysdate, '', null, '');\n-- 在线用户按钮\ninsert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query',       '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 'admin', sysdate, '', null, '');\n-- 代码生成按钮\ninsert into sys_menu values('1055', '生成查询', '115', '1', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query',             '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1056', '生成修改', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit',              '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1057', '生成删除', '115', '3', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove',            '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1058', '导入代码', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import',            '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1059', '预览代码', '115', '4', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview',           '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1060', '生成代码', '115', '5', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code',              '#', 'admin', sysdate, '', null, '');\n-- oss相关按钮\ninsert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:query',        '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:upload',       '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:download',     '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:remove',       '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1604', '配置添加', '118', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:add',          '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu values('1605', '配置编辑', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:edit',         '#', 'admin', sysdate, '', null, '');\n\n\n-- ----------------------------\n-- 6、用户和角色关联表  用户N-1角色\n-- ----------------------------\ncreate table sys_user_role (\n  user_id  number(20)  not null,\n  role_id  number(20)  not null\n);\n\nalter table sys_user_role add constraint pk_sys_user_role primary key (user_id, role_id);\n\ncomment on table  sys_user_role              is '用户和角色关联表';\ncomment on column sys_user_role.user_id      is '用户ID';\ncomment on column sys_user_role.role_id      is '角色ID';\n\n-- ----------------------------\n-- 初始化-用户和角色关联表数据\n-- ----------------------------\ninsert into sys_user_role values ('1', '1');\ninsert into sys_user_role values ('2', '2');\n\n\n-- ----------------------------\n-- 7、角色和菜单关联表  角色1-N菜单\n-- ----------------------------\ncreate table sys_role_menu (\n  role_id  number(20)  not null,\n  menu_id  number(20)  not null\n);\n\nalter table sys_role_menu add constraint pk_sys_role_menu primary key (role_id, menu_id);\n\ncomment on table  sys_role_menu              is '角色和菜单关联表';\ncomment on column sys_role_menu.role_id      is '角色ID';\ncomment on column sys_role_menu.menu_id      is '菜单ID';\n\n-- ----------------------------\n-- 初始化-角色和菜单关联表数据\n-- ----------------------------\ninsert into sys_role_menu values ('2', '1');\ninsert into sys_role_menu values ('2', '2');\ninsert into sys_role_menu values ('2', '3');\ninsert into sys_role_menu values ('2', '4');\ninsert into sys_role_menu values ('2', '100');\ninsert into sys_role_menu values ('2', '101');\ninsert into sys_role_menu values ('2', '102');\ninsert into sys_role_menu values ('2', '103');\ninsert into sys_role_menu values ('2', '104');\ninsert into sys_role_menu values ('2', '105');\ninsert into sys_role_menu values ('2', '106');\ninsert into sys_role_menu values ('2', '107');\ninsert into sys_role_menu values ('2', '108');\ninsert into sys_role_menu values ('2', '109');\ninsert into sys_role_menu values ('2', '110');\ninsert into sys_role_menu values ('2', '111');\ninsert into sys_role_menu values ('2', '112');\ninsert into sys_role_menu values ('2', '113');\ninsert into sys_role_menu values ('2', '114');\ninsert into sys_role_menu values ('2', '115');\ninsert into sys_role_menu values ('2', '116');\ninsert into sys_role_menu values ('2', '500');\ninsert into sys_role_menu values ('2', '501');\ninsert into sys_role_menu values ('2', '1000');\ninsert into sys_role_menu values ('2', '1001');\ninsert into sys_role_menu values ('2', '1002');\ninsert into sys_role_menu values ('2', '1003');\ninsert into sys_role_menu values ('2', '1004');\ninsert into sys_role_menu values ('2', '1005');\ninsert into sys_role_menu values ('2', '1006');\ninsert into sys_role_menu values ('2', '1007');\ninsert into sys_role_menu values ('2', '1008');\ninsert into sys_role_menu values ('2', '1009');\ninsert into sys_role_menu values ('2', '1010');\ninsert into sys_role_menu values ('2', '1011');\ninsert into sys_role_menu values ('2', '1012');\ninsert into sys_role_menu values ('2', '1013');\ninsert into sys_role_menu values ('2', '1014');\ninsert into sys_role_menu values ('2', '1015');\ninsert into sys_role_menu values ('2', '1016');\ninsert into sys_role_menu values ('2', '1017');\ninsert into sys_role_menu values ('2', '1018');\ninsert into sys_role_menu values ('2', '1019');\ninsert into sys_role_menu values ('2', '1020');\ninsert into sys_role_menu values ('2', '1021');\ninsert into sys_role_menu values ('2', '1022');\ninsert into sys_role_menu values ('2', '1023');\ninsert into sys_role_menu values ('2', '1024');\ninsert into sys_role_menu values ('2', '1025');\ninsert into sys_role_menu values ('2', '1026');\ninsert into sys_role_menu values ('2', '1027');\ninsert into sys_role_menu values ('2', '1028');\ninsert into sys_role_menu values ('2', '1029');\ninsert into sys_role_menu values ('2', '1030');\ninsert into sys_role_menu values ('2', '1031');\ninsert into sys_role_menu values ('2', '1032');\ninsert into sys_role_menu values ('2', '1033');\ninsert into sys_role_menu values ('2', '1034');\ninsert into sys_role_menu values ('2', '1035');\ninsert into sys_role_menu values ('2', '1036');\ninsert into sys_role_menu values ('2', '1037');\ninsert into sys_role_menu values ('2', '1038');\ninsert into sys_role_menu values ('2', '1039');\ninsert into sys_role_menu values ('2', '1040');\ninsert into sys_role_menu values ('2', '1041');\ninsert into sys_role_menu values ('2', '1042');\ninsert into sys_role_menu values ('2', '1043');\ninsert into sys_role_menu values ('2', '1044');\ninsert into sys_role_menu values ('2', '1045');\ninsert into sys_role_menu values ('2', '1050');\ninsert into sys_role_menu values ('2', '1046');\ninsert into sys_role_menu values ('2', '1047');\ninsert into sys_role_menu values ('2', '1048');\ninsert into sys_role_menu values ('2', '1055');\ninsert into sys_role_menu values ('2', '1056');\ninsert into sys_role_menu values ('2', '1057');\ninsert into sys_role_menu values ('2', '1058');\ninsert into sys_role_menu values ('2', '1059');\ninsert into sys_role_menu values ('2', '1060');\n\n-- ----------------------------\n-- 8、角色和部门关联表  角色1-N部门\n-- ----------------------------\ncreate table sys_role_dept (\n  role_id  number(20)  not null,\n  dept_id  number(20)  not null\n);\n\nalter table sys_role_dept add constraint pk_sys_role_dept primary key (role_id, dept_id);\n\ncomment on table  sys_role_dept              is '角色和部门关联表';\ncomment on column sys_role_dept.role_id      is '角色ID';\ncomment on column sys_role_dept.dept_id      is '部门ID';\n\n-- ----------------------------\n-- 初始化-角色和部门关联表数据\n-- ----------------------------\ninsert into sys_role_dept values ('2', '100');\ninsert into sys_role_dept values ('2', '101');\ninsert into sys_role_dept values ('2', '105');\n\n\n-- ----------------------------\n-- 9、用户与岗位关联表  用户1-N岗位\n-- ----------------------------\ncreate table sys_user_post (\n  user_id number(20)  not null,\n  post_id number(20)  not null\n);\n\nalter table sys_user_post add constraint pk_sys_user_post primary key (user_id, post_id);\n\ncomment on table  sys_user_post              is '用户与岗位关联表';\ncomment on column sys_user_post.user_id      is '用户ID';\ncomment on column sys_user_post.post_id      is '岗位ID';\n\n-- ----------------------------\n-- 初始化-用户与岗位关联表数据\n-- ----------------------------\ninsert into sys_user_post values ('1', '1');\ninsert into sys_user_post values ('2', '2');\n\n\n-- ----------------------------\n-- 10、操作日志记录\n-- ----------------------------\ncreate table sys_oper_log (\n  oper_id           number(20)      not null ,\n  title             varchar2(50)    default '',\n  business_type     number(2)       default 0,\n  method            varchar2(100)   default '',\n  request_method    varchar(10)     default '',\n  operator_type     number(1)       default 0,\n  oper_name         varchar2(50)    default '',\n  dept_name         varchar2(50)    default '',\n  oper_url          varchar2(255)   default '',\n  oper_ip           varchar2(128)   default '',\n  oper_location     varchar2(255)   default '',\n  oper_param        varchar2(2100)  default '',\n  json_result       varchar2(2100)  default '',\n  status            number(1)       default 0,\n  error_msg         varchar2(2100)  default '' ,\n  oper_time         date\n);\n\nalter table sys_oper_log add constraint pk_sys_oper_log primary key (oper_id);\ncreate index idx_sys_oper_log_bt on sys_oper_log (business_type);\ncreate index idx_sys_oper_log_s on sys_oper_log (status);\ncreate index idx_sys_oper_log_ot on sys_oper_log (oper_time);\n\ncomment on table  sys_oper_log                is '操作日志记录';\ncomment on column sys_oper_log.oper_id        is '日志主键';\ncomment on column sys_oper_log.title          is '模块标题';\ncomment on column sys_oper_log.business_type  is '业务类型（0其它 1新增 2修改 3删除）';\ncomment on column sys_oper_log.method         is '方法名称';\ncomment on column sys_oper_log.request_method is '请求方式';\ncomment on column sys_oper_log.operator_type  is '操作类别（0其它 1后台用户 2手机端用户）';\ncomment on column sys_oper_log.oper_name      is '操作人员';\ncomment on column sys_oper_log.dept_name      is '部门名称';\ncomment on column sys_oper_log.oper_url       is '请求URL';\ncomment on column sys_oper_log.oper_ip        is '主机地址';\ncomment on column sys_oper_log.oper_location  is '操作地点';\ncomment on column sys_oper_log.oper_param     is '请求参数';\ncomment on column sys_oper_log.json_result    is '返回参数';\ncomment on column sys_oper_log.status         is '操作状态（0正常 1异常）';\ncomment on column sys_oper_log.error_msg      is '错误消息';\ncomment on column sys_oper_log.oper_time      is '操作时间';\n\n\n-- ----------------------------\n-- 11、字典类型表\n-- ----------------------------\ncreate table sys_dict_type (\n  dict_id           number(20)      not null,\n  dict_name         varchar2(100)   default '',\n  dict_type         varchar2(100)   default '',\n  status            char(1)         default '0',\n  create_by         varchar2(64)    default '',\n  create_time       date,\n  update_by         varchar2(64)    default '',\n  update_time       date,\n  remark            varchar2(500)   default null\n);\n\nalter table sys_dict_type add constraint pk_sys_dict_type primary key (dict_id);\ncreate unique index sys_dict_type_index1 on sys_dict_type (dict_type);\n\ncomment on table  sys_dict_type               is '字典类型表';\ncomment on column sys_dict_type.dict_id       is '字典主键';\ncomment on column sys_dict_type.dict_name     is '字典名称';\ncomment on column sys_dict_type.dict_type     is '字典类型';\ncomment on column sys_dict_type.status        is '状态（0正常 1停用）';\ncomment on column sys_dict_type.create_by     is '创建者';\ncomment on column sys_dict_type.create_time   is '创建时间';\ncomment on column sys_dict_type.update_by     is '更新者';\ncomment on column sys_dict_type.update_time   is '更新时间';\ncomment on column sys_dict_type.remark        is '备注';\n\ninsert into sys_dict_type values(1,  '用户性别', 'sys_user_sex',        '0', 'admin', sysdate, '', null, '用户性别列表');\ninsert into sys_dict_type values(2,  '菜单状态', 'sys_show_hide',       '0', 'admin', sysdate, '', null, '菜单状态列表');\ninsert into sys_dict_type values(3,  '系统开关', 'sys_normal_disable',  '0', 'admin', sysdate, '', null, '系统开关列表');\ninsert into sys_dict_type values(6,  '系统是否', 'sys_yes_no',          '0', 'admin', sysdate, '', null, '系统是否列表');\ninsert into sys_dict_type values(7,  '通知类型', 'sys_notice_type',     '0', 'admin', sysdate, '', null, '通知类型列表');\ninsert into sys_dict_type values(8,  '通知状态', 'sys_notice_status',   '0', 'admin', sysdate, '', null, '通知状态列表');\ninsert into sys_dict_type values(9,  '操作类型', 'sys_oper_type',       '0', 'admin', sysdate, '', null, '操作类型列表');\ninsert into sys_dict_type values(10, '系统状态', 'sys_common_status',   '0', 'admin', sysdate, '', null, '登录状态列表');\n\n\n-- ----------------------------\n-- 12、字典数据表\n-- ----------------------------\ncreate table sys_dict_data (\n  dict_code        number(20)      not null,\n  dict_sort        number(4)       default 0,\n  dict_label       varchar2(100)   default '',\n  dict_value       varchar2(100)   default '',\n  dict_type        varchar2(100)   default '',\n  css_class        varchar2(100)   default null,\n  list_class       varchar2(100)   default null,\n  is_default       char(1)         default 'N',\n  status           char(1)         default '0',\n  create_by        varchar2(64)    default '',\n  create_time      date,\n  update_by        varchar2(64)    default '',\n  update_time      date,\n  remark           varchar2(500)   default null\n);\n\nalter table sys_dict_data add constraint pk_sys_dict_data primary key (dict_code);\n\ncomment on table  sys_dict_data               is '字典数据表';\ncomment on column sys_dict_data.dict_code     is '字典主键';\ncomment on column sys_dict_data.dict_sort     is '字典排序';\ncomment on column sys_dict_data.dict_label    is '字典标签';\ncomment on column sys_dict_data.dict_value    is '字典键值';\ncomment on column sys_dict_data.dict_type     is '字典类型';\ncomment on column sys_dict_data.css_class     is '样式属性（其他样式扩展）';\ncomment on column sys_dict_data.list_class    is '表格回显样式';\ncomment on column sys_dict_data.is_default    is '是否默认（Y是 N否）';\ncomment on column sys_dict_data.status        is '状态（0正常 1停用）';\ncomment on column sys_dict_data.create_by     is '创建者';\ncomment on column sys_dict_data.create_time   is '创建时间';\ncomment on column sys_dict_data.update_by     is '更新者';\ncomment on column sys_dict_data.update_time   is '更新时间';\ncomment on column sys_dict_data.remark        is '备注';\n\ninsert into sys_dict_data values(1,  1,  '男',       '0',       'sys_user_sex',        '',   '',        'Y', '0', 'admin', sysdate, '', null, '性别男');\ninsert into sys_dict_data values(2,  2,  '女',       '1',       'sys_user_sex',        '',   '',        'N', '0', 'admin', sysdate, '', null, '性别女');\ninsert into sys_dict_data values(3,  3,  '未知',     '2',       'sys_user_sex',        '',   '',        'N', '0', 'admin', sysdate, '', null, '性别未知');\ninsert into sys_dict_data values(4,  1,  '显示',     '0',       'sys_show_hide',       '',   'primary', 'Y', '0', 'admin', sysdate, '', null, '显示菜单');\ninsert into sys_dict_data values(5,  2,  '隐藏',     '1',       'sys_show_hide',       '',   'danger',  'N', '0', 'admin', sysdate, '', null, '隐藏菜单');\ninsert into sys_dict_data values(6,  1,  '正常',     '0',       'sys_normal_disable',  '',   'primary', 'Y', '0', 'admin', sysdate, '', null, '正常状态');\ninsert into sys_dict_data values(7,  2,  '停用',     '1',       'sys_normal_disable',  '',   'danger',  'N', '0', 'admin', sysdate, '', null, '停用状态');\ninsert into sys_dict_data values(12, 1,  '是',       'Y',       'sys_yes_no',          '',   'primary', 'Y', '0', 'admin', sysdate, '', null, '系统默认是');\ninsert into sys_dict_data values(13, 2,  '否',       'N',       'sys_yes_no',          '',   'danger',  'N', '0', 'admin', sysdate, '', null, '系统默认否');\ninsert into sys_dict_data values(14, 1,  '通知',     '1',       'sys_notice_type',     '',   'warning', 'Y', '0', 'admin', sysdate, '', null, '通知');\ninsert into sys_dict_data values(15, 2,  '公告',     '2',       'sys_notice_type',     '',   'success', 'N', '0', 'admin', sysdate, '', null, '公告');\ninsert into sys_dict_data values(16, 1,  '正常',     '0',       'sys_notice_status',   '',   'primary', 'Y', '0', 'admin', sysdate, '', null, '正常状态');\ninsert into sys_dict_data values(17, 2,  '关闭',     '1',       'sys_notice_status',   '',   'danger',  'N', '0', 'admin', sysdate, '', null, '关闭状态');\ninsert into sys_dict_data values(29, 99, '其他',     '0',       'sys_oper_type',       '',   'info',    'N', '0', 'admin', sysdate, '', null, '其他操作');\ninsert into sys_dict_data values(18, 1,  '新增',     '1',       'sys_oper_type',       '',   'info',    'N', '0', 'admin', sysdate, '', null, '新增操作');\ninsert into sys_dict_data values(19, 2,  '修改',     '2',       'sys_oper_type',       '',   'info',    'N', '0', 'admin', sysdate, '', null, '修改操作');\ninsert into sys_dict_data values(20, 3,  '删除',     '3',       'sys_oper_type',       '',   'danger',  'N', '0', 'admin', sysdate, '', null, '删除操作');\ninsert into sys_dict_data values(21, 4,  '授权',     '4',       'sys_oper_type',       '',   'primary', 'N', '0', 'admin', sysdate, '', null, '授权操作');\ninsert into sys_dict_data values(22, 5,  '导出',     '5',       'sys_oper_type',       '',   'warning', 'N', '0', 'admin', sysdate, '', null, '导出操作');\ninsert into sys_dict_data values(23, 6,  '导入',     '6',       'sys_oper_type',       '',   'warning', 'N', '0', 'admin', sysdate, '', null, '导入操作');\ninsert into sys_dict_data values(24, 7,  '强退',     '7',       'sys_oper_type',       '',   'danger',  'N', '0', 'admin', sysdate, '', null, '强退操作');\ninsert into sys_dict_data values(25, 8,  '生成代码', '8',       'sys_oper_type',       '',   'warning', 'N', '0', 'admin', sysdate, '', null, '生成操作');\ninsert into sys_dict_data values(26, 9,  '清空数据', '9',       'sys_oper_type',       '',   'danger',  'N', '0', 'admin', sysdate, '', null, '清空操作');\ninsert into sys_dict_data values(27, 1,  '成功',     '0',       'sys_common_status',   '',   'primary', 'N', '0', 'admin', sysdate, '', null, '正常状态');\ninsert into sys_dict_data values(28, 2,  '失败',     '1',       'sys_common_status',   '',   'danger',  'N', '0', 'admin', sysdate, '', null, '停用状态');\n\n\n-- ----------------------------\n-- 13、参数配置表\n-- ----------------------------\ncreate table sys_config (\n  config_id         number(20)     not null,\n  config_name       varchar2(100)  default '',\n  config_key        varchar2(100)  default '',\n  config_value      varchar2(100)  default '',\n  config_type       char(1)        default 'N',\n  create_by         varchar2(64)   default '',\n  create_time       date,\n  update_by         varchar2(64)   default '',\n  update_time       date,\n  remark            varchar2(500)  default null\n);\nalter table sys_config add constraint pk_sys_config primary key (config_id);\n\ncomment on table  sys_config               is '参数配置表';\ncomment on column sys_config.config_id     is '参数主键';\ncomment on column sys_config.config_name   is '参数名称';\ncomment on column sys_config.config_key    is '参数键名';\ncomment on column sys_config.config_value  is '参数键值';\ncomment on column sys_config.config_type   is '系统内置（Y是 N否）';\ncomment on column sys_config.create_by     is '创建者';\ncomment on column sys_config.create_time   is '创建时间';\ncomment on column sys_config.update_by     is '更新者';\ncomment on column sys_config.update_time   is '更新时间';\ncomment on column sys_config.remark        is '备注';\n\ninsert into sys_config values(1, '主框架页-默认皮肤样式名称',      'sys.index.skinName',            'skin-blue',     'Y', 'admin', sysdate, '', null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' );\ninsert into sys_config values(2, '用户管理-账号初始密码',         'sys.user.initPassword',         '123456',        'Y', 'admin', sysdate, '', null, '初始化密码 123456' );\ninsert into sys_config values(3, '主框架页-侧边栏主题',           'sys.index.sideTheme',           'theme-dark',    'Y', 'admin', sysdate, '', null, '深色主题theme-dark，浅色主题theme-light' );\ninsert into sys_config values(4, '账号自助-验证码开关',           'sys.account.captchaEnabled',    'true',          'Y', 'admin', sysdate, '', null, '是否开启验证码功能（true开启，false关闭）');\ninsert into sys_config values(5, '账号自助-是否开启用户注册功能',   'sys.account.registerUser',      'false',         'Y', 'admin', sysdate, '', null, '是否开启注册用户功能（true开启，false关闭）');\ninsert into sys_config values(11, 'OSS预览列表资源开关',          'sys.oss.previewListResource',   'true',          'Y', 'admin', sysdate, '', null, 'true:开启, false:关闭');\n\n\n-- ----------------------------\n-- 14、系统访问记录\n-- ----------------------------\ncreate table sys_logininfor (\n  info_id         number(20)     not null,\n  user_name       varchar2(50)   default '',\n  ipaddr          varchar2(128)  default '',\n  login_location  varchar2(255)  default '',\n  browser         varchar2(50)   default '',\n  os              varchar2(50)   default '',\n  status          char(1)        default '0',\n  msg             varchar2(255)  default '',\n  login_time      date\n);\n\nalter table sys_logininfor add constraint pk_sys_logininfor primary key (info_id);\ncreate index idx_sys_logininfor_s on sys_logininfor (status);\ncreate index idx_sys_logininfor_lt on sys_logininfor (login_time);\n\ncomment on table  sys_logininfor                is '系统访问记录';\ncomment on column sys_logininfor.info_id        is '访问ID';\ncomment on column sys_logininfor.user_name      is '登录账号';\ncomment on column sys_logininfor.ipaddr         is '登录IP地址';\ncomment on column sys_logininfor.login_location is '登录地点';\ncomment on column sys_logininfor.browser        is '浏览器类型';\ncomment on column sys_logininfor.os             is '操作系统';\ncomment on column sys_logininfor.status         is '登录状态（0成功 1失败）';\ncomment on column sys_logininfor.msg            is '提示消息';\ncomment on column sys_logininfor.login_time     is '访问时间';\n\n\n-- ----------------------------\n-- 17、通知公告表\n-- ----------------------------\ncreate table sys_notice (\n  notice_id         number(20)      not null,\n  notice_title      varchar2(50)    not null,\n  notice_type       char(1)         not null,\n  notice_content    clob            default null,\n  status            char(1)         default '0',\n  create_by         varchar2(64)    default '',\n  create_time       date,\n  update_by         varchar2(64)    default '',\n  update_time       date,\n  remark            varchar2(255)   default null\n);\n\nalter table sys_notice add constraint pk_sys_notice primary key (notice_id);\n\ncomment on table  sys_notice                   is '通知公告表';\ncomment on column sys_notice.notice_id         is '公告主键';\ncomment on column sys_notice.notice_title      is '公告标题';\ncomment on column sys_notice.notice_type       is '公告类型（1通知 2公告）';\ncomment on column sys_notice.notice_content    is '公告内容';\ncomment on column sys_notice.status            is '公告状态（0正常 1关闭）';\ncomment on column sys_notice.create_by         is '创建者';\ncomment on column sys_notice.create_time       is '创建时间';\ncomment on column sys_notice.update_by         is '更新者';\ncomment on column sys_notice.update_time       is '更新时间';\ncomment on column sys_notice.remark            is '备注';\n\n-- ----------------------------\n-- 初始化-公告信息表数据\n-- ----------------------------\ninsert into sys_notice values('1', '温馨提醒：2018-07-01 新版本发布啦', '2', '新版本内容', '0', 'admin', sysdate, '', null, '管理员');\ninsert into sys_notice values('2', '维护通知：2018-07-01 系统凌晨维护', '1', '维护内容',   '0', 'admin', sysdate, '', null, '管理员');\n\n\n-- ----------------------------\n-- 18、代码生成业务表\n-- ----------------------------\ncreate table gen_table (\n  table_id          number(20)       not null,\n  table_name        varchar2(200)    default '',\n  table_comment     varchar2(500)    default '',\n  sub_table_name    varchar(64)      default null,\n  sub_table_fk_name varchar(64)      default null,\n  class_name        varchar2(100)    default '',\n  tpl_category      varchar2(200)    default 'crud',\n  package_name      varchar2(100),\n  module_name       varchar2(30),\n  business_name     varchar2(30),\n  function_name     varchar2(50),\n  function_author   varchar2(50),\n  gen_type          char(1)          default '0',\n  gen_path          varchar2(200)    default '/',\n  options           varchar2(1000),\n  create_by         varchar2(64)     default '',\n  create_time       date,\n  update_by         varchar2(64)     default '',\n  update_time       date,\n  remark            varchar2(500)    default null\n);\n\nalter table gen_table add constraint pk_gen_table primary key (table_id);\n\ncomment on table  gen_table                   is '代码生成业务表';\ncomment on column gen_table.table_id          is '编号';\ncomment on column gen_table.table_name        is '表名称';\ncomment on column gen_table.table_comment     is '表描述';\ncomment on column gen_table.sub_table_name    is '关联子表的表名';\ncomment on column gen_table.sub_table_fk_name is '子表关联的外键名';\ncomment on column gen_table.class_name        is '实体类名称';\ncomment on column gen_table.tpl_category      is '使用的模板（crud单表操作 tree树表操作）';\ncomment on column gen_table.package_name      is '生成包路径';\ncomment on column gen_table.module_name       is '生成模块名';\ncomment on column gen_table.business_name     is '生成业务名';\ncomment on column gen_table.function_name     is '生成功能名';\ncomment on column gen_table.function_author   is '生成功能作者';\ncomment on column gen_table.gen_type          is '生成代码方式（0zip压缩包 1自定义路径）';\ncomment on column gen_table.gen_path          is '生成路径（不填默认项目路径）';\ncomment on column gen_table.options           is '其它生成选项';\ncomment on column gen_table.create_by         is '创建者';\ncomment on column gen_table.create_time       is '创建时间';\ncomment on column gen_table.update_by         is '更新者';\ncomment on column gen_table.update_time       is '更新时间';\ncomment on column gen_table.remark            is '备注';\n\n\n-- ----------------------------\n-- 19、代码生成业务表字段\n-- ----------------------------\ncreate table gen_table_column (\n  column_id         number(20)      not null,\n  table_id          number(20),\n  column_name       varchar2(200),\n  column_comment    varchar2(500),\n  column_type       varchar2(100),\n  java_type         varchar2(500),\n  java_field        varchar2(200),\n  is_pk             char(1),\n  is_increment      char(1),\n  is_required       char(1),\n  is_insert         char(1),\n  is_edit           char(1),\n  is_list           char(1),\n  is_query          char(1),\n  query_type        varchar(200)    default 'EQ',\n  html_type         varchar(200),\n  dict_type         varchar(200)    default '',\n  sort              number(4),\n  create_by         varchar(64)     default '',\n  create_time       date ,\n  update_by         varchar(64)     default '',\n  update_time       date\n);\n\nalter table gen_table_column add constraint pk_gen_table_column primary key (column_id);\n\ncomment on table  gen_table_column                is '代码生成业务表字段';\ncomment on column gen_table_column.column_id      is '编号';\ncomment on column gen_table_column.table_id       is '归属表编号';\ncomment on column gen_table_column.column_name    is '列名称';\ncomment on column gen_table_column.column_comment is '列描述';\ncomment on column gen_table_column.column_type    is '列类型';\ncomment on column gen_table_column.java_type      is 'JAVA类型';\ncomment on column gen_table_column.java_field     is 'JAVA字段名';\ncomment on column gen_table_column.is_pk          is '是否主键（1是）';\ncomment on column gen_table_column.is_increment   is '是否自增（1是）';\ncomment on column gen_table_column.is_required    is '是否必填（1是）';\ncomment on column gen_table_column.is_insert      is '是否为插入字段（1是）';\ncomment on column gen_table_column.is_edit        is '是否编辑字段（1是）';\ncomment on column gen_table_column.is_list        is '是否列表字段（1是）';\ncomment on column gen_table_column.is_query       is '是否查询字段（1是）';\ncomment on column gen_table_column.query_type     is '查询方式（等于、不等于、大于、小于、范围）';\ncomment on column gen_table_column.html_type      is '显示类型（文本框、文本域、下拉框、复选框、单选框、日期控件）';\ncomment on column gen_table_column.dict_type      is '字典类型';\ncomment on column gen_table_column.sort           is '排序';\ncomment on column gen_table_column.create_by      is '创建者';\ncomment on column gen_table_column.create_time    is '创建时间';\ncomment on column gen_table_column.update_by      is '更新者';\ncomment on column gen_table_column.update_time    is '更新时间';\n\n\n-- ----------------------------\n-- OSS对象存储表\n-- ----------------------------\ncreate table sys_oss (\n  oss_id          number(20)   not null,\n  file_name       varchar(255)  not null,\n  original_name   varchar(255)  not null,\n  file_suffix     varchar(10)  not null,\n  url             varchar(500) not null,\n  service         varchar(20)  default 'minio' not null,\n  create_by       varchar(64)  default '',\n  create_time     date,\n  update_by       varchar(64)  default '',\n  update_time     date\n);\n\nalter table sys_oss add constraint pk_sys_oss primary key (oss_id);\n\ncomment on table sys_oss is 'OSS对象存储表';\ncomment on column sys_oss.oss_id is '对象存储主键';\ncomment on column sys_oss.file_name is '文件名';\ncomment on column sys_oss.original_name is '原名';\ncomment on column sys_oss.file_suffix is '文件后缀名';\ncomment on column sys_oss.url is 'URL地址';\ncomment on column sys_oss.service is '服务商';\ncomment on column sys_oss.create_time is '创建时间';\ncomment on column sys_oss.create_by is '上传者';\ncomment on column sys_oss.update_time is '更新时间';\ncomment on column sys_oss.update_by is '更新者';\n\n\n-- ----------------------------\n-- OSS对象存储动态配置表\n-- ----------------------------\ncreate table sys_oss_config (\n  oss_config_id   number(20)    not null,\n  config_key      varchar(20)   not null,\n  access_key      varchar(255)  default '',\n  secret_key      varchar(255)  default '',\n  bucket_name     varchar(255)  default '',\n  prefix          varchar(255)  default '',\n  endpoint        varchar(255)  default '',\n  domain          varchar(255)  default '',\n  is_https        char(1)       default 'N',\n  region          varchar(255)  default '',\n  access_policy   char(1)       default '1' not null,\n  status          char(1)       default '1',\n  ext1            varchar(255)  default '',\n  create_by       varchar(64)   default '',\n  remark          varchar(500)  default null,\n  create_time     date,\n  update_by       varchar(64)   default '',\n  update_time     date\n);\n\nalter table sys_oss_config add constraint pk_sys_oss_config primary key (oss_config_id);\n\ncomment on table sys_oss_config is '对象存储配置表';\ncomment on column sys_oss_config.oss_config_id is '主建';\ncomment on column sys_oss_config.config_key is '配置key';\ncomment on column sys_oss_config.access_key is 'accesskey';\ncomment on column sys_oss_config.secret_key is '秘钥';\ncomment on column sys_oss_config.bucket_name is '桶名称';\ncomment on column sys_oss_config.prefix is '前缀';\ncomment on column sys_oss_config.endpoint is '访问站点';\ncomment on column sys_oss_config.domain is '自定义域名';\ncomment on column sys_oss_config.is_https is '是否https（Y=是,N=否）';\ncomment on column sys_oss_config.region is '域';\ncomment on column sys_oss_config.access_policy is '桶权限类型(0=private 1=public 2=custom)';\ncomment on column sys_oss_config.status is '是否默认（0=是,1=否）';\ncomment on column sys_oss_config.ext1 is '扩展字段';\ncomment on column sys_oss_config.remark is '备注';\ncomment on column sys_oss_config.create_by is '创建者';\ncomment on column sys_oss_config.create_time is '创建时间';\ncomment on column sys_oss_config.update_by is '更新者';\ncomment on column sys_oss_config.update_time is '更新时间';\n\ninsert into sys_oss_config values (1, 'minio',  'ruoyi',            'ruoyi123',        'ruoyi',             '', '127.0.0.1:9000',                '','N', '',            '1', '0', '', NULL, 'admin', sysdate, 'admin', sysdate);\ninsert into sys_oss_config values (2, 'qiniu',  'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi',             '', 's3-cn-north-1.qiniucs.com',     '','N', '',            '1', '1', '', NULL, 'admin', sysdate, 'admin', sysdate);\ninsert into sys_oss_config values (3, 'aliyun', 'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi',             '', 'oss-cn-beijing.aliyuncs.com',   '','N', '',            '1', '1', '', NULL, 'admin', sysdate, 'admin', sysdate);\ninsert into sys_oss_config values (4, 'qcloud', 'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi-1250000000',  '', 'cos.ap-beijing.myqcloud.com',   '','N', 'ap-beijing',  '1', '1', '', NULL, 'admin', sysdate, 'admin', sysdate);\ninsert into sys_oss_config values (5, 'image',  'ruoyi',            'ruoyi123',        'ruoyi',             'image', '127.0.0.1:9000',           '','N', '',            '1', '1', '', NULL, 'admin', sysdate, 'admin', sysdate);\n\n\n-- ----------------------------\n-- 钩子 ，用于session连接之后 自动设置默认的date类型格式化 简化时间查询\n-- 如需设置其它配置 可在此钩子内任意增加处理语句\n-- 例如： SELECT * FROM sys_user WHERE create_time BETWEEN '2022-03-01 00:00:00' AND '2022-04-01 00:00:00'\n-- ----------------------------\ncreate or replace trigger login_trg\nafter logon on database\nbegin\nexecute immediate 'alter session set nls_date_format=''YYYY-MM-DD HH24:MI:SS''';\nend;\n"
  },
  {
    "path": "script/sql/oracle/oracle_test.sql",
    "content": "create table test_demo (\n    id          number(20)      not null,\n    dept_id     number(20)      default null,\n    user_id     number(20)      default null,\n    order_num   number(10)      default 0,\n    test_key    varchar2(255)   default null,\n    value       varchar2(255)   default null,\n    version     number(10)      default 0,\n    create_time date,\n    create_by   varchar2(64)    default null,\n    update_time date,\n    update_by   varchar2(64)    default null,\n    del_flag    number(2)       default 0\n);\n\nalter table test_demo add constraint pk_test_demo primary key (id);\n\ncomment on table  test_demo              is '测试单表';\ncomment on column test_demo.id           is '主键';\ncomment on column test_demo.dept_id      is '部门id';\ncomment on column test_demo.user_id      is '用户id';\ncomment on column test_demo.order_num    is '排序号';\ncomment on column test_demo.test_key     is 'key键';\ncomment on column test_demo.value        is '值';\ncomment on column test_demo.version      is '版本';\ncomment on column test_demo.create_time  is '创建时间';\ncomment on column test_demo.create_by    is '创建人';\ncomment on column test_demo.update_time  is '更新时间';\ncomment on column test_demo.update_by    is '更新人';\ncomment on column test_demo.del_flag     is '删除标志';\n\ncreate table test_tree (\n    id          number(20)      not null,\n    parent_id   number(20)      default 0,\n    dept_id     number(20)      default null,\n    user_id     number(20)      default null,\n    tree_name   varchar2(255)   default null,\n    version     number(10)      default 0,\n    create_time date,\n    create_by   varchar2(64)    default null,\n    update_time date,\n    update_by   varchar2(64)    default null,\n    del_flag    number(2)       default 0\n);\n\nalter table test_tree add constraint pk_test_tree primary key (id);\n\ncomment on table  test_tree              is '测试树表';\ncomment on column test_tree.id           is '主键';\ncomment on column test_tree.parent_id    is '父id';\ncomment on column test_tree.dept_id      is '部门id';\ncomment on column test_tree.user_id      is '用户id';\ncomment on column test_tree.tree_name    is '值';\ncomment on column test_tree.version      is '版本';\ncomment on column test_tree.create_time  is '创建时间';\ncomment on column test_tree.create_by    is '创建人';\ncomment on column test_tree.update_time  is '更新时间';\ncomment on column test_tree.update_by    is '更新人';\ncomment on column test_tree.del_flag     is '删除标志';\n\ninsert into sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) values (3, 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate, 'admin', sysdate, 'test', sysdate, null);\ninsert into sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) values (4, 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate, 'admin', sysdate, 'test1', sysdate, null);\n\ninsert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (5, '测试菜单', 0, 5, 'demo', null, 1, 0, 'M', '0', '0', null, 'star', 'admin', sysdate, 'admin', sysdate, '');\n\ninsert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1500, '测试单表', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 'admin', sysdate, '', null, '测试单表菜单');\ninsert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1501, '测试单表查询', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1502, '测试单表新增', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1503, '测试单表修改', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1504, '测试单表删除', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1505, '测试单表导出', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 'admin', sysdate, '', null, '');\n\ninsert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1506, '测试树表', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 'admin', sysdate, '', null, '测试树表菜单');\ninsert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1507, '测试树表查询', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1508, '测试树表新增', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1509, '测试树表修改', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1510, '测试树表删除', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 'admin', sysdate, '', null, '');\ninsert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1511, '测试树表导出', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 'admin', sysdate, '', null, '');\n\ninsert into sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) values (3, '本部门及以下', 'test1', 3, '4', 1, 1, '0', '0', 'admin', sysdate, '', null, null);\ninsert into sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) values (4, '仅本人',      'test2', 4, '5', 1, 1, '0', '0', 'admin', sysdate, '', null, null);\n\ninsert into sys_role_menu(role_id, menu_id) values (3, 1);\ninsert into sys_role_menu(role_id, menu_id) values (3, 5);\ninsert into sys_role_menu(role_id, menu_id) values (3, 100);\ninsert into sys_role_menu(role_id, menu_id) values (3, 101);\ninsert into sys_role_menu(role_id, menu_id) values (3, 102);\ninsert into sys_role_menu(role_id, menu_id) values (3, 103);\ninsert into sys_role_menu(role_id, menu_id) values (3, 104);\ninsert into sys_role_menu(role_id, menu_id) values (3, 105);\ninsert into sys_role_menu(role_id, menu_id) values (3, 106);\ninsert into sys_role_menu(role_id, menu_id) values (3, 107);\ninsert into sys_role_menu(role_id, menu_id) values (3, 108);\ninsert into sys_role_menu(role_id, menu_id) values (3, 500);\ninsert into sys_role_menu(role_id, menu_id) values (3, 501);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1001);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1002);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1003);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1004);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1005);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1006);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1007);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1008);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1009);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1010);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1011);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1012);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1013);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1014);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1015);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1016);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1017);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1018);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1019);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1020);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1021);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1022);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1023);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1024);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1025);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1026);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1027);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1028);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1029);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1030);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1031);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1032);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1033);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1034);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1035);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1036);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1037);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1038);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1039);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1040);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1041);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1042);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1043);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1044);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1045);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1500);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1501);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1502);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1503);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1504);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1505);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1506);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1507);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1508);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1509);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1510);\ninsert into sys_role_menu(role_id, menu_id) values (3, 1511);\ninsert into sys_role_menu(role_id, menu_id) values (4, 5);\ninsert into sys_role_menu(role_id, menu_id) values (4, 1500);\ninsert into sys_role_menu(role_id, menu_id) values (4, 1501);\ninsert into sys_role_menu(role_id, menu_id) values (4, 1502);\ninsert into sys_role_menu(role_id, menu_id) values (4, 1503);\ninsert into sys_role_menu(role_id, menu_id) values (4, 1504);\ninsert into sys_role_menu(role_id, menu_id) values (4, 1505);\ninsert into sys_role_menu(role_id, menu_id) values (4, 1506);\ninsert into sys_role_menu(role_id, menu_id) values (4, 1507);\ninsert into sys_role_menu(role_id, menu_id) values (4, 1508);\ninsert into sys_role_menu(role_id, menu_id) values (4, 1509);\ninsert into sys_role_menu(role_id, menu_id) values (4, 1510);\ninsert into sys_role_menu(role_id, menu_id) values (4, 1511);\n\ninsert into sys_user_role(user_id, role_id) values (3, 3);\ninsert into sys_user_role(user_id, role_id) values (4, 4);\n\ninsert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (1, 102, 4, 1, '测试数据权限', '测试', 0, sysdate, 'admin', null, '', 0);\ninsert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (2, 102, 3, 2, '子节点1', '111', 0, sysdate, 'admin', null, '', 0);\ninsert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (3, 102, 3, 3, '子节点2', '222', 0, sysdate, 'admin', null, '', 0);\ninsert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (4, 108, 4, 4, '测试数据', 'demo', 0, sysdate, 'admin', null, '', 0);\ninsert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (5, 108, 3, 13, '子节点11', '1111', 0, sysdate, 'admin', null, '', 0);\ninsert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (6, 108, 3, 12, '子节点22', '2222', 0, sysdate, 'admin', null, '', 0);\ninsert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (7, 108, 3, 11, '子节点33', '3333', 0, sysdate, 'admin', null, '', 0);\ninsert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (8, 108, 3, 10, '子节点44', '4444', 0, sysdate, 'admin', null, '', 0);\ninsert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (9, 108, 3, 9, '子节点55', '5555', 0, sysdate, 'admin', null, '', 0);\ninsert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (10, 108, 3, 8, '子节点66', '6666', 0, sysdate, 'admin', null, '', 0);\ninsert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (11, 108, 3, 7, '子节点77', '7777', 0, sysdate, 'admin', null, '', 0);\ninsert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (12, 108, 3, 6, '子节点88', '8888', 0, sysdate, 'admin', null, '', 0);\ninsert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (13, 108, 3, 5, '子节点99', '9999', 0, sysdate, 'admin', null, '', 0);\n\ninsert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (1, 0, 102, 4, '测试数据权限', 0, sysdate, 'admin', null, '', 0);\ninsert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (2, 1, 102, 3, '子节点1', 0, sysdate, 'admin', null, '', 0);\ninsert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (3, 2, 102, 3, '子节点2', 0, sysdate, 'admin', null, '', 0);\ninsert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (4, 0, 108, 4, '测试树1', 0, sysdate, 'admin', null, '', 0);\ninsert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (5, 4, 108, 3, '子节点11', 0, sysdate, 'admin', null, '', 0);\ninsert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (6, 4, 108, 3, '子节点22', 0, sysdate, 'admin', null, '', 0);\ninsert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (7, 4, 108, 3, '子节点33', 0, sysdate, 'admin', null, '', 0);\ninsert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (8, 5, 108, 3, '子节点44', 0, sysdate, 'admin', null, '', 0);\ninsert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (9, 6, 108, 3, '子节点55', 0, sysdate, 'admin', null, '', 0);\ninsert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (10, 7, 108, 3, '子节点66', 0, sysdate, 'admin', null, '', 0);\ninsert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (11, 7, 108, 3, '子节点77', 0, sysdate, 'admin', null, '', 0);\ninsert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (12, 10, 108, 3, '子节点88', 0, sysdate, 'admin', null, '', 0);\ninsert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (13, 10, 108, 3, '子节点99', 0, sysdate, 'admin', null, '', 0);\n"
  },
  {
    "path": "script/sql/postgres/postgres_ry_vue_4.X.sql",
    "content": "-- ----------------------------\n-- 1、部门表\n-- ----------------------------\ndrop table if exists sys_dept;\ncreate table if not exists sys_dept\n(\n    dept_id     int8,\n    parent_id   int8        default 0,\n    ancestors   varchar(500)default ''::varchar,\n    dept_name   varchar(30) default ''::varchar,\n    order_num   int4        default 0,\n    leader      varchar(20) default null::varchar,\n    phone       varchar(11) default null::varchar,\n    email       varchar(50) default null::varchar,\n    status      char        default '0'::bpchar,\n    del_flag    char        default '0'::bpchar,\n    create_by   varchar(64) default ''::varchar,\n    create_time timestamp,\n    update_by   varchar(64) default ''::varchar,\n    update_time timestamp,\n    constraint \"sys_dept_pk\" primary key (dept_id)\n);\n\ncomment on table sys_dept is '部门表';\ncomment on column sys_dept.dept_id is '部门ID';\ncomment on column sys_dept.parent_id is '父部门ID';\ncomment on column sys_dept.ancestors is '祖级列表';\ncomment on column sys_dept.dept_name is '部门名称';\ncomment on column sys_dept.order_num is '显示顺序';\ncomment on column sys_dept.leader is '负责人';\ncomment on column sys_dept.phone is '联系电话';\ncomment on column sys_dept.email is '邮箱';\ncomment on column sys_dept.status is '部门状态（0正常 1停用）';\ncomment on column sys_dept.del_flag is '删除标志（0代表存在 2代表删除）';\ncomment on column sys_dept.create_by is '创建者';\ncomment on column sys_dept.create_time is '创建时间';\ncomment on column sys_dept.update_by is '更新者';\ncomment on column sys_dept.update_time is '更新时间';\n\n-- ----------------------------\n-- 初始化-部门表数据\n-- ----------------------------\ninsert into sys_dept values(100,  0,   '0',          '若依科技',   0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null);\ninsert into sys_dept values(101,  100, '0,100',      '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null);\ninsert into sys_dept values(102,  100, '0,100',      '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null);\ninsert into sys_dept values(103,  101, '0,100,101',  '研发部门',   1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null);\ninsert into sys_dept values(104,  101, '0,100,101',  '市场部门',   2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null);\ninsert into sys_dept values(105,  101, '0,100,101',  '测试部门',   3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null);\ninsert into sys_dept values(106,  101, '0,100,101',  '财务部门',   4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null);\ninsert into sys_dept values(107,  101, '0,100,101',  '运维部门',   5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null);\ninsert into sys_dept values(108,  102, '0,100,102',  '市场部门',   1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null);\ninsert into sys_dept values(109,  102, '0,100,102',  '财务部门',   2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null);\n\n-- ----------------------------\n-- 2、用户信息表\n-- ----------------------------\ndrop table if exists sys_user;\ncreate table if not exists sys_user\n(\n    user_id     int8,\n    dept_id     int8,\n    user_name   varchar(30) not null,\n    nick_name   varchar(30) not null,\n    user_type   varchar(10)  default 'sys_user'::varchar,\n    email       varchar(50)  default ''::varchar,\n    phonenumber varchar(11)  default ''::varchar,\n    sex         char         default '0'::bpchar,\n    avatar      varchar(100) default ''::varchar,\n    password    varchar(100) default ''::varchar,\n    status      char         default '0'::bpchar,\n    del_flag    char         default '0'::bpchar,\n    login_ip    varchar(128) default ''::varchar,\n    login_date  timestamp,\n    create_by   varchar(64)  default ''::varchar,\n    create_time timestamp,\n    update_by   varchar(64)  default ''::varchar,\n    update_time timestamp,\n    remark      varchar(500) default null::varchar,\n    constraint \"sys_user_pk\" primary key (user_id)\n);\n\ncomment on table sys_user is '用户信息表';\ncomment on column sys_user.user_id is '用户ID';\ncomment on column sys_user.dept_id is '部门ID';\ncomment on column sys_user.user_name is '用户账号';\ncomment on column sys_user.nick_name is '用户昵称';\ncomment on column sys_user.user_type is '用户类型（sys_user系统用户）';\ncomment on column sys_user.email is '用户邮箱';\ncomment on column sys_user.phonenumber is '手机号码';\ncomment on column sys_user.sex is '用户性别（0男 1女 2未知）';\ncomment on column sys_user.avatar is '头像地址';\ncomment on column sys_user.password is '密码';\ncomment on column sys_user.status is '帐号状态（0正常 1停用）';\ncomment on column sys_user.del_flag is '删除标志（0代表存在 2代表删除）';\ncomment on column sys_user.login_ip is '最后登陆IP';\ncomment on column sys_user.login_date is '最后登陆时间';\ncomment on column sys_user.create_by is '创建者';\ncomment on column sys_user.create_time is '创建时间';\ncomment on column sys_user.update_by is '更新者';\ncomment on column sys_user.update_time is '更新时间';\ncomment on column sys_user.remark is '备注';\n\n-- ----------------------------\n-- 初始化-用户信息表数据\n-- ----------------------------\ninsert into sys_user values(1,  103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', now(), 'admin', now(), '', null, '管理员');\ninsert into sys_user values(2,  105, 'ry',    '疯狂的狮子Li', 'sys_user', 'crazyLionLi@qq.com',  '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', now(), 'admin', now(), '', null, '测试员');\n\n\n-- ----------------------------\n-- 3、岗位信息表\n-- ----------------------------\ndrop table if exists sys_post;\ncreate table if not exists sys_post\n(\n    post_id     int8,\n    post_code   varchar(64) not null,\n    post_name   varchar(50) not null,\n    post_sort   int4        not null,\n    status      char        not null,\n    create_by   varchar(64)  default ''::varchar,\n    create_time timestamp,\n    update_by   varchar(64)  default ''::varchar,\n    update_time timestamp,\n    remark      varchar(500) default null::varchar,\n    constraint \"sys_post_pk\" primary key (post_id)\n);\n\ncomment on table sys_post is '岗位信息表';\ncomment on column sys_post.post_id is '岗位ID';\ncomment on column sys_post.post_code is '岗位编码';\ncomment on column sys_post.post_name is '岗位名称';\ncomment on column sys_post.post_sort is '显示顺序';\ncomment on column sys_post.status is '状态（0正常 1停用）';\ncomment on column sys_post.create_by is '创建者';\ncomment on column sys_post.create_time is '创建时间';\ncomment on column sys_post.update_by is '更新者';\ncomment on column sys_post.update_time is '更新时间';\ncomment on column sys_post.remark is '备注';\n\n-- ----------------------------\n-- 初始化-岗位信息表数据\n-- ----------------------------\ninsert into sys_post values(1, 'ceo',  '董事长',    1, '0', 'admin', now(), '', null, '');\ninsert into sys_post values(2, 'se',   '项目经理',  2, '0', 'admin', now(), '', null, '');\ninsert into sys_post values(3, 'hr',   '人力资源',  3, '0', 'admin', now(), '', null, '');\ninsert into sys_post values(4, 'user', '普通员工',  4, '0', 'admin', now(), '', null, '');\n\n-- ----------------------------\n-- 4、角色信息表\n-- ----------------------------\ndrop table if exists sys_role;\ncreate table if not exists sys_role\n(\n    role_id             int8,\n    role_name           varchar(30)  not null,\n    role_key            varchar(100) not null,\n    role_sort           int4         not null,\n    data_scope          char         default '1'::bpchar,\n    menu_check_strictly bool         default true,\n    dept_check_strictly bool         default true,\n    status              char         not null,\n    del_flag            char         default '0'::bpchar,\n    create_by           varchar(64)  default ''::varchar,\n    create_time         timestamp,\n    update_by           varchar(64)  default ''::varchar,\n    update_time         timestamp,\n    remark              varchar(500) default null::varchar,\n    constraint \"sys_role_pk\" primary key (role_id)\n);\n\ncomment on table sys_role is '角色信息表';\ncomment on column sys_role.role_id is '角色ID';\ncomment on column sys_role.role_name is '角色名称';\ncomment on column sys_role.role_key is '角色权限字符串';\ncomment on column sys_role.role_sort is '显示顺序';\ncomment on column sys_role.data_scope is '数据范围（1：全部数据权限 2：自定数据权限 3：本部门数据权限 4：本部门及以下数据权限）';\ncomment on column sys_role.menu_check_strictly is '菜单树选择项是否关联显示';\ncomment on column sys_role.dept_check_strictly is '部门树选择项是否关联显示';\ncomment on column sys_role.status is '角色状态（0正常 1停用）';\ncomment on column sys_role.del_flag is '删除标志（0代表存在 2代表删除）';\ncomment on column sys_role.create_by is '创建者';\ncomment on column sys_role.create_time is '创建时间';\ncomment on column sys_role.update_by is '更新者';\ncomment on column sys_role.update_time is '更新时间';\ncomment on column sys_role.remark is '备注';\n\n-- ----------------------------\n-- 初始化-角色信息表数据\n-- ----------------------------\ninsert into sys_role values('1', '超级管理员',  'admin',  1, '1', 't', 't', '0', '0', 'admin', now(), '', null, '超级管理员');\ninsert into sys_role values('2', '普通角色',    'common', 2, '2', 't', 't', '0', '0', 'admin', now(), '', null, '普通角色');\n\n\n-- ----------------------------\n-- 5、菜单权限表\n-- ----------------------------\ndrop table if exists sys_menu;\ncreate table if not exists sys_menu\n(\n    menu_id     int8,\n    menu_name   varchar(50) not null,\n    parent_id   int8         default 0,\n    order_num   int4         default 0,\n    path        varchar(200) default ''::varchar,\n    component   varchar(255) default null::varchar,\n    query_param varchar(255) default null::varchar,\n    is_frame    char         default '1'::bpchar,\n    is_cache    char         default '0'::bpchar,\n    menu_type   char         default ''::bpchar,\n    visible     char         default '0'::bpchar,\n    status      char         default '0'::bpchar,\n    perms       varchar(100) default null::varchar,\n    icon        varchar(100) default '#'::varchar,\n    create_by   varchar(64)  default ''::varchar,\n    create_time timestamp,\n    update_by   varchar(64)  default ''::varchar,\n    update_time timestamp,\n    remark      varchar(500) default ''::varchar,\n    constraint \"sys_menu_pk\" primary key (menu_id)\n);\n\ncomment on table sys_menu is '菜单权限表';\ncomment on column sys_menu.menu_id is '菜单ID';\ncomment on column sys_menu.menu_name is '菜单名称';\ncomment on column sys_menu.parent_id is '父菜单ID';\ncomment on column sys_menu.order_num is '显示顺序';\ncomment on column sys_menu.path is '路由地址';\ncomment on column sys_menu.component is '组件路径';\ncomment on column sys_menu.query_param is '路由参数';\ncomment on column sys_menu.is_frame is '是否为外链（0是 1否）';\ncomment on column sys_menu.is_cache is '是否缓存（0缓存 1不缓存）';\ncomment on column sys_menu.menu_type is '菜单类型（M目录 C菜单 F按钮）';\ncomment on column sys_menu.visible is '显示状态（0显示 1隐藏）';\ncomment on column sys_menu.status is '菜单状态（0正常 1停用）';\ncomment on column sys_menu.perms is '权限标识';\ncomment on column sys_menu.icon is '菜单图标';\ncomment on column sys_menu.create_by is '创建者';\ncomment on column sys_menu.create_time is '创建时间';\ncomment on column sys_menu.update_by is '更新者';\ncomment on column sys_menu.update_time is '更新时间';\ncomment on column sys_menu.remark is '备注';\n\n-- ----------------------------\n-- 初始化-菜单信息表数据\n-- ----------------------------\n-- 一级菜单\ninsert into sys_menu values('1', '系统管理', '0', '1', 'system',           null, '', '1', '0', 'M', '0', '0', '', 'system',   'admin', now(), '', null, '系统管理目录');\ninsert into sys_menu values('2', '系统监控', '0', '2', 'monitor',          null, '', '1', '0', 'M', '0', '0', '', 'monitor',  'admin', now(), '', null, '系统监控目录');\ninsert into sys_menu values('3', '系统工具', '0', '3', 'tool',             null, '', '1', '0', 'M', '0', '0', '', 'tool',     'admin', now(), '', null, '系统工具目录');\ninsert into sys_menu values('4', 'PLUS官网', '0', '4', 'https://gitee.com/dromara/RuoYi-Vue-Plus', null, '', '0', '0', 'M', '0', '0', '', 'guide',    'admin', now(), '', null, 'RuoYi-Vue-Plus官网地址');\n-- 二级菜单\ninsert into sys_menu values('100',  '用户管理', '1',   '1', 'user',       'system/user/index',        '', '1', '0', 'C', '0', '0', 'system:user:list',        'user',          'admin', now(), '', null, '用户管理菜单');\ninsert into sys_menu values('101',  '角色管理', '1',   '2', 'role',       'system/role/index',        '', '1', '0', 'C', '0', '0', 'system:role:list',        'peoples',       'admin', now(), '', null, '角色管理菜单');\ninsert into sys_menu values('102',  '菜单管理', '1',   '3', 'menu',       'system/menu/index',        '', '1', '0', 'C', '0', '0', 'system:menu:list',        'tree-table',    'admin', now(), '', null, '菜单管理菜单');\ninsert into sys_menu values('103',  '部门管理', '1',   '4', 'dept',       'system/dept/index',        '', '1', '0', 'C', '0', '0', 'system:dept:list',        'tree',          'admin', now(), '', null, '部门管理菜单');\ninsert into sys_menu values('104',  '岗位管理', '1',   '5', 'post',       'system/post/index',        '', '1', '0', 'C', '0', '0', 'system:post:list',        'post',          'admin', now(), '', null, '岗位管理菜单');\ninsert into sys_menu values('105',  '字典管理', '1',   '6', 'dict',       'system/dict/index',        '', '1', '0', 'C', '0', '0', 'system:dict:list',        'dict',          'admin', now(), '', null, '字典管理菜单');\ninsert into sys_menu values('106',  '参数设置', '1',   '7', 'config',     'system/config/index',      '', '1', '0', 'C', '0', '0', 'system:config:list',      'edit',          'admin', now(), '', null, '参数设置菜单');\ninsert into sys_menu values('107',  '通知公告', '1',   '8', 'notice',     'system/notice/index',      '', '1', '0', 'C', '0', '0', 'system:notice:list',      'message',       'admin', now(), '', null, '通知公告菜单');\ninsert into sys_menu values('108',  '日志管理', '1',   '9', 'log',        '',                         '', '1', '0', 'M', '0', '0', '',                        'log',           'admin', now(), '', null, '日志管理菜单');\ninsert into sys_menu values('109',  '在线用户', '2',   '1', 'online',     'monitor/online/index',     '', '1', '0', 'C', '0', '0', 'monitor:online:list',     'online',        'admin', now(), '', null, '在线用户菜单');\ninsert into sys_menu values('112',  '缓存列表', '2',   '6', 'cacheList',  'monitor/cache/list',       '', '1', '0', 'C', '0', '0', 'monitor:cache:list',      'redis-list',    'admin', now(), '', null, '缓存列表菜单');\ninsert into sys_menu values('113',  '缓存监控', '2',   '5', 'cache',      'monitor/cache/index',      '', '1', '0', 'C', '0', '0', 'monitor:cache:list',      'redis',         'admin', now(), '', null, '缓存监控菜单');\ninsert into sys_menu values('114',  '表单构建', '3',   '1', 'build',      'tool/build/index',         '', '1', '0', 'C', '0', '0', 'tool:build:list',         'build',         'admin', now(), '', null, '表单构建菜单');\ninsert into sys_menu values('115',  '代码生成', '3',   '2', 'gen',        'tool/gen/index',           '', '1', '0', 'C', '0', '0', 'tool:gen:list',           'code',          'admin', now(), '', null, '代码生成菜单');\n-- springboot-admin监控\ninsert into sys_menu values('117',  'Admin监控', '2',  '5', 'Admin',      'monitor/admin/index',      '', '1', '0', 'C', '0', '0', 'monitor:admin:list',      'dashboard',     'admin', now(), '', null, 'Admin监控菜单');\n-- oss菜单\ninsert into sys_menu values('118',  '文件管理', '1', '10', 'oss', 'system/oss/index', '', '1', '0', 'C', '0', '0', 'system:oss:list', 'upload', 'admin', now(), '', null, '文件管理菜单');\n-- xxl-job-admin控制台\ninsert into sys_menu values('120',  '任务调度中心', '2',  '5', 'XxlJob',      'monitor/xxljob/index',      '', '1', '0', 'C', '0', '0', 'monitor:xxljob:list',      'job',     'admin', now(), '', null, 'Xxl-Job控制台菜单');\n\n-- 三级菜单\ninsert into sys_menu values('500',  '操作日志', '108', '1', 'operlog',    'monitor/operlog/index',    '', '1', '0', 'C', '0', '0', 'monitor:operlog:list',    'form',          'admin', now(), '', null, '操作日志菜单');\ninsert into sys_menu values('501',  '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', '1', '0', 'C', '0', '0', 'monitor:logininfor:list', 'logininfor',    'admin', now(), '', null, '登录日志菜单');\n-- 用户管理按钮\ninsert into sys_menu values('1001', '用户查询', '100', '1',  '', '', '', '1', '0', 'F', '0', '0', 'system:user:query',          '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1002', '用户新增', '100', '2',  '', '', '', '1', '0', 'F', '0', '0', 'system:user:add',            '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1003', '用户修改', '100', '3',  '', '', '', '1', '0', 'F', '0', '0', 'system:user:edit',           '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1004', '用户删除', '100', '4',  '', '', '', '1', '0', 'F', '0', '0', 'system:user:remove',         '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1005', '用户导出', '100', '5',  '', '', '', '1', '0', 'F', '0', '0', 'system:user:export',         '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1006', '用户导入', '100', '6',  '', '', '', '1', '0', 'F', '0', '0', 'system:user:import',         '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1007', '重置密码', '100', '7',  '', '', '', '1', '0', 'F', '0', '0', 'system:user:resetPwd',       '#', 'admin', now(), '', null, '');\n-- 角色管理按钮\ninsert into sys_menu values('1008', '角色查询', '101', '1',  '', '', '', '1', '0', 'F', '0', '0', 'system:role:query',          '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1009', '角色新增', '101', '2',  '', '', '', '1', '0', 'F', '0', '0', 'system:role:add',            '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1010', '角色修改', '101', '3',  '', '', '', '1', '0', 'F', '0', '0', 'system:role:edit',           '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1011', '角色删除', '101', '4',  '', '', '', '1', '0', 'F', '0', '0', 'system:role:remove',         '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1012', '角色导出', '101', '5',  '', '', '', '1', '0', 'F', '0', '0', 'system:role:export',         '#', 'admin', now(), '', null, '');\n-- 菜单管理按钮\ninsert into sys_menu values('1013', '菜单查询', '102', '1',  '', '', '', '1', '0', 'F', '0', '0', 'system:menu:query',          '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1014', '菜单新增', '102', '2',  '', '', '', '1', '0', 'F', '0', '0', 'system:menu:add',            '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1015', '菜单修改', '102', '3',  '', '', '', '1', '0', 'F', '0', '0', 'system:menu:edit',           '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1016', '菜单删除', '102', '4',  '', '', '', '1', '0', 'F', '0', '0', 'system:menu:remove',         '#', 'admin', now(), '', null, '');\n-- 部门管理按钮\ninsert into sys_menu values('1017', '部门查询', '103', '1',  '', '', '', '1', '0', 'F', '0', '0', 'system:dept:query',          '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1018', '部门新增', '103', '2',  '', '', '', '1', '0', 'F', '0', '0', 'system:dept:add',            '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1019', '部门修改', '103', '3',  '', '', '', '1', '0', 'F', '0', '0', 'system:dept:edit',           '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1020', '部门删除', '103', '4',  '', '', '', '1', '0', 'F', '0', '0', 'system:dept:remove',         '#', 'admin', now(), '', null, '');\n-- 岗位管理按钮\ninsert into sys_menu values('1021', '岗位查询', '104', '1',  '', '', '', '1', '0', 'F', '0', '0', 'system:post:query',          '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1022', '岗位新增', '104', '2',  '', '', '', '1', '0', 'F', '0', '0', 'system:post:add',            '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1023', '岗位修改', '104', '3',  '', '', '', '1', '0', 'F', '0', '0', 'system:post:edit',           '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1024', '岗位删除', '104', '4',  '', '', '', '1', '0', 'F', '0', '0', 'system:post:remove',         '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1025', '岗位导出', '104', '5',  '', '', '', '1', '0', 'F', '0', '0', 'system:post:export',         '#', 'admin', now(), '', null, '');\n-- 字典管理按钮\ninsert into sys_menu values('1026', '字典查询', '105', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:query',          '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1027', '字典新增', '105', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:add',            '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1028', '字典修改', '105', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:edit',           '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1029', '字典删除', '105', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:remove',         '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1030', '字典导出', '105', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:export',         '#', 'admin', now(), '', null, '');\n-- 参数设置按钮\ninsert into sys_menu values('1031', '参数查询', '106', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:query',        '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1032', '参数新增', '106', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:add',          '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1033', '参数修改', '106', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:edit',         '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1034', '参数删除', '106', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:remove',       '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1035', '参数导出', '106', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:export',       '#', 'admin', now(), '', null, '');\n-- 通知公告按钮\ninsert into sys_menu values('1036', '公告查询', '107', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:query',        '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1037', '公告新增', '107', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:add',          '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1038', '公告修改', '107', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:edit',         '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1039', '公告删除', '107', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:remove',       '#', 'admin', now(), '', null, '');\n-- 操作日志按钮\ninsert into sys_menu values('1040', '操作查询', '500', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:operlog:query',      '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1041', '操作删除', '500', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:operlog:remove',     '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:operlog:export',     '#', 'admin', now(), '', null, '');\n-- 登录日志按钮\ninsert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:query',   '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:remove',  '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:export',  '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:unlock',  '#', 'admin', now(), '', null, '');\n-- 在线用户按钮\ninsert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:query',       '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:batchLogout', '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:forceLogout', '#', 'admin', now(), '', null, '');\n-- 代码生成按钮\ninsert into sys_menu values('1055', '生成查询', '115', '1', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:query',             '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1056', '生成修改', '115', '2', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:edit',              '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1057', '生成删除', '115', '3', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:remove',            '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1058', '导入代码', '115', '2', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:import',            '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1059', '预览代码', '115', '4', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:preview',           '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1060', '生成代码', '115', '5', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:code',              '#', 'admin', now(), '', null, '');\n-- oss相关按钮\ninsert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:query',        '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:upload',       '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:download',     '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:remove',       '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1604', '配置添加', '118', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:add',          '#', 'admin', now(), '', null, '');\ninsert into sys_menu values('1605', '配置编辑', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:edit',         '#', 'admin', now(), '', null, '');\n\n\n-- ----------------------------\n-- 6、用户和角色关联表  用户N-1角色\n-- ----------------------------\ndrop table if exists sys_user_role;\ncreate table if not exists sys_user_role\n(\n    user_id int8 not null,\n    role_id int8 not null,\n    constraint sys_user_role_pk primary key (user_id, role_id)\n);\n\ncomment on table sys_user_role is '用户和角色关联表';\ncomment on column sys_user_role.user_id is '用户ID';\ncomment on column sys_user_role.role_id is '角色ID';\n\n-- ----------------------------\n-- 初始化-用户和角色关联表数据\n-- ----------------------------\ninsert into sys_user_role values ('1', '1');\ninsert into sys_user_role values ('2', '2');\n\n\n-- ----------------------------\n-- 7、角色和菜单关联表  角色1-N菜单\n-- ----------------------------\ndrop table if exists sys_role_menu;\ncreate table if not exists sys_role_menu\n(\n    role_id int8 not null,\n    menu_id int8 not null,\n    constraint sys_role_menu_pk primary key (role_id, menu_id)\n);\n\ncomment on table sys_role_menu is '角色和菜单关联表';\ncomment on column sys_role_menu.role_id is '角色ID';\ncomment on column sys_role_menu.menu_id is '菜单ID';\n\n-- ----------------------------\n-- 初始化-角色和菜单关联表数据\n-- ----------------------------\ninsert into sys_role_menu values ('2', '1');\ninsert into sys_role_menu values ('2', '2');\ninsert into sys_role_menu values ('2', '3');\ninsert into sys_role_menu values ('2', '4');\ninsert into sys_role_menu values ('2', '100');\ninsert into sys_role_menu values ('2', '101');\ninsert into sys_role_menu values ('2', '102');\ninsert into sys_role_menu values ('2', '103');\ninsert into sys_role_menu values ('2', '104');\ninsert into sys_role_menu values ('2', '105');\ninsert into sys_role_menu values ('2', '106');\ninsert into sys_role_menu values ('2', '107');\ninsert into sys_role_menu values ('2', '108');\ninsert into sys_role_menu values ('2', '109');\ninsert into sys_role_menu values ('2', '110');\ninsert into sys_role_menu values ('2', '111');\ninsert into sys_role_menu values ('2', '112');\ninsert into sys_role_menu values ('2', '113');\ninsert into sys_role_menu values ('2', '114');\ninsert into sys_role_menu values ('2', '115');\ninsert into sys_role_menu values ('2', '116');\ninsert into sys_role_menu values ('2', '500');\ninsert into sys_role_menu values ('2', '501');\ninsert into sys_role_menu values ('2', '1000');\ninsert into sys_role_menu values ('2', '1001');\ninsert into sys_role_menu values ('2', '1002');\ninsert into sys_role_menu values ('2', '1003');\ninsert into sys_role_menu values ('2', '1004');\ninsert into sys_role_menu values ('2', '1005');\ninsert into sys_role_menu values ('2', '1006');\ninsert into sys_role_menu values ('2', '1007');\ninsert into sys_role_menu values ('2', '1008');\ninsert into sys_role_menu values ('2', '1009');\ninsert into sys_role_menu values ('2', '1010');\ninsert into sys_role_menu values ('2', '1011');\ninsert into sys_role_menu values ('2', '1012');\ninsert into sys_role_menu values ('2', '1013');\ninsert into sys_role_menu values ('2', '1014');\ninsert into sys_role_menu values ('2', '1015');\ninsert into sys_role_menu values ('2', '1016');\ninsert into sys_role_menu values ('2', '1017');\ninsert into sys_role_menu values ('2', '1018');\ninsert into sys_role_menu values ('2', '1019');\ninsert into sys_role_menu values ('2', '1020');\ninsert into sys_role_menu values ('2', '1021');\ninsert into sys_role_menu values ('2', '1022');\ninsert into sys_role_menu values ('2', '1023');\ninsert into sys_role_menu values ('2', '1024');\ninsert into sys_role_menu values ('2', '1025');\ninsert into sys_role_menu values ('2', '1026');\ninsert into sys_role_menu values ('2', '1027');\ninsert into sys_role_menu values ('2', '1028');\ninsert into sys_role_menu values ('2', '1029');\ninsert into sys_role_menu values ('2', '1030');\ninsert into sys_role_menu values ('2', '1031');\ninsert into sys_role_menu values ('2', '1032');\ninsert into sys_role_menu values ('2', '1033');\ninsert into sys_role_menu values ('2', '1034');\ninsert into sys_role_menu values ('2', '1035');\ninsert into sys_role_menu values ('2', '1036');\ninsert into sys_role_menu values ('2', '1037');\ninsert into sys_role_menu values ('2', '1038');\ninsert into sys_role_menu values ('2', '1039');\ninsert into sys_role_menu values ('2', '1040');\ninsert into sys_role_menu values ('2', '1041');\ninsert into sys_role_menu values ('2', '1042');\ninsert into sys_role_menu values ('2', '1043');\ninsert into sys_role_menu values ('2', '1044');\ninsert into sys_role_menu values ('2', '1045');\ninsert into sys_role_menu values ('2', '1050');\ninsert into sys_role_menu values ('2', '1046');\ninsert into sys_role_menu values ('2', '1047');\ninsert into sys_role_menu values ('2', '1048');\ninsert into sys_role_menu values ('2', '1055');\ninsert into sys_role_menu values ('2', '1056');\ninsert into sys_role_menu values ('2', '1057');\ninsert into sys_role_menu values ('2', '1058');\ninsert into sys_role_menu values ('2', '1059');\ninsert into sys_role_menu values ('2', '1060');\n\n-- ----------------------------\n-- 8、角色和部门关联表  角色1-N部门\n-- ----------------------------\ndrop table if exists sys_role_dept;\ncreate table if not exists sys_role_dept\n(\n    role_id int8 not null,\n    dept_id int8 not null,\n    constraint sys_role_dept_pk primary key (role_id, dept_id)\n);\n\ncomment on table sys_role_dept is '角色和部门关联表';\ncomment on column sys_role_dept.role_id is '角色ID';\ncomment on column sys_role_dept.dept_id is '部门ID';\n\n-- ----------------------------\n-- 初始化-角色和部门关联表数据\n-- ----------------------------\ninsert into sys_role_dept values ('2', '100');\ninsert into sys_role_dept values ('2', '101');\ninsert into sys_role_dept values ('2', '105');\n\n\n-- ----------------------------\n-- 9、用户与岗位关联表  用户1-N岗位\n-- ----------------------------\ndrop table if exists sys_user_post;\ncreate table if not exists sys_user_post\n(\n    user_id int8 not null,\n    post_id int8 not null,\n    constraint sys_user_post_pk primary key (user_id, post_id)\n);\n\ncomment on table sys_user_post is '用户与岗位关联表';\ncomment on column sys_user_post.user_id is '用户ID';\ncomment on column sys_user_post.post_id is '岗位ID';\n\n-- ----------------------------\n-- 初始化-用户与岗位关联表数据\n-- ----------------------------\ninsert into sys_user_post values ('1', '1');\ninsert into sys_user_post values ('2', '2');\n\n\n-- ----------------------------\n-- 10、操作日志记录\n-- ----------------------------\ndrop table if exists sys_oper_log;\ncreate table if not exists sys_oper_log\n(\n    oper_id        int8,\n    title          varchar(50)   default ''::varchar,\n    business_type  int4          default 0,\n    method         varchar(100)  default ''::varchar,\n    request_method varchar(10)   default ''::varchar,\n    operator_type  int4          default 0,\n    oper_name      varchar(50)   default ''::varchar,\n    dept_name      varchar(50)   default ''::varchar,\n    oper_url       varchar(255)  default ''::varchar,\n    oper_ip        varchar(128)  default ''::varchar,\n    oper_location  varchar(255)  default ''::varchar,\n    oper_param     varchar(2000) default ''::varchar,\n    json_result    varchar(2000) default ''::varchar,\n    status         int4          default 0,\n    error_msg      varchar(2000) default ''::varchar,\n    oper_time      timestamp,\n    constraint sys_oper_log_pk primary key (oper_id)\n);\n\ncreate index idx_sys_oper_log_bt ON sys_oper_log (business_type);\ncreate index idx_sys_oper_log_s ON sys_oper_log (status);\ncreate index idx_sys_oper_log_ot ON sys_oper_log (oper_time);\n\ncomment on table sys_oper_log is '操作日志记录';\ncomment on column sys_oper_log.oper_id is '日志主键';\ncomment on column sys_oper_log.title is '模块标题';\ncomment on column sys_oper_log.business_type is '业务类型（0其它 1新增 2修改 3删除）';\ncomment on column sys_oper_log.method is '方法名称';\ncomment on column sys_oper_log.request_method is '请求方式';\ncomment on column sys_oper_log.operator_type is '操作类别（0其它 1后台用户 2手机端用户）';\ncomment on column sys_oper_log.oper_name is '操作人员';\ncomment on column sys_oper_log.dept_name is '部门名称';\ncomment on column sys_oper_log.oper_url is '请求URL';\ncomment on column sys_oper_log.oper_ip is '主机地址';\ncomment on column sys_oper_log.oper_location is '操作地点';\ncomment on column sys_oper_log.oper_param is '请求参数';\ncomment on column sys_oper_log.json_result is '返回参数';\ncomment on column sys_oper_log.status is '操作状态（0正常 1异常）';\ncomment on column sys_oper_log.error_msg is '错误消息';\ncomment on column sys_oper_log.oper_time is '操作时间';\n\n-- ----------------------------\n-- 11、字典类型表\n-- ----------------------------\ndrop table if exists sys_dict_type;\ncreate table if not exists sys_dict_type\n(\n    dict_id     int8,\n    dict_name   varchar(100) default ''::varchar,\n    dict_type   varchar(100) default ''::varchar,\n    status      char         default '0'::bpchar,\n    create_by   varchar(64)  default ''::varchar,\n    create_time timestamp,\n    update_by   varchar(64)  default ''::varchar,\n    update_time timestamp,\n    remark      varchar(500) default null::varchar,\n    constraint sys_dict_type_pk primary key (dict_id)\n);\n\ncreate unique index sys_dict_type_index1 ON sys_dict_type (dict_type);\n\ncomment on table sys_dict_type is '字典类型表';\ncomment on column sys_dict_type.dict_id is '字典主键';\ncomment on column sys_dict_type.dict_name is '字典名称';\ncomment on column sys_dict_type.dict_type is '字典类型';\ncomment on column sys_dict_type.status is '状态（0正常 1停用）';\ncomment on column sys_dict_type.create_by is '创建者';\ncomment on column sys_dict_type.create_time is '创建时间';\ncomment on column sys_dict_type.update_by is '更新者';\ncomment on column sys_dict_type.update_time is '更新时间';\ncomment on column sys_dict_type.remark is '备注';\n\ninsert into sys_dict_type values(1,  '用户性别', 'sys_user_sex',        '0', 'admin', now(), '', null, '用户性别列表');\ninsert into sys_dict_type values(2,  '菜单状态', 'sys_show_hide',       '0', 'admin', now(), '', null, '菜单状态列表');\ninsert into sys_dict_type values(3,  '系统开关', 'sys_normal_disable',  '0', 'admin', now(), '', null, '系统开关列表');\ninsert into sys_dict_type values(6,  '系统是否', 'sys_yes_no',          '0', 'admin', now(), '', null, '系统是否列表');\ninsert into sys_dict_type values(7,  '通知类型', 'sys_notice_type',     '0', 'admin', now(), '', null, '通知类型列表');\ninsert into sys_dict_type values(8,  '通知状态', 'sys_notice_status',   '0', 'admin', now(), '', null, '通知状态列表');\ninsert into sys_dict_type values(9,  '操作类型', 'sys_oper_type',       '0', 'admin', now(), '', null, '操作类型列表');\ninsert into sys_dict_type values(10, '系统状态', 'sys_common_status',   '0', 'admin', now(), '', null, '登录状态列表');\n\n\n-- ----------------------------\n-- 12、字典数据表\n-- ----------------------------\ndrop table if exists sys_dict_data;\ncreate table if not exists sys_dict_data\n(\n    dict_code   int8,\n    dict_sort   int4         default 0,\n    dict_label  varchar(100) default ''::varchar,\n    dict_value  varchar(100) default ''::varchar,\n    dict_type   varchar(100) default ''::varchar,\n    css_class   varchar(100) default null::varchar,\n    list_class  varchar(100) default null::varchar,\n    is_default  char         default 'N'::bpchar,\n    status      char         default '0'::bpchar,\n    create_by   varchar(64)  default ''::varchar,\n    create_time timestamp,\n    update_by   varchar(64)  default ''::varchar,\n    update_time timestamp,\n    remark      varchar(500) default null::varchar,\n    constraint sys_dict_data_pk primary key (dict_code)\n);\n\ncomment on table sys_dict_data is '字典数据表';\ncomment on column sys_dict_data.dict_code is '字典编码';\ncomment on column sys_dict_data.dict_sort is '字典排序';\ncomment on column sys_dict_data.dict_label is '字典标签';\ncomment on column sys_dict_data.dict_value is '字典键值';\ncomment on column sys_dict_data.dict_type is '字典类型';\ncomment on column sys_dict_data.css_class is '样式属性（其他样式扩展）';\ncomment on column sys_dict_data.list_class is '表格回显样式';\ncomment on column sys_dict_data.is_default is '是否默认（Y是 N否）';\ncomment on column sys_dict_data.status is '状态（0正常 1停用）';\ncomment on column sys_dict_data.create_by is '创建者';\ncomment on column sys_dict_data.create_time is '创建时间';\ncomment on column sys_dict_data.update_by is '更新者';\ncomment on column sys_dict_data.update_time is '更新时间';\ncomment on column sys_dict_data.remark is '备注';\n\ninsert into sys_dict_data values(1,  1,  '男',       '0',       'sys_user_sex',        '',   '',        'Y', '0', 'admin', now(), '', null, '性别男');\ninsert into sys_dict_data values(2,  2,  '女',       '1',       'sys_user_sex',        '',   '',        'N', '0', 'admin', now(), '', null, '性别女');\ninsert into sys_dict_data values(3,  3,  '未知',     '2',       'sys_user_sex',        '',   '',        'N', '0', 'admin', now(), '', null, '性别未知');\ninsert into sys_dict_data values(4,  1,  '显示',     '0',       'sys_show_hide',       '',   'primary', 'Y', '0', 'admin', now(), '', null, '显示菜单');\ninsert into sys_dict_data values(5,  2,  '隐藏',     '1',       'sys_show_hide',       '',   'danger',  'N', '0', 'admin', now(), '', null, '隐藏菜单');\ninsert into sys_dict_data values(6,  1,  '正常',     '0',       'sys_normal_disable',  '',   'primary', 'Y', '0', 'admin', now(), '', null, '正常状态');\ninsert into sys_dict_data values(7,  2,  '停用',     '1',       'sys_normal_disable',  '',   'danger',  'N', '0', 'admin', now(), '', null, '停用状态');\ninsert into sys_dict_data values(12, 1,  '是',       'Y',       'sys_yes_no',          '',   'primary', 'Y', '0', 'admin', now(), '', null, '系统默认是');\ninsert into sys_dict_data values(13, 2,  '否',       'N',       'sys_yes_no',          '',   'danger',  'N', '0', 'admin', now(), '', null, '系统默认否');\ninsert into sys_dict_data values(14, 1,  '通知',     '1',       'sys_notice_type',     '',   'warning', 'Y', '0', 'admin', now(), '', null, '通知');\ninsert into sys_dict_data values(15, 2,  '公告',     '2',       'sys_notice_type',     '',   'success', 'N', '0', 'admin', now(), '', null, '公告');\ninsert into sys_dict_data values(16, 1,  '正常',     '0',       'sys_notice_status',   '',   'primary', 'Y', '0', 'admin', now(), '', null, '正常状态');\ninsert into sys_dict_data values(17, 2,  '关闭',     '1',       'sys_notice_status',   '',   'danger',  'N', '0', 'admin', now(), '', null, '关闭状态');\ninsert into sys_dict_data values(29, 99, '其他',     '0',       'sys_oper_type',       '',   'info',    'N', '0', 'admin', now(), '', null, '其他操作');\ninsert into sys_dict_data values(18, 1,  '新增',     '1',       'sys_oper_type',       '',   'info',    'N', '0', 'admin', now(), '', null, '新增操作');\ninsert into sys_dict_data values(19, 2,  '修改',     '2',       'sys_oper_type',       '',   'info',    'N', '0', 'admin', now(), '', null, '修改操作');\ninsert into sys_dict_data values(20, 3,  '删除',     '3',       'sys_oper_type',       '',   'danger',  'N', '0', 'admin', now(), '', null, '删除操作');\ninsert into sys_dict_data values(21, 4,  '授权',     '4',       'sys_oper_type',       '',   'primary', 'N', '0', 'admin', now(), '', null, '授权操作');\ninsert into sys_dict_data values(22, 5,  '导出',     '5',       'sys_oper_type',       '',   'warning', 'N', '0', 'admin', now(), '', null, '导出操作');\ninsert into sys_dict_data values(23, 6,  '导入',     '6',       'sys_oper_type',       '',   'warning', 'N', '0', 'admin', now(), '', null, '导入操作');\ninsert into sys_dict_data values(24, 7,  '强退',     '7',       'sys_oper_type',       '',   'danger',  'N', '0', 'admin', now(), '', null, '强退操作');\ninsert into sys_dict_data values(25, 8,  '生成代码', '8',       'sys_oper_type',       '',   'warning', 'N', '0', 'admin', now(), '', null, '生成操作');\ninsert into sys_dict_data values(26, 9,  '清空数据', '9',       'sys_oper_type',       '',   'danger',  'N', '0', 'admin', now(), '', null, '清空操作');\ninsert into sys_dict_data values(27, 1,  '成功',     '0',       'sys_common_status',   '',   'primary', 'N', '0', 'admin', now(), '', null, '正常状态');\ninsert into sys_dict_data values(28, 2,  '失败',     '1',       'sys_common_status',   '',   'danger',  'N', '0', 'admin', now(), '', null, '停用状态');\n\n\n-- ----------------------------\n-- 13、参数配置表\n-- ----------------------------\ndrop table if exists sys_config;\ncreate table if not exists sys_config\n(\n    config_id    int8,\n    config_name  varchar(100) default ''::varchar,\n    config_key   varchar(100) default ''::varchar,\n    config_value varchar(500) default ''::varchar,\n    config_type  char         default 'N'::bpchar,\n    create_by    varchar(64)  default ''::varchar,\n    create_time  timestamp,\n    update_by    varchar(64)  default ''::varchar,\n    update_time  timestamp,\n    remark       varchar(500) default null::varchar,\n    constraint sys_config_pk primary key (config_id)\n);\n\ncomment on table sys_config is '参数配置表';\ncomment on column sys_config.config_id is '参数主键';\ncomment on column sys_config.config_name is '参数名称';\ncomment on column sys_config.config_key is '参数键名';\ncomment on column sys_config.config_value is '参数键值';\ncomment on column sys_config.config_type is '系统内置（Y是 N否）';\ncomment on column sys_config.create_by is '创建者';\ncomment on column sys_config.create_time is '创建时间';\ncomment on column sys_config.update_by is '更新者';\ncomment on column sys_config.update_time is '更新时间';\ncomment on column sys_config.remark is '备注';\n\ninsert into sys_config values(1, '主框架页-默认皮肤样式名称',     'sys.index.skinName',            'skin-blue',     'Y', 'admin', now(), '', null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' );\ninsert into sys_config values(2, '用户管理-账号初始密码',         'sys.user.initPassword',         '123456',        'Y', 'admin', now(), '', null, '初始化密码 123456' );\ninsert into sys_config values(3, '主框架页-侧边栏主题',           'sys.index.sideTheme',           'theme-dark',    'Y', 'admin', now(), '', null, '深色主题theme-dark，浅色主题theme-light' );\ninsert into sys_config values(4, '账号自助-验证码开关',           'sys.account.captchaEnabled',    'true',          'Y', 'admin', now(), '', null, '是否开启验证码功能（true开启，false关闭）');\ninsert into sys_config values(5, '账号自助-是否开启用户注册功能',   'sys.account.registerUser',      'false',         'Y', 'admin', now(), '', null, '是否开启注册用户功能（true开启，false关闭）');\ninsert into sys_config values(11, 'OSS预览列表资源开关',          'sys.oss.previewListResource',   'true',          'Y', 'admin', now(), '', null, 'true:开启, false:关闭');\n\n\n-- ----------------------------\n-- 14、系统访问记录\n-- ----------------------------\ndrop table if exists sys_logininfor;\ncreate table if not exists sys_logininfor\n(\n    info_id        int8,\n    user_name      varchar(50)  default ''::varchar,\n    ipaddr         varchar(128) default ''::varchar,\n    login_location varchar(255) default ''::varchar,\n    browser        varchar(50)  default ''::varchar,\n    os             varchar(50)  default ''::varchar,\n    status         char         default '0'::bpchar,\n    msg            varchar(255) default ''::varchar,\n    login_time     timestamp,\n    constraint sys_logininfor_pk primary key (info_id)\n);\n\ncreate index idx_sys_logininfor_s ON sys_logininfor (status);\ncreate index idx_sys_logininfor_lt ON sys_logininfor (login_time);\n\ncomment on table sys_logininfor is '系统访问记录';\ncomment on column sys_logininfor.info_id is '访问ID';\ncomment on column sys_logininfor.user_name is '用户账号';\ncomment on column sys_logininfor.ipaddr is '登录IP地址';\ncomment on column sys_logininfor.login_location is '登录地点';\ncomment on column sys_logininfor.browser is '浏览器类型';\ncomment on column sys_logininfor.os is '操作系统';\ncomment on column sys_logininfor.status is '登录状态（0成功 1失败）';\ncomment on column sys_logininfor.msg is '提示消息';\ncomment on column sys_logininfor.login_time is '访问时间';\n\n-- ----------------------------\n-- 17、通知公告表\n-- ----------------------------\ndrop table if exists sys_notice;\ncreate table if not exists sys_notice\n(\n    notice_id      int8,\n    notice_title   varchar(50) not null,\n    notice_type    char        not null,\n    notice_content text,\n    status         char         default '0'::bpchar,\n    create_by      varchar(64)  default ''::varchar,\n    create_time    timestamp,\n    update_by      varchar(64)  default ''::varchar,\n    update_time    timestamp,\n    remark         varchar(255) default null::varchar,\n    constraint sys_notice_pk primary key (notice_id)\n);\n\ncomment on table sys_notice is '通知公告表';\ncomment on column sys_notice.notice_id is '公告ID';\ncomment on column sys_notice.notice_title is '公告标题';\ncomment on column sys_notice.notice_type is '公告类型（1通知 2公告）';\ncomment on column sys_notice.notice_content is '公告内容';\ncomment on column sys_notice.status is '公告状态（0正常 1关闭）';\ncomment on column sys_notice.create_by is '创建者';\ncomment on column sys_notice.create_time is '创建时间';\ncomment on column sys_notice.update_by is '更新者';\ncomment on column sys_notice.update_time is '更新时间';\ncomment on column sys_notice.remark is '备注';\n\n-- ----------------------------\n-- 初始化-公告信息表数据\n-- ----------------------------\ninsert into sys_notice values('1', '温馨提醒：2018-07-01 新版本发布啦', '2', '新版本内容', '0', 'admin', now(), '', null, '管理员');\ninsert into sys_notice values('2', '维护通知：2018-07-01 系统凌晨维护', '1', '维护内容',   '0', 'admin', now(), '', null, '管理员');\n\n\n-- ----------------------------\n-- 18、代码生成业务表\n-- ----------------------------\ndrop table if exists gen_table;\ncreate table if not exists gen_table\n(\n    table_id          int8,\n    table_name        varchar(200)  default ''::varchar,\n    table_comment     varchar(500)  default ''::varchar,\n    sub_table_name    varchar(64)   default ''::varchar,\n    sub_table_fk_name varchar(64)   default ''::varchar,\n    class_name        varchar(100)  default ''::varchar,\n    tpl_category      varchar(200)  default 'crud'::varchar,\n    package_name      varchar(100)  default null::varchar,\n    module_name       varchar(30)   default null::varchar,\n    business_name     varchar(30)   default null::varchar,\n    function_name     varchar(50)   default null::varchar,\n    function_author   varchar(50)   default null::varchar,\n    gen_type          char          default '0'::bpchar not null,\n    gen_path          varchar(200)  default '/'::varchar,\n    options           varchar(1000) default null::varchar,\n    create_by         varchar(64)   default ''::varchar,\n    create_time       timestamp,\n    update_by         varchar(64)   default ''::varchar,\n    update_time       timestamp,\n    remark            varchar(500)  default null::varchar,\n    constraint gen_table_pk primary key (table_id)\n);\n\ncomment on table gen_table is '代码生成业务表';\ncomment on column gen_table.table_id is '编号';\ncomment on column gen_table.table_name is '表名称';\ncomment on column gen_table.table_comment is '表描述';\ncomment on column gen_table.sub_table_name is '关联子表的表名';\ncomment on column gen_table.sub_table_fk_name is '子表关联的外键名';\ncomment on column gen_table.class_name is '实体类名称';\ncomment on column gen_table.tpl_category is '使用的模板（CRUD单表操作 TREE树表操作）';\ncomment on column gen_table.package_name is '生成包路径';\ncomment on column gen_table.module_name is '生成模块名';\ncomment on column gen_table.business_name is '生成业务名';\ncomment on column gen_table.function_name is '生成功能名';\ncomment on column gen_table.function_author is '生成功能作者';\ncomment on column gen_table.gen_type is '生成代码方式（0zip压缩包 1自定义路径）';\ncomment on column gen_table.gen_path is '生成路径（不填默认项目路径）';\ncomment on column gen_table.options is '其它生成选项';\ncomment on column gen_table.create_by is '创建者';\ncomment on column gen_table.create_time is '创建时间';\ncomment on column gen_table.update_by is '更新者';\ncomment on column gen_table.update_time is '更新时间';\ncomment on column gen_table.remark is '备注';\n\n-- ----------------------------\n-- 19、代码生成业务表字段\n-- ----------------------------\ndrop table if exists gen_table_column;\ncreate table if not exists gen_table_column\n(\n    column_id      int8,\n    table_id       int8,\n    column_name    varchar(200) default null::varchar,\n    column_comment varchar(500) default null::varchar,\n    column_type    varchar(100) default null::varchar,\n    java_type      varchar(500) default null::varchar,\n    java_field     varchar(200) default null::varchar,\n    is_pk          char         default null::bpchar,\n    is_increment   char         default null::bpchar,\n    is_required    char         default null::bpchar,\n    is_insert      char         default null::bpchar,\n    is_edit        char         default null::bpchar,\n    is_list        char         default null::bpchar,\n    is_query       char         default null::bpchar,\n    query_type     varchar(200) default 'EQ'::varchar,\n    html_type      varchar(200) default null::varchar,\n    dict_type      varchar(200) default ''::varchar,\n    sort           int4,\n    create_by      varchar(64)  default ''::varchar,\n    create_time    timestamp,\n    update_by      varchar(64)  default ''::varchar,\n    update_time    timestamp,\n    constraint gen_table_column_pk primary key (column_id)\n);\n\ncomment on table gen_table_column is '代码生成业务表字段';\ncomment on column gen_table_column.column_id is '编号';\ncomment on column gen_table_column.table_id is '归属表编号';\ncomment on column gen_table_column.column_name is '列名称';\ncomment on column gen_table_column.column_comment is '列描述';\ncomment on column gen_table_column.column_type is '列类型';\ncomment on column gen_table_column.java_type is 'JAVA类型';\ncomment on column gen_table_column.java_field is 'JAVA字段名';\ncomment on column gen_table_column.is_pk is '是否主键（1是）';\ncomment on column gen_table_column.is_increment is '是否自增（1是）';\ncomment on column gen_table_column.is_required is '是否必填（1是）';\ncomment on column gen_table_column.is_insert is '是否为插入字段（1是）';\ncomment on column gen_table_column.is_edit is '是否编辑字段（1是）';\ncomment on column gen_table_column.is_list is '是否列表字段（1是）';\ncomment on column gen_table_column.is_query is '是否查询字段（1是）';\ncomment on column gen_table_column.query_type is '查询方式（等于、不等于、大于、小于、范围）';\ncomment on column gen_table_column.html_type is '显示类型（文本框、文本域、下拉框、复选框、单选框、日期控件）';\ncomment on column gen_table_column.dict_type is '字典类型';\ncomment on column gen_table_column.sort is '排序';\ncomment on column gen_table_column.create_by is '创建者';\ncomment on column gen_table_column.create_time is '创建时间';\ncomment on column gen_table_column.update_by is '更新者';\ncomment on column gen_table_column.update_time is '更新时间';\n\n-- ----------------------------\n-- OSS对象存储表\n-- ----------------------------\ndrop table if exists sys_oss;\ncreate table if not exists sys_oss\n(\n    oss_id        int8,\n    file_name     varchar(255) default ''::varchar not null,\n    original_name varchar(255) default ''::varchar not null,\n    file_suffix   varchar(10)  default ''::varchar not null,\n    url           varchar(500) default ''::varchar not null,\n    create_by     varchar(64)  default ''::varchar,\n    create_time   timestamp,\n    update_by     varchar(64)  default ''::varchar,\n    update_time   timestamp,\n    service       varchar(20)  default 'minio'::varchar,\n    constraint sys_oss_pk primary key (oss_id)\n);\n\ncomment on table sys_oss is 'OSS对象存储表';\ncomment on column sys_oss.oss_id is '对象存储主键';\ncomment on column sys_oss.file_name is '文件名';\ncomment on column sys_oss.original_name is '原名';\ncomment on column sys_oss.file_suffix is '文件后缀名';\ncomment on column sys_oss.url is 'URL地址';\ncomment on column sys_oss.create_by is '上传人';\ncomment on column sys_oss.create_time is '创建时间';\ncomment on column sys_oss.update_by is '更新者';\ncomment on column sys_oss.update_time is '更新时间';\ncomment on column sys_oss.service is '服务商';\n\n-- ----------------------------\n-- OSS对象存储动态配置表\n-- ----------------------------\ndrop table if exists sys_oss_config;\ncreate table if not exists sys_oss_config\n(\n    oss_config_id int8,\n    config_key    varchar(20)  default ''::varchar not null,\n    access_key    varchar(255) default ''::varchar,\n    secret_key    varchar(255) default ''::varchar,\n    bucket_name   varchar(255) default ''::varchar,\n    prefix        varchar(255) default ''::varchar,\n    endpoint      varchar(255) default ''::varchar,\n    domain        varchar(255) default ''::varchar,\n    is_https      char         default 'N'::bpchar,\n    region        varchar(255) default ''::varchar,\n    access_policy char(1)      default '1'::bpchar not null,\n    status        char         default '1'::bpchar,\n    ext1          varchar(255) default ''::varchar,\n    create_by     varchar(64)  default ''::varchar,\n    create_time   timestamp,\n    update_by     varchar(64)  default ''::varchar,\n    update_time   timestamp,\n    remark        varchar(500) default ''::varchar,\n    constraint sys_oss_config_pk primary key (oss_config_id)\n);\n\ncomment on table sys_oss_config is '对象存储配置表';\ncomment on column sys_oss_config.oss_config_id is '主建';\ncomment on column sys_oss_config.config_key is '配置key';\ncomment on column sys_oss_config.access_key is 'accessKey';\ncomment on column sys_oss_config.secret_key is '秘钥';\ncomment on column sys_oss_config.bucket_name is '桶名称';\ncomment on column sys_oss_config.prefix is '前缀';\ncomment on column sys_oss_config.endpoint is '访问站点';\ncomment on column sys_oss_config.domain is '自定义域名';\ncomment on column sys_oss_config.is_https is '是否https（Y=是,N=否）';\ncomment on column sys_oss_config.region is '域';\ncomment on column sys_oss_config.access_policy is '桶权限类型(0=private 1=public 2=custom)';\ncomment on column sys_oss_config.status is '是否默认（0=是,1=否）';\ncomment on column sys_oss_config.ext1 is '扩展字段';\ncomment on column sys_oss_config.create_by is '创建者';\ncomment on column sys_oss_config.create_time is '创建时间';\ncomment on column sys_oss_config.update_by is '更新者';\ncomment on column sys_oss_config.update_time is '更新时间';\ncomment on column sys_oss_config.remark is '备注';\n\ninsert into sys_oss_config values (1, 'minio',  'ruoyi',            'ruoyi123',        'ruoyi',             '', '127.0.0.1:9000',                      '','N', '',            '1', '0', '', 'admin', now(), 'admin', now(), null);\ninsert into sys_oss_config values (2, 'qiniu',  'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi',             '', 's3-cn-north-1.qiniucs.com',           '','N', '',            '1', '1', '', 'admin', now(), 'admin', now(), null);\ninsert into sys_oss_config values (3, 'aliyun', 'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi',             '', 'oss-cn-beijing.aliyuncs.com',         '','N', '',            '1', '1', '', 'admin', now(), 'admin', now(), null);\ninsert into sys_oss_config values (4, 'qcloud', 'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi-1250000000',  '', 'cos.ap-beijing.myqcloud.com',         '','N', 'ap-beijing',  '1', '1', '', 'admin', now(), 'admin', now(), null);\ninsert into sys_oss_config values (5, 'image',  'ruoyi',            'ruoyi123',        'ruoyi',             'image', '127.0.0.1:9000',                 '','N', '',            '1', '1', '', 'admin', now(), 'admin', now(), NULL);\n\n-- 字符串自动转时间 避免框架时间查询报错问题\ncreate or replace function cast_varchar_to_timestamp(varchar) returns timestamptz as $$\nselect to_timestamp($1, 'yyyy-mm-dd hh24:mi:ss');\n$$ language sql strict ;\n\ncreate cast (varchar as timestamptz) with function cast_varchar_to_timestamp as IMPLICIT;\n"
  },
  {
    "path": "script/sql/postgres/postgres_test.sql",
    "content": "DROP TABLE if EXISTS test_demo;\ncreate table if not exists test_demo\n(\n    id          int8,\n    dept_id     int8,\n    user_id     int8,\n    order_num   int4    default 0,\n    test_key    varchar(255),\n    value       varchar(255),\n    version     int4    default 0,\n    create_time timestamp,\n    create_by   varchar(64),\n    update_time timestamp,\n    update_by   varchar(64),\n    del_flag    int4 default 0\n);\n\ncomment on table test_demo is '测试单表';\ncomment on column test_demo.id is '主键';\ncomment on column test_demo.dept_id is '部门id';\ncomment on column test_demo.user_id is '用户id';\ncomment on column test_demo.order_num is '排序号';\ncomment on column test_demo.test_key is 'key键';\ncomment on column test_demo.value is '值';\ncomment on column test_demo.version is '版本';\ncomment on column test_demo.create_time is '创建时间';\ncomment on column test_demo.create_by is '创建人';\ncomment on column test_demo.update_time is '更新时间';\ncomment on column test_demo.update_by is '更新人';\ncomment on column test_demo.del_flag is '删除标志';\n\nDROP TABLE if EXISTS test_tree;\ncreate table if not exists test_tree\n(\n    id          int8,\n    parent_id   int8    default 0,\n    dept_id     int8,\n    user_id     int8,\n    tree_name   varchar(255),\n    version     int4    default 0,\n    create_time timestamp,\n    create_by   varchar(64),\n    update_time timestamp,\n    update_by   varchar(64),\n    del_flag    integer default 0\n);\n\ncomment on table test_tree is '测试树表';\ncomment on column test_tree.id is '主键';\ncomment on column test_tree.parent_id is '父id';\ncomment on column test_tree.dept_id is '部门id';\ncomment on column test_tree.user_id is '用户id';\ncomment on column test_tree.tree_name is '值';\ncomment on column test_tree.version is '版本';\ncomment on column test_tree.create_time is '创建时间';\ncomment on column test_tree.create_by is '创建人';\ncomment on column test_tree.update_time is '更新时间';\ncomment on column test_tree.update_by is '更新人';\ncomment on column test_tree.del_flag is '删除标志';\n\nINSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (3, 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', now(), 'admin', now(), 'test', now(), NULL);\nINSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (4, 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', now(), 'admin', now(), 'test1', now(), NULL);\n\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (5, '测试菜单', 0, 5, 'demo', NULL, 1, 0, 'M', '0', '0', NULL, 'star', 'admin', now(), NULL, NULL, '');\n\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1500, '测试单表', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 'admin', now(), '', NULL, '测试单表菜单');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1501, '测试单表查询', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 'admin', now(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1502, '测试单表新增', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 'admin', now(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1503, '测试单表修改', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 'admin', now(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1504, '测试单表删除', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 'admin', now(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1505, '测试单表导出', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 'admin', now(), '', NULL, '');\n\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1506, '测试树表', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 'admin', now(), '', NULL, '测试树表菜单');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1507, '测试树表查询', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 'admin', now(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1508, '测试树表新增', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 'admin', now(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1509, '测试树表修改', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 'admin', now(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1510, '测试树表删除', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 'admin', now(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1511, '测试树表导出', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 'admin', now(), '', NULL, '');\n\nINSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (3, '本部门及以下', 'test1', 3, '4', 't', 't', '0', '0', 'admin', now(), 'admin', NULL, NULL);\nINSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (4, '仅本人', 'test2', 4, '5', 't', 't', '0', '0', 'admin', now(), 'admin', NULL, NULL);\n\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 5);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 100);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 101);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 102);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 103);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 104);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 105);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 106);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 107);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 108);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 500);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 501);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1001);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1002);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1003);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1004);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1005);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1006);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1007);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1008);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1009);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1010);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1011);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1012);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1013);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1014);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1015);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1016);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1017);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1018);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1019);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1020);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1021);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1022);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1023);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1024);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1025);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1026);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1027);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1028);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1029);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1030);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1031);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1032);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1033);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1034);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1035);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1036);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1037);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1038);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1039);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1040);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1041);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1042);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1043);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1044);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1045);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1500);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1501);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1502);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1503);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1504);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1505);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1506);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1507);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1508);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1509);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1510);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1511);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 5);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1500);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1501);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1502);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1503);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1504);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1505);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1506);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1507);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1508);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1509);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1510);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1511);\n\nINSERT INTO sys_user_role(user_id, role_id) VALUES (3, 3);\nINSERT INTO sys_user_role(user_id, role_id) VALUES (4, 4);\n\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 102, 4, 1, '测试数据权限', '测试', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 102, 3, 2, '子节点1', '111', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 102, 3, 3, '子节点2', '222', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 108, 4, 4, '测试数据', 'demo', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 108, 3, 13, '子节点11', '1111', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 108, 3, 12, '子节点22', '2222', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 108, 3, 11, '子节点33', '3333', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 108, 3, 10, '子节点44', '4444', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 108, 3, 9, '子节点55', '5555', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 108, 3, 8, '子节点66', '6666', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 108, 3, 7, '子节点77', '7777', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 108, 3, 6, '子节点88', '8888', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 108, 3, 5, '子节点99', '9999', 0, now(), 'admin', NULL, NULL, 0);\n\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 0, 102, 4, '测试数据权限', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 1, 102, 3, '子节点1', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 2, 102, 3, '子节点2', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 0, 108, 4, '测试树1', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 4, 108, 3, '子节点11', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 4, 108, 3, '子节点22', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 4, 108, 3, '子节点33', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 5, 108, 3, '子节点44', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 6, 108, 3, '子节点55', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 7, 108, 3, '子节点66', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 7, 108, 3, '子节点77', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 10, 108, 3, '子节点88', 0, now(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 10, 108, 3, '子节点99', 0, now(), 'admin', NULL, NULL, 0);\n"
  },
  {
    "path": "script/sql/ry_vue_4.X.sql",
    "content": "-- ----------------------------\n-- 1、部门表\n-- ----------------------------\ndrop table if exists sys_dept;\ncreate table sys_dept (\n  dept_id           bigint(20)      not null                   comment '部门id',\n  parent_id         bigint(20)      default 0                  comment '父部门id',\n  ancestors         varchar(500)    default ''                 comment '祖级列表',\n  dept_name         varchar(30)     default ''                 comment '部门名称',\n  order_num         int(4)          default 0                  comment '显示顺序',\n  leader            varchar(20)     default null               comment '负责人',\n  phone             varchar(11)     default null               comment '联系电话',\n  email             varchar(50)     default null               comment '邮箱',\n  status            char(1)         default '0'                comment '部门状态（0正常 1停用）',\n  del_flag          char(1)         default '0'                comment '删除标志（0代表存在 2代表删除）',\n  create_by         varchar(64)     default ''                 comment '创建者',\n  create_time       datetime                                   comment '创建时间',\n  update_by         varchar(64)     default ''                 comment '更新者',\n  update_time       datetime                                   comment '更新时间',\n  primary key (dept_id)\n) engine=innodb comment = '部门表';\n\n-- ----------------------------\n-- 初始化-部门表数据\n-- ----------------------------\ninsert into sys_dept values(100,  0,   '0',          '若依科技',   0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);\ninsert into sys_dept values(101,  100, '0,100',      '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);\ninsert into sys_dept values(102,  100, '0,100',      '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);\ninsert into sys_dept values(103,  101, '0,100,101',  '研发部门',   1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);\ninsert into sys_dept values(104,  101, '0,100,101',  '市场部门',   2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);\ninsert into sys_dept values(105,  101, '0,100,101',  '测试部门',   3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);\ninsert into sys_dept values(106,  101, '0,100,101',  '财务部门',   4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);\ninsert into sys_dept values(107,  101, '0,100,101',  '运维部门',   5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);\ninsert into sys_dept values(108,  102, '0,100,102',  '市场部门',   1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);\ninsert into sys_dept values(109,  102, '0,100,102',  '财务部门',   2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);\n\n\n-- ----------------------------\n-- 2、用户信息表\n-- ----------------------------\ndrop table if exists sys_user;\ncreate table sys_user (\n  user_id           bigint(20)      not null     comment '用户ID',\n  dept_id           bigint(20)      default null               comment '部门ID',\n  user_name         varchar(30)     not null                   comment '用户账号',\n  nick_name         varchar(30)     not null                   comment '用户昵称',\n  user_type         varchar(10)     default 'sys_user'         comment '用户类型（sys_user系统用户）',\n  email             varchar(50)     default ''                 comment '用户邮箱',\n  phonenumber       varchar(11)     default ''                 comment '手机号码',\n  sex               char(1)         default '0'                comment '用户性别（0男 1女 2未知）',\n  avatar            varchar(100)    default ''                 comment '头像地址',\n  password          varchar(100)    default ''                 comment '密码',\n  status            char(1)         default '0'                comment '帐号状态（0正常 1停用）',\n  del_flag          char(1)         default '0'                comment '删除标志（0代表存在 2代表删除）',\n  login_ip          varchar(128)    default ''                 comment '最后登录IP',\n  login_date        datetime                                   comment '最后登录时间',\n  create_by         varchar(64)     default ''                 comment '创建者',\n  create_time       datetime                                   comment '创建时间',\n  update_by         varchar(64)     default ''                 comment '更新者',\n  update_time       datetime                                   comment '更新时间',\n  remark            varchar(500)    default null               comment '备注',\n  primary key (user_id)\n) engine=innodb comment = '用户信息表';\n\n-- ----------------------------\n-- 初始化-用户信息表数据\n-- ----------------------------\ninsert into sys_user values(1,  103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '管理员');\ninsert into sys_user values(2,  105, 'lionli', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@qq.com',  '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '测试员');\n\n\n-- ----------------------------\n-- 3、岗位信息表\n-- ----------------------------\ndrop table if exists sys_post;\ncreate table sys_post\n(\n  post_id       bigint(20)      not null                   comment '岗位ID',\n  post_code     varchar(64)     not null                   comment '岗位编码',\n  post_name     varchar(50)     not null                   comment '岗位名称',\n  post_sort     int(4)          not null                   comment '显示顺序',\n  status        char(1)         not null                   comment '状态（0正常 1停用）',\n  create_by     varchar(64)     default ''                 comment '创建者',\n  create_time   datetime                                   comment '创建时间',\n  update_by     varchar(64)     default ''                 comment '更新者',\n  update_time   datetime                                   comment '更新时间',\n  remark        varchar(500)    default null               comment '备注',\n  primary key (post_id)\n) engine=innodb comment = '岗位信息表';\n\n-- ----------------------------\n-- 初始化-岗位信息表数据\n-- ----------------------------\ninsert into sys_post values(1, 'ceo',  '董事长',    1, '0', 'admin', sysdate(), '', null, '');\ninsert into sys_post values(2, 'se',   '项目经理',  2, '0', 'admin', sysdate(), '', null, '');\ninsert into sys_post values(3, 'hr',   '人力资源',  3, '0', 'admin', sysdate(), '', null, '');\ninsert into sys_post values(4, 'user', '普通员工',  4, '0', 'admin', sysdate(), '', null, '');\n\n\n-- ----------------------------\n-- 4、角色信息表\n-- ----------------------------\ndrop table if exists sys_role;\ncreate table sys_role (\n  role_id              bigint(20)      not null                   comment '角色ID',\n  role_name            varchar(30)     not null                   comment '角色名称',\n  role_key             varchar(100)    not null                   comment '角色权限字符串',\n  role_sort            int(4)          not null                   comment '显示顺序',\n  data_scope           char(1)         default '1'                comment '数据范围（1：全部数据权限 2：自定数据权限 3：本部门数据权限 4：本部门及以下数据权限）',\n  menu_check_strictly  tinyint(1)      default 1                  comment '菜单树选择项是否关联显示',\n  dept_check_strictly  tinyint(1)      default 1                  comment '部门树选择项是否关联显示',\n  status               char(1)         not null                   comment '角色状态（0正常 1停用）',\n  del_flag             char(1)         default '0'                comment '删除标志（0代表存在 2代表删除）',\n  create_by            varchar(64)     default ''                 comment '创建者',\n  create_time          datetime                                   comment '创建时间',\n  update_by            varchar(64)     default ''                 comment '更新者',\n  update_time          datetime                                   comment '更新时间',\n  remark               varchar(500)    default null               comment '备注',\n  primary key (role_id)\n) engine=innodb comment = '角色信息表';\n\n-- ----------------------------\n-- 初始化-角色信息表数据\n-- ----------------------------\ninsert into sys_role values('1', '超级管理员',  'admin',  1, 1, 1, 1, '0', '0', 'admin', sysdate(), '', null, '超级管理员');\ninsert into sys_role values('2', '普通角色',    'common', 2, 2, 1, 1, '0', '0', 'admin', sysdate(), '', null, '普通角色');\n\n\n-- ----------------------------\n-- 5、菜单权限表\n-- ----------------------------\ndrop table if exists sys_menu;\ncreate table sys_menu (\n  menu_id           bigint(20)      not null                   comment '菜单ID',\n  menu_name         varchar(50)     not null                   comment '菜单名称',\n  parent_id         bigint(20)      default 0                  comment '父菜单ID',\n  order_num         int(4)          default 0                  comment '显示顺序',\n  path              varchar(200)    default ''                 comment '路由地址',\n  component         varchar(255)    default null               comment '组件路径',\n  query_param       varchar(255)    default null               comment '路由参数',\n  is_frame          int(1)          default 1                  comment '是否为外链（0是 1否）',\n  is_cache          int(1)          default 0                  comment '是否缓存（0缓存 1不缓存）',\n  menu_type         char(1)         default ''                 comment '菜单类型（M目录 C菜单 F按钮）',\n  visible           char(1)         default 0                  comment '显示状态（0显示 1隐藏）',\n  status            char(1)         default 0                  comment '菜单状态（0正常 1停用）',\n  perms             varchar(100)    default null               comment '权限标识',\n  icon              varchar(100)    default '#'                comment '菜单图标',\n  create_by         varchar(64)     default ''                 comment '创建者',\n  create_time       datetime                                   comment '创建时间',\n  update_by         varchar(64)     default ''                 comment '更新者',\n  update_time       datetime                                   comment '更新时间',\n  remark            varchar(500)    default ''                 comment '备注',\n  primary key (menu_id)\n) engine=innodb comment = '菜单权限表';\n\n-- ----------------------------\n-- 初始化-菜单信息表数据\n-- ----------------------------\n-- 一级菜单\ninsert into sys_menu values('1', '系统管理', '0', '1', 'system',           null, '', 1, 0, 'M', '0', '0', '', 'system',   'admin', sysdate(), '', null, '系统管理目录');\ninsert into sys_menu values('2', '系统监控', '0', '2', 'monitor',          null, '', 1, 0, 'M', '0', '0', '', 'monitor',  'admin', sysdate(), '', null, '系统监控目录');\ninsert into sys_menu values('3', '系统工具', '0', '3', 'tool',             null, '', 1, 0, 'M', '0', '0', '', 'tool',     'admin', sysdate(), '', null, '系统工具目录');\ninsert into sys_menu values('4', 'PLUS官网', '0', '4', 'https://gitee.com/dromara/RuoYi-Vue-Plus', null, '', 0, 0, 'M', '0', '0', '', 'guide',    'admin', sysdate(), '', null, 'RuoYi-Vue-Plus官网地址');\n-- 二级菜单\ninsert into sys_menu values('100',  '用户管理', '1',   '1', 'user',       'system/user/index',        '', 1, 0, 'C', '0', '0', 'system:user:list',        'user',          'admin', sysdate(), '', null, '用户管理菜单');\ninsert into sys_menu values('101',  '角色管理', '1',   '2', 'role',       'system/role/index',        '', 1, 0, 'C', '0', '0', 'system:role:list',        'peoples',       'admin', sysdate(), '', null, '角色管理菜单');\ninsert into sys_menu values('102',  '菜单管理', '1',   '3', 'menu',       'system/menu/index',        '', 1, 0, 'C', '0', '0', 'system:menu:list',        'tree-table',    'admin', sysdate(), '', null, '菜单管理菜单');\ninsert into sys_menu values('103',  '部门管理', '1',   '4', 'dept',       'system/dept/index',        '', 1, 0, 'C', '0', '0', 'system:dept:list',        'tree',          'admin', sysdate(), '', null, '部门管理菜单');\ninsert into sys_menu values('104',  '岗位管理', '1',   '5', 'post',       'system/post/index',        '', 1, 0, 'C', '0', '0', 'system:post:list',        'post',          'admin', sysdate(), '', null, '岗位管理菜单');\ninsert into sys_menu values('105',  '字典管理', '1',   '6', 'dict',       'system/dict/index',        '', 1, 0, 'C', '0', '0', 'system:dict:list',        'dict',          'admin', sysdate(), '', null, '字典管理菜单');\ninsert into sys_menu values('106',  '参数设置', '1',   '7', 'config',     'system/config/index',      '', 1, 0, 'C', '0', '0', 'system:config:list',      'edit',          'admin', sysdate(), '', null, '参数设置菜单');\ninsert into sys_menu values('107',  '通知公告', '1',   '8', 'notice',     'system/notice/index',      '', 1, 0, 'C', '0', '0', 'system:notice:list',      'message',       'admin', sysdate(), '', null, '通知公告菜单');\ninsert into sys_menu values('108',  '日志管理', '1',   '9', 'log',        '',                         '', 1, 0, 'M', '0', '0', '',                        'log',           'admin', sysdate(), '', null, '日志管理菜单');\ninsert into sys_menu values('109',  '在线用户', '2',   '1', 'online',     'monitor/online/index',     '', 1, 0, 'C', '0', '0', 'monitor:online:list',     'online',        'admin', sysdate(), '', null, '在线用户菜单');\ninsert into sys_menu values('112',  '缓存列表', '2',   '6', 'cacheList',  'monitor/cache/list',       '', 1, 0, 'C', '0', '0', 'monitor:cache:list',      'redis-list',    'admin', sysdate(), '', null, '缓存列表菜单');\ninsert into sys_menu values('113',  '缓存监控', '2',   '5', 'cache',      'monitor/cache/index',      '', 1, 0, 'C', '0', '0', 'monitor:cache:list',      'redis',         'admin', sysdate(), '', null, '缓存监控菜单');\ninsert into sys_menu values('114',  '表单构建', '3',   '1', 'build',      'tool/build/index',         '', 1, 0, 'C', '0', '0', 'tool:build:list',         'build',         'admin', sysdate(), '', null, '表单构建菜单');\ninsert into sys_menu values('115',  '代码生成', '3',   '2', 'gen',        'tool/gen/index',           '', 1, 0, 'C', '0', '0', 'tool:gen:list',           'code',          'admin', sysdate(), '', null, '代码生成菜单');\n-- springboot-admin监控\ninsert into sys_menu values('117',  'Admin监控', '2',  '5', 'Admin',      'monitor/admin/index',      '', 1, 0, 'C', '0', '0', 'monitor:admin:list',      'dashboard',     'admin', sysdate(), '', null, 'Admin监控菜单');\n-- oss菜单\ninsert into sys_menu values('118',  '文件管理', '1', '10', 'oss', 'system/oss/index', '', 1, 0, 'C', '0', '0', 'system:oss:list', 'upload', 'admin', sysdate(), '', null, '文件管理菜单');\n-- xxl-job-admin控制台\ninsert into sys_menu values('120',  '任务调度中心', '2',  '5', 'XxlJob',      'monitor/xxljob/index',      '', 1, 0, 'C', '0', '0', 'monitor:xxljob:list',      'job',     'admin', sysdate(), '', null, 'Xxl-Job控制台菜单');\n\n-- 三级菜单\ninsert into sys_menu values('500',  '操作日志', '108', '1', 'operlog',    'monitor/operlog/index',    '', 1, 0, 'C', '0', '0', 'monitor:operlog:list',    'form',          'admin', sysdate(), '', null, '操作日志菜单');\ninsert into sys_menu values('501',  '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor',    'admin', sysdate(), '', null, '登录日志菜单');\n-- 用户管理按钮\ninsert into sys_menu values('1001', '用户查询', '100', '1',  '', '', '', 1, 0, 'F', '0', '0', 'system:user:query',          '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1002', '用户新增', '100', '2',  '', '', '', 1, 0, 'F', '0', '0', 'system:user:add',            '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1003', '用户修改', '100', '3',  '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit',           '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1004', '用户删除', '100', '4',  '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove',         '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1005', '用户导出', '100', '5',  '', '', '', 1, 0, 'F', '0', '0', 'system:user:export',         '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1006', '用户导入', '100', '6',  '', '', '', 1, 0, 'F', '0', '0', 'system:user:import',         '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1007', '重置密码', '100', '7',  '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd',       '#', 'admin', sysdate(), '', null, '');\n-- 角色管理按钮\ninsert into sys_menu values('1008', '角色查询', '101', '1',  '', '', '', 1, 0, 'F', '0', '0', 'system:role:query',          '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1009', '角色新增', '101', '2',  '', '', '', 1, 0, 'F', '0', '0', 'system:role:add',            '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1010', '角色修改', '101', '3',  '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit',           '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1011', '角色删除', '101', '4',  '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove',         '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1012', '角色导出', '101', '5',  '', '', '', 1, 0, 'F', '0', '0', 'system:role:export',         '#', 'admin', sysdate(), '', null, '');\n-- 菜单管理按钮\ninsert into sys_menu values('1013', '菜单查询', '102', '1',  '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query',          '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1014', '菜单新增', '102', '2',  '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add',            '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1015', '菜单修改', '102', '3',  '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit',           '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1016', '菜单删除', '102', '4',  '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove',         '#', 'admin', sysdate(), '', null, '');\n-- 部门管理按钮\ninsert into sys_menu values('1017', '部门查询', '103', '1',  '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query',          '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1018', '部门新增', '103', '2',  '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add',            '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1019', '部门修改', '103', '3',  '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit',           '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1020', '部门删除', '103', '4',  '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove',         '#', 'admin', sysdate(), '', null, '');\n-- 岗位管理按钮\ninsert into sys_menu values('1021', '岗位查询', '104', '1',  '', '', '', 1, 0, 'F', '0', '0', 'system:post:query',          '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1022', '岗位新增', '104', '2',  '', '', '', 1, 0, 'F', '0', '0', 'system:post:add',            '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1023', '岗位修改', '104', '3',  '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit',           '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1024', '岗位删除', '104', '4',  '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove',         '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1025', '岗位导出', '104', '5',  '', '', '', 1, 0, 'F', '0', '0', 'system:post:export',         '#', 'admin', sysdate(), '', null, '');\n-- 字典管理按钮\ninsert into sys_menu values('1026', '字典查询', '105', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:query',          '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1027', '字典新增', '105', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:add',            '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1028', '字典修改', '105', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit',           '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1029', '字典删除', '105', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove',         '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1030', '字典导出', '105', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:export',         '#', 'admin', sysdate(), '', null, '');\n-- 参数设置按钮\ninsert into sys_menu values('1031', '参数查询', '106', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:query',        '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1032', '参数新增', '106', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:add',          '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1033', '参数修改', '106', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:edit',         '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1034', '参数删除', '106', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:remove',       '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1035', '参数导出', '106', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:export',       '#', 'admin', sysdate(), '', null, '');\n-- 通知公告按钮\ninsert into sys_menu values('1036', '公告查询', '107', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:query',        '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1037', '公告新增', '107', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:add',          '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1038', '公告修改', '107', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit',         '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1039', '公告删除', '107', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove',       '#', 'admin', sysdate(), '', null, '');\n-- 操作日志按钮\ninsert into sys_menu values('1040', '操作查询', '500', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query',      '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1041', '操作删除', '500', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove',     '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export',     '#', 'admin', sysdate(), '', null, '');\n-- 登录日志按钮\ninsert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query',   '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove',  '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export',  '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock',  '#', 'admin', sysdate(), '', null, '');\n-- 在线用户按钮\ninsert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query',       '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 'admin', sysdate(), '', null, '');\n-- 代码生成按钮\ninsert into sys_menu values('1055', '生成查询', '115', '1', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query',             '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1056', '生成修改', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit',              '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1057', '生成删除', '115', '3', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove',            '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1058', '导入代码', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import',            '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1059', '预览代码', '115', '4', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview',           '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1060', '生成代码', '115', '5', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code',              '#', 'admin', sysdate(), '', null, '');\n-- oss相关按钮\ninsert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:query',        '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:upload',       '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:download',     '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:remove',       '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1604', '配置添加', '118', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:add',          '#', 'admin', sysdate(), '', null, '');\ninsert into sys_menu values('1605', '配置编辑', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:edit',         '#', 'admin', sysdate(), '', null, '');\n\n\n-- ----------------------------\n-- 6、用户和角色关联表  用户N-1角色\n-- ----------------------------\ndrop table if exists sys_user_role;\ncreate table sys_user_role (\n  user_id   bigint(20) not null comment '用户ID',\n  role_id   bigint(20) not null comment '角色ID',\n  primary key(user_id, role_id)\n) engine=innodb comment = '用户和角色关联表';\n\n-- ----------------------------\n-- 初始化-用户和角色关联表数据\n-- ----------------------------\ninsert into sys_user_role values ('1', '1');\ninsert into sys_user_role values ('2', '2');\n\n\n-- ----------------------------\n-- 7、角色和菜单关联表  角色1-N菜单\n-- ----------------------------\ndrop table if exists sys_role_menu;\ncreate table sys_role_menu (\n  role_id   bigint(20) not null comment '角色ID',\n  menu_id   bigint(20) not null comment '菜单ID',\n  primary key(role_id, menu_id)\n) engine=innodb comment = '角色和菜单关联表';\n\n-- ----------------------------\n-- 初始化-角色和菜单关联表数据\n-- ----------------------------\ninsert into sys_role_menu values ('2', '1');\ninsert into sys_role_menu values ('2', '2');\ninsert into sys_role_menu values ('2', '3');\ninsert into sys_role_menu values ('2', '4');\ninsert into sys_role_menu values ('2', '100');\ninsert into sys_role_menu values ('2', '101');\ninsert into sys_role_menu values ('2', '102');\ninsert into sys_role_menu values ('2', '103');\ninsert into sys_role_menu values ('2', '104');\ninsert into sys_role_menu values ('2', '105');\ninsert into sys_role_menu values ('2', '106');\ninsert into sys_role_menu values ('2', '107');\ninsert into sys_role_menu values ('2', '108');\ninsert into sys_role_menu values ('2', '109');\ninsert into sys_role_menu values ('2', '110');\ninsert into sys_role_menu values ('2', '111');\ninsert into sys_role_menu values ('2', '112');\ninsert into sys_role_menu values ('2', '113');\ninsert into sys_role_menu values ('2', '114');\ninsert into sys_role_menu values ('2', '115');\ninsert into sys_role_menu values ('2', '116');\ninsert into sys_role_menu values ('2', '500');\ninsert into sys_role_menu values ('2', '501');\ninsert into sys_role_menu values ('2', '1000');\ninsert into sys_role_menu values ('2', '1001');\ninsert into sys_role_menu values ('2', '1002');\ninsert into sys_role_menu values ('2', '1003');\ninsert into sys_role_menu values ('2', '1004');\ninsert into sys_role_menu values ('2', '1005');\ninsert into sys_role_menu values ('2', '1006');\ninsert into sys_role_menu values ('2', '1007');\ninsert into sys_role_menu values ('2', '1008');\ninsert into sys_role_menu values ('2', '1009');\ninsert into sys_role_menu values ('2', '1010');\ninsert into sys_role_menu values ('2', '1011');\ninsert into sys_role_menu values ('2', '1012');\ninsert into sys_role_menu values ('2', '1013');\ninsert into sys_role_menu values ('2', '1014');\ninsert into sys_role_menu values ('2', '1015');\ninsert into sys_role_menu values ('2', '1016');\ninsert into sys_role_menu values ('2', '1017');\ninsert into sys_role_menu values ('2', '1018');\ninsert into sys_role_menu values ('2', '1019');\ninsert into sys_role_menu values ('2', '1020');\ninsert into sys_role_menu values ('2', '1021');\ninsert into sys_role_menu values ('2', '1022');\ninsert into sys_role_menu values ('2', '1023');\ninsert into sys_role_menu values ('2', '1024');\ninsert into sys_role_menu values ('2', '1025');\ninsert into sys_role_menu values ('2', '1026');\ninsert into sys_role_menu values ('2', '1027');\ninsert into sys_role_menu values ('2', '1028');\ninsert into sys_role_menu values ('2', '1029');\ninsert into sys_role_menu values ('2', '1030');\ninsert into sys_role_menu values ('2', '1031');\ninsert into sys_role_menu values ('2', '1032');\ninsert into sys_role_menu values ('2', '1033');\ninsert into sys_role_menu values ('2', '1034');\ninsert into sys_role_menu values ('2', '1035');\ninsert into sys_role_menu values ('2', '1036');\ninsert into sys_role_menu values ('2', '1037');\ninsert into sys_role_menu values ('2', '1038');\ninsert into sys_role_menu values ('2', '1039');\ninsert into sys_role_menu values ('2', '1040');\ninsert into sys_role_menu values ('2', '1041');\ninsert into sys_role_menu values ('2', '1042');\ninsert into sys_role_menu values ('2', '1043');\ninsert into sys_role_menu values ('2', '1044');\ninsert into sys_role_menu values ('2', '1045');\ninsert into sys_role_menu values ('2', '1050');\ninsert into sys_role_menu values ('2', '1046');\ninsert into sys_role_menu values ('2', '1047');\ninsert into sys_role_menu values ('2', '1048');\ninsert into sys_role_menu values ('2', '1055');\ninsert into sys_role_menu values ('2', '1056');\ninsert into sys_role_menu values ('2', '1057');\ninsert into sys_role_menu values ('2', '1058');\ninsert into sys_role_menu values ('2', '1059');\ninsert into sys_role_menu values ('2', '1060');\n\n-- ----------------------------\n-- 8、角色和部门关联表  角色1-N部门\n-- ----------------------------\ndrop table if exists sys_role_dept;\ncreate table sys_role_dept (\n  role_id   bigint(20) not null comment '角色ID',\n  dept_id   bigint(20) not null comment '部门ID',\n  primary key(role_id, dept_id)\n) engine=innodb comment = '角色和部门关联表';\n\n-- ----------------------------\n-- 初始化-角色和部门关联表数据\n-- ----------------------------\ninsert into sys_role_dept values ('2', '100');\ninsert into sys_role_dept values ('2', '101');\ninsert into sys_role_dept values ('2', '105');\n\n\n-- ----------------------------\n-- 9、用户与岗位关联表  用户1-N岗位\n-- ----------------------------\ndrop table if exists sys_user_post;\ncreate table sys_user_post\n(\n  user_id   bigint(20) not null comment '用户ID',\n  post_id   bigint(20) not null comment '岗位ID',\n  primary key (user_id, post_id)\n) engine=innodb comment = '用户与岗位关联表';\n\n-- ----------------------------\n-- 初始化-用户与岗位关联表数据\n-- ----------------------------\ninsert into sys_user_post values ('1', '1');\ninsert into sys_user_post values ('2', '2');\n\n\n-- ----------------------------\n-- 10、操作日志记录\n-- ----------------------------\ndrop table if exists sys_oper_log;\ncreate table sys_oper_log (\n  oper_id           bigint(20)      not null                   comment '日志主键',\n  title             varchar(50)     default ''                 comment '模块标题',\n  business_type     int(2)          default 0                  comment '业务类型（0其它 1新增 2修改 3删除）',\n  method            varchar(100)    default ''                 comment '方法名称',\n  request_method    varchar(10)     default ''                 comment '请求方式',\n  operator_type     int(1)          default 0                  comment '操作类别（0其它 1后台用户 2手机端用户）',\n  oper_name         varchar(50)     default ''                 comment '操作人员',\n  dept_name         varchar(50)     default ''                 comment '部门名称',\n  oper_url          varchar(255)    default ''                 comment '请求URL',\n  oper_ip           varchar(128)    default ''                 comment '主机地址',\n  oper_location     varchar(255)    default ''                 comment '操作地点',\n  oper_param        varchar(2000)   default ''                 comment '请求参数',\n  json_result       varchar(2000)   default ''                 comment '返回参数',\n  status            int(1)          default 0                  comment '操作状态（0正常 1异常）',\n  error_msg         varchar(2000)   default ''                 comment '错误消息',\n  oper_time         datetime                                   comment '操作时间',\n  primary key (oper_id),\n  key idx_sys_oper_log_bt (business_type),\n  key idx_sys_oper_log_s  (status),\n  key idx_sys_oper_log_ot (oper_time)\n) engine=innodb comment = '操作日志记录';\n\n\n-- ----------------------------\n-- 11、字典类型表\n-- ----------------------------\ndrop table if exists sys_dict_type;\ncreate table sys_dict_type\n(\n  dict_id          bigint(20)      not null                   comment '字典主键',\n  dict_name        varchar(100)    default ''                 comment '字典名称',\n  dict_type        varchar(100)    default ''                 comment '字典类型',\n  status           char(1)         default '0'                comment '状态（0正常 1停用）',\n  create_by        varchar(64)     default ''                 comment '创建者',\n  create_time      datetime                                   comment '创建时间',\n  update_by        varchar(64)     default ''                 comment '更新者',\n  update_time      datetime                                   comment '更新时间',\n  remark           varchar(500)    default null               comment '备注',\n  primary key (dict_id),\n  unique (dict_type)\n) engine=innodb comment = '字典类型表';\n\ninsert into sys_dict_type values(1,  '用户性别', 'sys_user_sex',        '0', 'admin', sysdate(), '', null, '用户性别列表');\ninsert into sys_dict_type values(2,  '菜单状态', 'sys_show_hide',       '0', 'admin', sysdate(), '', null, '菜单状态列表');\ninsert into sys_dict_type values(3,  '系统开关', 'sys_normal_disable',  '0', 'admin', sysdate(), '', null, '系统开关列表');\ninsert into sys_dict_type values(6,  '系统是否', 'sys_yes_no',          '0', 'admin', sysdate(), '', null, '系统是否列表');\ninsert into sys_dict_type values(7,  '通知类型', 'sys_notice_type',     '0', 'admin', sysdate(), '', null, '通知类型列表');\ninsert into sys_dict_type values(8,  '通知状态', 'sys_notice_status',   '0', 'admin', sysdate(), '', null, '通知状态列表');\ninsert into sys_dict_type values(9,  '操作类型', 'sys_oper_type',       '0', 'admin', sysdate(), '', null, '操作类型列表');\ninsert into sys_dict_type values(10, '系统状态', 'sys_common_status',   '0', 'admin', sysdate(), '', null, '登录状态列表');\n\n\n-- ----------------------------\n-- 12、字典数据表\n-- ----------------------------\ndrop table if exists sys_dict_data;\ncreate table sys_dict_data\n(\n  dict_code        bigint(20)      not null                   comment '字典编码',\n  dict_sort        int(4)          default 0                  comment '字典排序',\n  dict_label       varchar(100)    default ''                 comment '字典标签',\n  dict_value       varchar(100)    default ''                 comment '字典键值',\n  dict_type        varchar(100)    default ''                 comment '字典类型',\n  css_class        varchar(100)    default null               comment '样式属性（其他样式扩展）',\n  list_class       varchar(100)    default null               comment '表格回显样式',\n  is_default       char(1)         default 'N'                comment '是否默认（Y是 N否）',\n  status           char(1)         default '0'                comment '状态（0正常 1停用）',\n  create_by        varchar(64)     default ''                 comment '创建者',\n  create_time      datetime                                   comment '创建时间',\n  update_by        varchar(64)     default ''                 comment '更新者',\n  update_time      datetime                                   comment '更新时间',\n  remark           varchar(500)    default null               comment '备注',\n  primary key (dict_code)\n) engine=innodb comment = '字典数据表';\n\ninsert into sys_dict_data values(1,  1,  '男',       '0',       'sys_user_sex',        '',   '',        'Y', '0', 'admin', sysdate(), '', null, '性别男');\ninsert into sys_dict_data values(2,  2,  '女',       '1',       'sys_user_sex',        '',   '',        'N', '0', 'admin', sysdate(), '', null, '性别女');\ninsert into sys_dict_data values(3,  3,  '未知',     '2',       'sys_user_sex',        '',   '',        'N', '0', 'admin', sysdate(), '', null, '性别未知');\ninsert into sys_dict_data values(4,  1,  '显示',     '0',       'sys_show_hide',       '',   'primary', 'Y', '0', 'admin', sysdate(), '', null, '显示菜单');\ninsert into sys_dict_data values(5,  2,  '隐藏',     '1',       'sys_show_hide',       '',   'danger',  'N', '0', 'admin', sysdate(), '', null, '隐藏菜单');\ninsert into sys_dict_data values(6,  1,  '正常',     '0',       'sys_normal_disable',  '',   'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态');\ninsert into sys_dict_data values(7,  2,  '停用',     '1',       'sys_normal_disable',  '',   'danger',  'N', '0', 'admin', sysdate(), '', null, '停用状态');\ninsert into sys_dict_data values(12, 1,  '是',       'Y',       'sys_yes_no',          '',   'primary', 'Y', '0', 'admin', sysdate(), '', null, '系统默认是');\ninsert into sys_dict_data values(13, 2,  '否',       'N',       'sys_yes_no',          '',   'danger',  'N', '0', 'admin', sysdate(), '', null, '系统默认否');\ninsert into sys_dict_data values(14, 1,  '通知',     '1',       'sys_notice_type',     '',   'warning', 'Y', '0', 'admin', sysdate(), '', null, '通知');\ninsert into sys_dict_data values(15, 2,  '公告',     '2',       'sys_notice_type',     '',   'success', 'N', '0', 'admin', sysdate(), '', null, '公告');\ninsert into sys_dict_data values(16, 1,  '正常',     '0',       'sys_notice_status',   '',   'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态');\ninsert into sys_dict_data values(17, 2,  '关闭',     '1',       'sys_notice_status',   '',   'danger',  'N', '0', 'admin', sysdate(), '', null, '关闭状态');\ninsert into sys_dict_data values(29, 99, '其他',     '0',       'sys_oper_type',       '',   'info',    'N', '0', 'admin', sysdate(), '', null, '其他操作');\ninsert into sys_dict_data values(18, 1,  '新增',     '1',       'sys_oper_type',       '',   'info',    'N', '0', 'admin', sysdate(), '', null, '新增操作');\ninsert into sys_dict_data values(19, 2,  '修改',     '2',       'sys_oper_type',       '',   'info',    'N', '0', 'admin', sysdate(), '', null, '修改操作');\ninsert into sys_dict_data values(20, 3,  '删除',     '3',       'sys_oper_type',       '',   'danger',  'N', '0', 'admin', sysdate(), '', null, '删除操作');\ninsert into sys_dict_data values(21, 4,  '授权',     '4',       'sys_oper_type',       '',   'primary', 'N', '0', 'admin', sysdate(), '', null, '授权操作');\ninsert into sys_dict_data values(22, 5,  '导出',     '5',       'sys_oper_type',       '',   'warning', 'N', '0', 'admin', sysdate(), '', null, '导出操作');\ninsert into sys_dict_data values(23, 6,  '导入',     '6',       'sys_oper_type',       '',   'warning', 'N', '0', 'admin', sysdate(), '', null, '导入操作');\ninsert into sys_dict_data values(24, 7,  '强退',     '7',       'sys_oper_type',       '',   'danger',  'N', '0', 'admin', sysdate(), '', null, '强退操作');\ninsert into sys_dict_data values(25, 8,  '生成代码', '8',       'sys_oper_type',       '',   'warning', 'N', '0', 'admin', sysdate(), '', null, '生成操作');\ninsert into sys_dict_data values(26, 9,  '清空数据', '9',       'sys_oper_type',       '',   'danger',  'N', '0', 'admin', sysdate(), '', null, '清空操作');\ninsert into sys_dict_data values(27, 1,  '成功',     '0',       'sys_common_status',   '',   'primary', 'N', '0', 'admin', sysdate(), '', null, '正常状态');\ninsert into sys_dict_data values(28, 2,  '失败',     '1',       'sys_common_status',   '',   'danger',  'N', '0', 'admin', sysdate(), '', null, '停用状态');\n\n\n-- ----------------------------\n-- 13、参数配置表\n-- ----------------------------\ndrop table if exists sys_config;\ncreate table sys_config (\n  config_id         bigint(20)      not null                   comment '参数主键',\n  config_name       varchar(100)    default ''                 comment '参数名称',\n  config_key        varchar(100)    default ''                 comment '参数键名',\n  config_value      varchar(500)    default ''                 comment '参数键值',\n  config_type       char(1)         default 'N'                comment '系统内置（Y是 N否）',\n  create_by         varchar(64)     default ''                 comment '创建者',\n  create_time       datetime                                   comment '创建时间',\n  update_by         varchar(64)     default ''                 comment '更新者',\n  update_time       datetime                                   comment '更新时间',\n  remark            varchar(500)    default null               comment '备注',\n  primary key (config_id)\n) engine=innodb comment = '参数配置表';\n\ninsert into sys_config values(1, '主框架页-默认皮肤样式名称',     'sys.index.skinName',            'skin-blue',     'Y', 'admin', sysdate(), '', null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' );\ninsert into sys_config values(2, '用户管理-账号初始密码',        'sys.user.initPassword',         '123456',        'Y', 'admin', sysdate(), '', null, '初始化密码 123456' );\ninsert into sys_config values(3, '主框架页-侧边栏主题',          'sys.index.sideTheme',           'theme-dark',    'Y', 'admin', sysdate(), '', null, '深色主题theme-dark，浅色主题theme-light' );\ninsert into sys_config values(4, '账号自助-验证码开关',          'sys.account.captchaEnabled',    'true',          'Y', 'admin', sysdate(), '', null, '是否开启验证码功能（true开启，false关闭）');\ninsert into sys_config values(5, '账号自助-是否开启用户注册功能',  'sys.account.registerUser',      'false',         'Y', 'admin', sysdate(), '', null, '是否开启注册用户功能（true开启，false关闭）');\ninsert into sys_config values(11, 'OSS预览列表资源开关',         'sys.oss.previewListResource',   'true',          'Y', 'admin', sysdate(), '', null, 'true:开启, false:关闭');\n\n\n-- ----------------------------\n-- 14、系统访问记录\n-- ----------------------------\ndrop table if exists sys_logininfor;\ncreate table sys_logininfor (\n  info_id        bigint(20)     not null                  comment '访问ID',\n  user_name      varchar(50)    default ''                comment '用户账号',\n  ipaddr         varchar(128)   default ''                comment '登录IP地址',\n  login_location varchar(255)   default ''                comment '登录地点',\n  browser        varchar(50)    default ''                comment '浏览器类型',\n  os             varchar(50)    default ''                comment '操作系统',\n  status         char(1)        default '0'               comment '登录状态（0成功 1失败）',\n  msg            varchar(255)   default ''                comment '提示消息',\n  login_time     datetime                                 comment '访问时间',\n  primary key (info_id),\n  key idx_sys_logininfor_s  (status),\n  key idx_sys_logininfor_lt (login_time)\n) engine=innodb comment = '系统访问记录';\n\n\n-- ----------------------------\n-- 17、通知公告表\n-- ----------------------------\ndrop table if exists sys_notice;\ncreate table sys_notice (\n  notice_id         bigint(20)      not null                   comment '公告ID',\n  notice_title      varchar(50)     not null                   comment '公告标题',\n  notice_type       char(1)         not null                   comment '公告类型（1通知 2公告）',\n  notice_content    longblob        default null               comment '公告内容',\n  status            char(1)         default '0'                comment '公告状态（0正常 1关闭）',\n  create_by         varchar(64)     default ''                 comment '创建者',\n  create_time       datetime                                   comment '创建时间',\n  update_by         varchar(64)     default ''                 comment '更新者',\n  update_time       datetime                                   comment '更新时间',\n  remark            varchar(255)    default null               comment '备注',\n  primary key (notice_id)\n) engine=innodb comment = '通知公告表';\n\n-- ----------------------------\n-- 初始化-公告信息表数据\n-- ----------------------------\ninsert into sys_notice values('1', '温馨提醒：2018-07-01 新版本发布啦', '2', '新版本内容', '0', 'admin', sysdate(), '', null, '管理员');\ninsert into sys_notice values('2', '维护通知：2018-07-01 系统凌晨维护', '1', '维护内容',   '0', 'admin', sysdate(), '', null, '管理员');\n\n\n-- ----------------------------\n-- 18、代码生成业务表\n-- ----------------------------\ndrop table if exists gen_table;\ncreate table gen_table (\n  table_id          bigint(20)      not null                   comment '编号',\n  table_name        varchar(200)    default ''                 comment '表名称',\n  table_comment     varchar(500)    default ''                 comment '表描述',\n  sub_table_name    varchar(64)     default null               comment '关联子表的表名',\n  sub_table_fk_name varchar(64)     default null               comment '子表关联的外键名',\n  class_name        varchar(100)    default ''                 comment '实体类名称',\n  tpl_category      varchar(200)    default 'crud'             comment '使用的模板（crud单表操作 tree树表操作）',\n  package_name      varchar(100)                               comment '生成包路径',\n  module_name       varchar(30)                                comment '生成模块名',\n  business_name     varchar(30)                                comment '生成业务名',\n  function_name     varchar(50)                                comment '生成功能名',\n  function_author   varchar(50)                                comment '生成功能作者',\n  gen_type          char(1)         default '0'                comment '生成代码方式（0zip压缩包 1自定义路径）',\n  gen_path          varchar(200)    default '/'                comment '生成路径（不填默认项目路径）',\n  options           varchar(1000)                              comment '其它生成选项',\n  create_by         varchar(64)     default ''                 comment '创建者',\n  create_time       datetime                                   comment '创建时间',\n  update_by         varchar(64)     default ''                 comment '更新者',\n  update_time       datetime                                   comment '更新时间',\n  remark            varchar(500)    default null               comment '备注',\n  primary key (table_id)\n) engine=innodb comment = '代码生成业务表';\n\n\n-- ----------------------------\n-- 19、代码生成业务表字段\n-- ----------------------------\ndrop table if exists gen_table_column;\ncreate table gen_table_column (\n  column_id         bigint(20)      not null                   comment '编号',\n  table_id          bigint(20)                                 comment '归属表编号',\n  column_name       varchar(200)                               comment '列名称',\n  column_comment    varchar(500)                               comment '列描述',\n  column_type       varchar(100)                               comment '列类型',\n  java_type         varchar(500)                               comment 'JAVA类型',\n  java_field        varchar(200)                               comment 'JAVA字段名',\n  is_pk             char(1)                                    comment '是否主键（1是）',\n  is_increment      char(1)                                    comment '是否自增（1是）',\n  is_required       char(1)                                    comment '是否必填（1是）',\n  is_insert         char(1)                                    comment '是否为插入字段（1是）',\n  is_edit           char(1)                                    comment '是否编辑字段（1是）',\n  is_list           char(1)                                    comment '是否列表字段（1是）',\n  is_query          char(1)                                    comment '是否查询字段（1是）',\n  query_type        varchar(200)    default 'EQ'               comment '查询方式（等于、不等于、大于、小于、范围）',\n  html_type         varchar(200)                               comment '显示类型（文本框、文本域、下拉框、复选框、单选框、日期控件）',\n  dict_type         varchar(200)    default ''                 comment '字典类型',\n  sort              int                                        comment '排序',\n  create_by         varchar(64)     default ''                 comment '创建者',\n  create_time       datetime                                   comment '创建时间',\n  update_by         varchar(64)     default ''                 comment '更新者',\n  update_time       datetime                                   comment '更新时间',\n  primary key (column_id)\n) engine=innodb comment = '代码生成业务表字段';\n\n-- ----------------------------\n-- OSS对象存储表\n-- ----------------------------\ndrop table if exists sys_oss;\ncreate table sys_oss (\n  oss_id          bigint(20)   not null                   comment '对象存储主键',\n  file_name       varchar(255) not null default ''        comment '文件名',\n  original_name   varchar(255) not null default ''        comment '原名',\n  file_suffix     varchar(10)  not null default ''        comment '文件后缀名',\n  url             varchar(500) not null                   comment 'URL地址',\n  create_time     datetime              default null      comment '创建时间',\n  create_by       varchar(64)           default ''        comment '上传人',\n  update_time     datetime              default null      comment '更新时间',\n  update_by       varchar(64)           default ''        comment '更新人',\n  service         varchar(20)  not null default 'minio'   comment '服务商',\n  primary key (oss_id)\n) engine=innodb comment ='OSS对象存储表';\n\n-- ----------------------------\n-- OSS对象存储动态配置表\n-- ----------------------------\ndrop table if exists sys_oss_config;\ncreate table sys_oss_config (\n  oss_config_id   bigint(20)   not null                   comment '主建',\n  config_key      varchar(20)  not null   default ''      comment '配置key',\n  access_key      varchar(255)            default ''      comment 'accessKey',\n  secret_key      varchar(255)            default ''      comment '秘钥',\n  bucket_name     varchar(255)            default ''      comment '桶名称',\n  prefix           varchar(255)           default ''      comment '前缀',\n  endpoint         varchar(255)           default ''      comment '访问站点',\n  domain           varchar(255)           default ''      comment '自定义域名',\n  is_https         char(1)                default 'N'     comment '是否https（Y=是,N=否）',\n  region           varchar(255)           default ''      comment '域',\n  access_policy    char(1)     not null   default '1'     comment '桶权限类型(0=private 1=public 2=custom)',\n  status           char(1)                default '1'     comment '是否默认（0=是,1=否）',\n  ext1             varchar(255)           default ''      comment '扩展字段',\n  create_by       varchar(64)             default ''      comment '创建者',\n  create_time     datetime                default null    comment '创建时间',\n  update_by       varchar(64)             default ''      comment '更新者',\n  update_time     datetime                default null    comment '更新时间',\n  remark           varchar(500)           default null    comment '备注',\n  primary key (oss_config_id)\n) engine=innodb comment='对象存储配置表';\n\ninsert into sys_oss_config values (1, 'minio',  'ruoyi',            'ruoyi123',        'ruoyi',             '', '127.0.0.1:9000',                '','N', '',             '1' ,'0', '', 'admin', sysdate(), 'admin', sysdate(), NULL);\ninsert into sys_oss_config values (2, 'qiniu',  'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi',             '', 's3-cn-north-1.qiniucs.com',     '','N', '',             '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);\ninsert into sys_oss_config values (3, 'aliyun', 'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi',             '', 'oss-cn-beijing.aliyuncs.com',   '','N', '',             '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);\ninsert into sys_oss_config values (4, 'qcloud', 'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi-1250000000',  '', 'cos.ap-beijing.myqcloud.com',   '','N', 'ap-beijing',   '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);\ninsert into sys_oss_config values (5, 'image',  'ruoyi',            'ruoyi123',        'ruoyi',             'image', '127.0.0.1:9000',           '','N', '',             '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);\n"
  },
  {
    "path": "script/sql/sqlserver/sqlserver_ry_vue_4.X.sql",
    "content": "\nCREATE TABLE gen_table\n(\n    table_id          bigint                         NOT NULL,\n    table_name        nvarchar(200) DEFAULT ''       NULL,\n    table_comment     nvarchar(500) DEFAULT ''       NULL,\n    sub_table_name    nvarchar(64)                   NULL,\n    sub_table_fk_name nvarchar(64)                   NULL,\n    class_name        nvarchar(100) DEFAULT ''       NULL,\n    tpl_category      nvarchar(200) DEFAULT ('crud') NULL,\n    package_name      nvarchar(100)                  NULL,\n    module_name       nvarchar(30)                   NULL,\n    business_name     nvarchar(30)                   NULL,\n    function_name     nvarchar(50)                   NULL,\n    function_author   nvarchar(50)                   NULL,\n    gen_type          nchar(1)      DEFAULT ('0')    NULL,\n    gen_path          nvarchar(200) DEFAULT ('/')    NULL,\n    options           nvarchar(1000)                 NULL,\n    create_by         nvarchar(64)  DEFAULT ''       NULL,\n    create_time       datetime2(7)                   NULL,\n    update_by         nvarchar(64)  DEFAULT ''       NULL,\n    update_time       datetime2(7)                   NULL,\n    remark            nvarchar(500)                  NULL,\n    CONSTRAINT PK__gen_tabl__B21E8F2427725F8A PRIMARY KEY CLUSTERED (table_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'编号' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'table_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'表名称' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'table_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'表描述' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'table_comment'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'关联子表的表名' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'sub_table_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'子表关联的外键名' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'sub_table_fk_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'实体类名称' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'class_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'使用的模板（crud单表操作 tree树表操作）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'tpl_category'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'生成包路径' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'package_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'生成模块名' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'module_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'生成业务名' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'business_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'生成功能名' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'function_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'生成功能作者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'function_author'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'生成代码方式（0zip压缩包 1自定义路径）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'gen_type'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'生成路径（不填默认项目路径）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'gen_path'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'其它生成选项' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'options'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'create_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'create_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'update_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'update_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'备注' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table',\n    'COLUMN', N'remark'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'代码生成业务表' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table'\nGO\n\nCREATE TABLE gen_table_column\n(\n    column_id      bigint                       NOT NULL,\n    table_id       bigint                       NULL,\n    column_name    nvarchar(200)                NULL,\n    column_comment nvarchar(500)                NULL,\n    column_type    nvarchar(100)                NULL,\n    java_type      nvarchar(500)                NULL,\n    java_field     nvarchar(200)                NULL,\n    is_pk          nchar(1)                     NULL,\n    is_increment   nchar(1)                     NULL,\n    is_required    nchar(1)                     NULL,\n    is_insert      nchar(1)                     NULL,\n    is_edit        nchar(1)                     NULL,\n    is_list        nchar(1)                     NULL,\n    is_query       nchar(1)                     NULL,\n    query_type     nvarchar(200) DEFAULT ('EQ') NULL,\n    html_type      nvarchar(200)                NULL,\n    dict_type      nvarchar(200) DEFAULT ''     NULL,\n    sort           int                          NULL,\n    create_by      nvarchar(64)  DEFAULT ''     NULL,\n    create_time    datetime2(7)                 NULL,\n    update_by      nvarchar(64)  DEFAULT ''     NULL,\n    update_time    datetime2(7)                 NULL,\n    CONSTRAINT PK__gen_tabl__E301851F2E68B4E8 PRIMARY KEY CLUSTERED (column_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'编号' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'column_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'归属表编号' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'table_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'列名称' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'column_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'列描述' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'column_comment'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'列类型' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'column_type'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'JAVA类型' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'java_type'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'JAVA字段名' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'java_field'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'是否主键（1是）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'is_pk'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'是否自增（1是）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'is_increment'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'是否必填（1是）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'is_required'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'是否为插入字段（1是）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'is_insert'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'是否编辑字段（1是）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'is_edit'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'是否列表字段（1是）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'is_list'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'是否查询字段（1是）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'is_query'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'查询方式（等于、不等于、大于、小于、范围）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'query_type'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'显示类型（文本框、文本域、下拉框、复选框、单选框、日期控件）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'html_type'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'字典类型' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'dict_type'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'排序' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'sort'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'create_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'create_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'update_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column',\n    'COLUMN', N'update_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'代码生成业务表字段' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'gen_table_column'\nGO\n\nCREATE TABLE sys_config\n(\n    config_id    bigint                      NOT NULL,\n    config_name  nvarchar(100) DEFAULT ''    NULL,\n    config_key   nvarchar(100) DEFAULT ''    NULL,\n    config_value nvarchar(500) DEFAULT ''    NULL,\n    config_type  nchar(1)      DEFAULT ('N') NULL,\n    create_by    nvarchar(64)  DEFAULT ''    NULL,\n    create_time  datetime2(7)                NULL,\n    update_by    nvarchar(64)  DEFAULT ''    NULL,\n    update_time  datetime2(7)                NULL,\n    remark       nvarchar(500)               NULL,\n    CONSTRAINT PK__sys_conf__4AD1BFF182643682 PRIMARY KEY CLUSTERED (config_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'参数主键' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_config',\n    'COLUMN', N'config_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'参数名称' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_config',\n    'COLUMN', N'config_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'参数键名' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_config',\n    'COLUMN', N'config_key'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'参数键值' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_config',\n    'COLUMN', N'config_value'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'系统内置（Y是 N否）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_config',\n    'COLUMN', N'config_type'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_config',\n    'COLUMN', N'create_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_config',\n    'COLUMN', N'create_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_config',\n    'COLUMN', N'update_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_config',\n    'COLUMN', N'update_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'备注' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_config',\n    'COLUMN', N'remark'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'参数配置表' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_config'\nGO\n\nINSERT sys_config VALUES (1, N'主框架页-默认皮肤样式名称', N'sys.index.skinName', N'skin-blue', N'Y', N'admin', getdate(), N'', NULL, N'蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow')\nGO\nINSERT sys_config VALUES (2, N'用户管理-账号初始密码', N'sys.user.initPassword', N'123456', N'Y', N'admin', getdate(), N'', NULL, N'初始化密码 123456')\nGO\nINSERT sys_config VALUES (3, N'主框架页-侧边栏主题', N'sys.index.sideTheme', N'theme-dark', N'Y', N'admin', getdate(), N'', NULL, N'深色主题theme-dark，浅色主题theme-light')\nGO\nINSERT sys_config VALUES (4, N'账号自助-验证码开关', N'sys.account.captchaEnabled', N'true', N'Y', N'admin', getdate(), N'', NULL, N'是否开启验证码功能（true开启，false关闭）')\nGO\nINSERT sys_config VALUES (5, N'账号自助-是否开启用户注册功能', N'sys.account.registerUser', N'false', N'Y', N'admin', getdate(), N'', NULL, N'是否开启注册用户功能（true开启，false关闭）')\nGO\nINSERT sys_config VALUES (11, N'OSS预览列表资源开关', N'sys.oss.previewListResource', N'true', N'Y', N'admin', getdate(), N'', null, N'true:开启, false:关闭');\nGO\n\nCREATE TABLE sys_dept\n(\n    dept_id     bigint                     NOT NULL,\n    parent_id   bigint       DEFAULT ((0)) NULL,\n    ancestors   nvarchar(500)DEFAULT ''    NULL,\n    dept_name   nvarchar(30) DEFAULT ''    NULL,\n    order_num   int          DEFAULT ((0)) NULL,\n    leader      nvarchar(20)               NULL,\n    phone       nvarchar(11)               NULL,\n    email       nvarchar(50)               NULL,\n    status      nchar(1)     DEFAULT ('0') NULL,\n    del_flag    nchar(1)     DEFAULT ('0') NULL,\n    create_by   nvarchar(64) DEFAULT ''    NULL,\n    create_time datetime2(7)               NULL,\n    update_by   nvarchar(64) DEFAULT ''    NULL,\n    update_time datetime2(7)               NULL,\n    CONSTRAINT PK__sys_dept__DCA659747DE13804 PRIMARY KEY CLUSTERED (dept_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'部门id' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dept',\n    'COLUMN', N'dept_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'父部门id' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dept',\n    'COLUMN', N'parent_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'祖级列表' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dept',\n    'COLUMN', N'ancestors'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'部门名称' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dept',\n    'COLUMN', N'dept_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'显示顺序' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dept',\n    'COLUMN', N'order_num'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'负责人' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dept',\n    'COLUMN', N'leader'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'联系电话' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dept',\n    'COLUMN', N'phone'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'邮箱' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dept',\n    'COLUMN', N'email'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'部门状态（0正常 1停用）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dept',\n    'COLUMN', N'status'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'删除标志（0代表存在 2代表删除）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dept',\n    'COLUMN', N'del_flag'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dept',\n    'COLUMN', N'create_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dept',\n    'COLUMN', N'create_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dept',\n    'COLUMN', N'update_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dept',\n    'COLUMN', N'update_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'部门表' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dept'\nGO\n\nINSERT sys_dept VALUES (100, 0, N'0', N'若依科技', 0, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL)\nGO\nINSERT sys_dept VALUES (101, 100, N'0,100', N'深圳总公司', 1, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL)\nGO\nINSERT sys_dept VALUES (102, 100, N'0,100', N'长沙分公司', 2, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL)\nGO\nINSERT sys_dept VALUES (103, 101, N'0,100,101', N'研发部门', 1, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL)\nGO\nINSERT sys_dept VALUES (104, 101, N'0,100,101', N'市场部门', 2, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL)\nGO\nINSERT sys_dept VALUES (105, 101, N'0,100,101', N'测试部门', 3, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL)\nGO\nINSERT sys_dept VALUES (106, 101, N'0,100,101', N'财务部门', 4, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL)\nGO\nINSERT sys_dept VALUES (107, 101, N'0,100,101', N'运维部门', 5, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL)\nGO\nINSERT sys_dept VALUES (108, 102, N'0,100,102', N'市场部门', 1, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL)\nGO\nINSERT sys_dept VALUES (109, 102, N'0,100,102', N'财务部门', 2, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL)\nGO\n\nCREATE TABLE sys_dict_data\n(\n    dict_code   bigint                      NOT NULL,\n    dict_sort   int           DEFAULT ((0)) NULL,\n    dict_label  nvarchar(100) DEFAULT ''    NULL,\n    dict_value  nvarchar(100) DEFAULT ''    NULL,\n    dict_type   nvarchar(100) DEFAULT ''    NULL,\n    css_class   nvarchar(100)               NULL,\n    list_class  nvarchar(100)               NULL,\n    is_default  nchar(1)      DEFAULT ('N') NULL,\n    status      nchar(1)      DEFAULT ('0') NULL,\n    create_by   nvarchar(64)  DEFAULT ''    NULL,\n    create_time datetime2(7)                NULL,\n    update_by   nvarchar(64)  DEFAULT ''    NULL,\n    update_time datetime2(7)                NULL,\n    remark      nvarchar(500)               NULL,\n    CONSTRAINT PK__sys_dict__19CBC34B661AF3B3 PRIMARY KEY CLUSTERED (dict_code)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'字典编码' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_data',\n    'COLUMN', N'dict_code'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'字典排序' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_data',\n    'COLUMN', N'dict_sort'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'字典标签' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_data',\n    'COLUMN', N'dict_label'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'字典键值' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_data',\n    'COLUMN', N'dict_value'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'字典类型' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_data',\n    'COLUMN', N'dict_type'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'样式属性（其他样式扩展）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_data',\n    'COLUMN', N'css_class'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'表格回显样式' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_data',\n    'COLUMN', N'list_class'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'是否默认（Y是 N否）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_data',\n    'COLUMN', N'is_default'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'状态（0正常 1停用）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_data',\n    'COLUMN', N'status'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_data',\n    'COLUMN', N'create_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_data',\n    'COLUMN', N'create_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_data',\n    'COLUMN', N'update_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_data',\n    'COLUMN', N'update_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'备注' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_data',\n    'COLUMN', N'remark'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'字典数据表' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_data'\nGO\n\nINSERT sys_dict_data VALUES (1, 1, N'男', N'0', N'sys_user_sex', N'', N'', N'Y', N'0', N'admin', getdate(), N'', NULL, N'性别男')\nGO\nINSERT sys_dict_data VALUES (2, 2, N'女', N'1', N'sys_user_sex', N'', N'', N'N', N'0', N'admin', getdate(), N'', NULL, N'性别女')\nGO\nINSERT sys_dict_data VALUES (3, 3, N'未知', N'2', N'sys_user_sex', N'', N'', N'N', N'0', N'admin', getdate(), N'', NULL, N'性别未知')\nGO\nINSERT sys_dict_data VALUES (4, 1, N'显示', N'0', N'sys_show_hide', N'', N'primary', N'Y', N'0', N'admin', getdate(), N'', NULL, N'显示菜单')\nGO\nINSERT sys_dict_data VALUES (5, 2, N'隐藏', N'1', N'sys_show_hide', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'隐藏菜单')\nGO\nINSERT sys_dict_data VALUES (6, 1, N'正常', N'0', N'sys_normal_disable', N'', N'primary', N'Y', N'0', N'admin', getdate(), N'', NULL, N'正常状态')\nGO\nINSERT sys_dict_data VALUES (7, 2, N'停用', N'1', N'sys_normal_disable', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'停用状态')\nGO\nINSERT sys_dict_data VALUES (12, 1, N'是', N'Y', N'sys_yes_no', N'', N'primary', N'Y', N'0', N'admin', getdate(), N'', NULL, N'系统默认是')\nGO\nINSERT sys_dict_data VALUES (13, 2, N'否', N'N', N'sys_yes_no', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'系统默认否')\nGO\nINSERT sys_dict_data VALUES (14, 1, N'通知', N'1', N'sys_notice_type', N'', N'warning', N'Y', N'0', N'admin', getdate(), N'', NULL, N'通知')\nGO\nINSERT sys_dict_data VALUES (15, 2, N'公告', N'2', N'sys_notice_type', N'', N'success', N'N', N'0', N'admin', getdate(), N'', NULL, N'公告')\nGO\nINSERT sys_dict_data VALUES (16, 1, N'正常', N'0', N'sys_notice_status', N'', N'primary', N'Y', N'0', N'admin', getdate(), N'', NULL, N'正常状态')\nGO\nINSERT sys_dict_data VALUES (17, 2, N'关闭', N'1', N'sys_notice_status', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'关闭状态')\nGO\nINSERT sys_dict_data VALUES (29, 99, N'其他', N'0', N'sys_oper_type', N'', N'info', N'N', N'0', N'admin', getdate(), N'', NULL, N'其他操作');\nGO\nINSERT sys_dict_data VALUES (18, 1, N'新增', N'1', N'sys_oper_type', N'', N'info', N'N', N'0', N'admin', getdate(), N'', NULL, N'新增操作')\nGO\nINSERT sys_dict_data VALUES (19, 2, N'修改', N'2', N'sys_oper_type', N'', N'info', N'N', N'0', N'admin', getdate(), N'', NULL, N'修改操作')\nGO\nINSERT sys_dict_data VALUES (20, 3, N'删除', N'3', N'sys_oper_type', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'删除操作')\nGO\nINSERT sys_dict_data VALUES (21, 4, N'授权', N'4', N'sys_oper_type', N'', N'primary', N'N', N'0', N'admin', getdate(), N'', NULL, N'授权操作')\nGO\nINSERT sys_dict_data VALUES (22, 5, N'导出', N'5', N'sys_oper_type', N'', N'warning', N'N', N'0', N'admin', getdate(), N'', NULL, N'导出操作')\nGO\nINSERT sys_dict_data VALUES (23, 6, N'导入', N'6', N'sys_oper_type', N'', N'warning', N'N', N'0', N'admin', getdate(), N'', NULL, N'导入操作')\nGO\nINSERT sys_dict_data VALUES (24, 7, N'强退', N'7', N'sys_oper_type', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'强退操作')\nGO\nINSERT sys_dict_data VALUES (25, 8, N'生成代码', N'8', N'sys_oper_type', N'', N'warning', N'N', N'0', N'admin', getdate(), N'', NULL, N'生成操作')\nGO\nINSERT sys_dict_data VALUES (26, 9, N'清空数据', N'9', N'sys_oper_type', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'清空操作')\nGO\nINSERT sys_dict_data VALUES (27, 1, N'成功', N'0', N'sys_common_status', N'', N'primary', N'N', N'0', N'admin', getdate(), N'', NULL, N'正常状态')\nGO\nINSERT sys_dict_data VALUES (28, 2, N'失败', N'1', N'sys_common_status', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'停用状态')\nGO\n\nCREATE TABLE sys_dict_type\n(\n    dict_id     bigint                      NOT NULL,\n    dict_name   nvarchar(100) DEFAULT ''    NULL,\n    dict_type   nvarchar(100) DEFAULT ''    NULL,\n    status      nchar(1)      DEFAULT ('0') NULL,\n    create_by   nvarchar(64)  DEFAULT ''    NULL,\n    create_time datetime2(7)                NULL,\n    update_by   nvarchar(64)  DEFAULT ''    NULL,\n    update_time datetime2(7)                NULL,\n    remark      nvarchar(500)               NULL,\n    CONSTRAINT PK__sys_dict__3BD4186C409C5391 PRIMARY KEY CLUSTERED (dict_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nCREATE NONCLUSTERED INDEX sys_dict_type_index1 ON sys_dict_type (dict_type)\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'字典主键' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_type',\n    'COLUMN', N'dict_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'字典名称' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_type',\n    'COLUMN', N'dict_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'字典类型' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_type',\n    'COLUMN', N'dict_type'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'状态（0正常 1停用）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_type',\n    'COLUMN', N'status'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_type',\n    'COLUMN', N'create_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_type',\n    'COLUMN', N'create_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_type',\n    'COLUMN', N'update_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_type',\n    'COLUMN', N'update_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'备注' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_type',\n    'COLUMN', N'remark'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'字典类型表' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_dict_type'\nGO\n\nINSERT sys_dict_type VALUES (1, N'用户性别', N'sys_user_sex', N'0', N'admin', getdate(), N'', NULL, N'用户性别列表')\nGO\nINSERT sys_dict_type VALUES (2, N'菜单状态', N'sys_show_hide', N'0', N'admin', getdate(), N'', NULL, N'菜单状态列表')\nGO\nINSERT sys_dict_type VALUES (3, N'系统开关', N'sys_normal_disable', N'0', N'admin', getdate(), N'', NULL, N'系统开关列表')\nGO\nINSERT sys_dict_type VALUES (6, N'系统是否', N'sys_yes_no', N'0', N'admin', getdate(), N'', NULL, N'系统是否列表')\nGO\nINSERT sys_dict_type VALUES (7, N'通知类型', N'sys_notice_type', N'0', N'admin', getdate(), N'', NULL, N'通知类型列表')\nGO\nINSERT sys_dict_type VALUES (8, N'通知状态', N'sys_notice_status', N'0', N'admin', getdate(), N'', NULL, N'通知状态列表')\nGO\nINSERT sys_dict_type VALUES (9, N'操作类型', N'sys_oper_type', N'0', N'admin', getdate(), N'', NULL, N'操作类型列表')\nGO\nINSERT sys_dict_type VALUES (10, N'系统状态', N'sys_common_status', N'0', N'admin', getdate(), N'', NULL, N'登录状态列表')\nGO\n\nCREATE TABLE sys_logininfor\n(\n    info_id        bigint                      NOT NULL,\n    user_name      nvarchar(50)  DEFAULT ''    NULL,\n    ipaddr         nvarchar(128) DEFAULT ''    NULL,\n    login_location nvarchar(255) DEFAULT ''    NULL,\n    browser        nvarchar(50)  DEFAULT ''    NULL,\n    os             nvarchar(50)  DEFAULT ''    NULL,\n    status         nchar(1)      DEFAULT ('0') NULL,\n    msg            nvarchar(255) DEFAULT ''    NULL,\n    login_time     datetime2(7)                NULL,\n    CONSTRAINT PK__sys_logi__3D8A9C1A1854AE10 PRIMARY KEY CLUSTERED (info_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nCREATE NONCLUSTERED INDEX idx_sys_logininfor_s ON sys_logininfor (status)\nGO\nCREATE NONCLUSTERED INDEX idx_sys_logininfor_lt ON sys_logininfor (login_time)\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'访问ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_logininfor',\n    'COLUMN', N'info_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'用户账号' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_logininfor',\n    'COLUMN', N'user_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'登录IP地址' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_logininfor',\n    'COLUMN', N'ipaddr'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'登录地点' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_logininfor',\n    'COLUMN', N'login_location'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'浏览器类型' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_logininfor',\n    'COLUMN', N'browser'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'操作系统' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_logininfor',\n    'COLUMN', N'os'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'登录状态（0成功 1失败）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_logininfor',\n    'COLUMN', N'status'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'提示消息' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_logininfor',\n    'COLUMN', N'msg'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'访问时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_logininfor',\n    'COLUMN', N'login_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'系统访问记录' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_logininfor'\nGO\n\nCREATE TABLE sys_menu\n(\n    menu_id     bigint                      NOT NULL,\n    menu_name   nvarchar(50)                NOT NULL,\n    parent_id   bigint        DEFAULT ((0)) NULL,\n    order_num   int           DEFAULT ((0)) NULL,\n    path        nvarchar(200) DEFAULT ''    NULL,\n    component   nvarchar(255)               NULL,\n    query_param nvarchar(255)               NULL,\n    is_frame    int           DEFAULT ((1)) NULL,\n    is_cache    int           DEFAULT ((0)) NULL,\n    menu_type   nchar(1)      DEFAULT ''    NULL,\n    visible     nchar(1)      DEFAULT ((0)) NULL,\n    status      nchar(1)      DEFAULT ((0)) NULL,\n    perms       nvarchar(100)               NULL,\n    icon        nvarchar(100) DEFAULT ('#') NULL,\n    create_by   nvarchar(64)  DEFAULT ''    NULL,\n    create_time datetime2(7)                NULL,\n    update_by   nvarchar(64)  DEFAULT ''    NULL,\n    update_time datetime2(7)                NULL,\n    remark      nvarchar(500) DEFAULT ''    NULL,\n    CONSTRAINT PK__sys_menu__4CA0FADCF8545C58 PRIMARY KEY CLUSTERED (menu_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'菜单ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'menu_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'菜单名称' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'menu_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'父菜单ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'parent_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'显示顺序' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'order_num'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'路由地址' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'path'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'组件路径' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'component'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'路由参数' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'query_param'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'是否为外链（0是 1否）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'is_frame'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'是否缓存（0缓存 1不缓存）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'is_cache'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'菜单类型（M目录 C菜单 F按钮）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'menu_type'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'显示状态（0显示 1隐藏）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'visible'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'菜单状态（0正常 1停用）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'status'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'权限标识' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'perms'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'菜单图标' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'icon'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'create_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'create_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'update_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'update_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'备注' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu',\n    'COLUMN', N'remark'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'菜单权限表' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_menu'\nGO\n\nINSERT sys_menu VALUES (1, N'系统管理', 0, 1, N'system', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'system', N'admin', getdate(), N'', NULL, N'系统管理目录')\nGO\nINSERT sys_menu VALUES (2, N'系统监控', 0, 2, N'monitor', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'monitor', N'admin', getdate(), N'', NULL, N'系统监控目录')\nGO\nINSERT sys_menu VALUES (3, N'系统工具', 0, 3, N'tool', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'tool', N'admin', getdate(), N'', NULL, N'系统工具目录')\nGO\nINSERT sys_menu VALUES (4, N'PLUS官网', 0, 5, N'https://gitee.com/dromara/RuoYi-Vue-Plus', null, N'', 0, 0, N'M', N'0', N'0', N'', N'guide', N'admin', getdate(), N'', null, N'RuoYi-Vue-Plus官网地址');\nGO\nINSERT sys_menu VALUES (100, N'用户管理', 1, 1, N'user', N'system/user/index', N'', 1, 0, N'C', N'0', N'0', N'system:user:list', N'user', N'admin', getdate(), N'', NULL, N'用户管理菜单')\nGO\nINSERT sys_menu VALUES (101, N'角色管理', 1, 2, N'role', N'system/role/index', N'', 1, 0, N'C', N'0', N'0', N'system:role:list', N'peoples', N'admin', getdate(), N'', NULL, N'角色管理菜单')\nGO\nINSERT sys_menu VALUES (102, N'菜单管理', 1, 3, N'menu', N'system/menu/index', N'', 1, 0, N'C', N'0', N'0', N'system:menu:list', N'tree-table', N'admin', getdate(), N'', NULL, N'菜单管理菜单')\nGO\nINSERT sys_menu VALUES (103, N'部门管理', 1, 4, N'dept', N'system/dept/index', N'', 1, 0, N'C', N'0', N'0', N'system:dept:list', N'tree', N'admin', getdate(), N'', NULL, N'部门管理菜单')\nGO\nINSERT sys_menu VALUES (104, N'岗位管理', 1, 5, N'post', N'system/post/index', N'', 1, 0, N'C', N'0', N'0', N'system:post:list', N'post', N'admin', getdate(), N'', NULL, N'岗位管理菜单')\nGO\nINSERT sys_menu VALUES (105, N'字典管理', 1, 6, N'dict', N'system/dict/index', N'', 1, 0, N'C', N'0', N'0', N'system:dict:list', N'dict', N'admin', getdate(), N'', NULL, N'字典管理菜单')\nGO\nINSERT sys_menu VALUES (106, N'参数设置', 1, 7, N'config', N'system/config/index', N'', 1, 0, N'C', N'0', N'0', N'system:config:list', N'edit', N'admin', getdate(), N'', NULL, N'参数设置菜单')\nGO\nINSERT sys_menu VALUES (107, N'通知公告', 1, 8, N'notice', N'system/notice/index', N'', 1, 0, N'C', N'0', N'0', N'system:notice:list', N'message', N'admin', getdate(), N'', NULL, N'通知公告菜单')\nGO\nINSERT sys_menu VALUES (108, N'日志管理', 1, 9, N'log', N'', N'', 1, 0, N'M', N'0', N'0', N'', N'log', N'admin', getdate(), N'', NULL, N'日志管理菜单')\nGO\nINSERT sys_menu VALUES (109, N'在线用户', 2, 1, N'online', N'monitor/online/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:online:list', N'online', N'admin', getdate(), N'', NULL, N'在线用户菜单')\nGO\nINSERT sys_menu VALUES (112, N'缓存列表', 2, 6, N'cacheList', N'monitor/cache/list', N'', 1, 0, N'C', N'0', N'0', N'monitor:cache:list', N'redis-list', N'admin', getdate(), N'', NULL, N'缓存列表菜单')\nGO\nINSERT sys_menu VALUES (113, N'缓存监控', 2, 5, N'cache', N'monitor/cache/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:cache:list', N'redis', N'admin', getdate(), N'', NULL, N'缓存监控菜单')\nGO\nINSERT sys_menu VALUES (114, N'表单构建', 3, 1, N'build', N'tool/build/index', N'', 1, 0, N'C', N'0', N'0', N'tool:build:list', N'build', N'admin', getdate(), N'', NULL, N'表单构建菜单')\nGO\nINSERT sys_menu VALUES (115, N'代码生成', 3, 2, N'gen', N'tool/gen/index', N'', 1, 0, N'C', N'0', N'0', N'tool:gen:list', N'code', N'admin', getdate(), N'', NULL, N'代码生成菜单')\nGO\nINSERT sys_menu VALUES (117, N'Admin监控', 2, 5, N'Admin', N'monitor/admin/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:admin:list', N'dashboard', N'admin', getdate(), N'', NULL, N'Admin监控菜单');\nGO\nINSERT sys_menu VALUES (118, N'文件管理', 1, 10, N'oss', N'system/oss/index', N'', 1, 0, N'C', '0', N'0', N'system:oss:list', N'upload', N'admin', getdate(), N'', NULL, N'文件管理菜单');\nGO\nINSERT sys_menu VALUES (120, N'任务调度中心', 2, 5, N'XxlJob', N'monitor/xxljob/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:xxljob:list', N'job', N'admin', getdate(), N'', NULL, N'Xxl-Job控制台菜单');\nGO\nINSERT sys_menu VALUES (500, N'操作日志', 108, 1, N'operlog', N'monitor/operlog/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:operlog:list', N'form', N'admin', getdate(), N'', NULL, N'操作日志菜单')\nGO\nINSERT sys_menu VALUES (501, N'登录日志', 108, 2, N'logininfor', N'monitor/logininfor/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:logininfor:list', N'logininfor', N'admin', getdate(), N'', NULL, N'登录日志菜单')\nGO\nINSERT sys_menu VALUES (1001, N'用户查询', 100, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:query', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1002, N'用户新增', 100, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:add', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1003, N'用户修改', 100, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:edit', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1004, N'用户删除', 100, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:remove', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1005, N'用户导出', 100, 5, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:export', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1006, N'用户导入', 100, 6, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:import', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1007, N'重置密码', 100, 7, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:resetPwd', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1008, N'角色查询', 101, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:query', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1009, N'角色新增', 101, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:add', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1010, N'角色修改', 101, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:edit', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1011, N'角色删除', 101, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:remove', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1012, N'角色导出', 101, 5, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:export', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1013, N'菜单查询', 102, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:query', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1014, N'菜单新增', 102, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:add', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1015, N'菜单修改', 102, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:edit', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1016, N'菜单删除', 102, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:remove', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1017, N'部门查询', 103, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:query', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1018, N'部门新增', 103, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:add', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1019, N'部门修改', 103, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:edit', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1020, N'部门删除', 103, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:remove', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1021, N'岗位查询', 104, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:query', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1022, N'岗位新增', 104, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:add', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1023, N'岗位修改', 104, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:edit', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1024, N'岗位删除', 104, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:remove', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1025, N'岗位导出', 104, 5, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:export', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1026, N'字典查询', 105, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:query', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1027, N'字典新增', 105, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:add', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1028, N'字典修改', 105, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:edit', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1029, N'字典删除', 105, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:remove', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1030, N'字典导出', 105, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:export', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1031, N'参数查询', 106, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:query', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1032, N'参数新增', 106, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:add', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1033, N'参数修改', 106, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:edit', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1034, N'参数删除', 106, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:remove', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1035, N'参数导出', 106, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:export', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1036, N'公告查询', 107, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:query', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1037, N'公告新增', 107, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:add', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1038, N'公告修改', 107, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:edit', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1039, N'公告删除', 107, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:remove', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1040, N'操作查询', 500, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:operlog:query', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1041, N'操作删除', 500, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:operlog:remove', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1042, N'日志导出', 500, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:operlog:export', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1043, N'登录查询', 501, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:query', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1044, N'登录删除', 501, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:remove', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1045, N'日志导出', 501, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:export', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1050, N'账户解锁', 501, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:unlock',  N'#', N'admin', getdate(), N'', null, N'')\nGO\nINSERT sys_menu VALUES (1046, N'在线查询', 109, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:query', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1047, N'批量强退', 109, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:batchLogout', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1048, N'单条强退', 109, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:forceLogout', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1055, N'生成查询', 115, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:query', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1056, N'生成修改', 115, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:edit', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1057, N'生成删除', 115, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:remove', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1058, N'导入代码', 115, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:import', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1059, N'预览代码', 115, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:preview', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_menu VALUES (1060, N'生成代码', 115, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:code', N'#', N'admin', getdate(), N'', NULL, N'')\nGO\n-- oss相关按钮\nINSERT sys_menu VALUES (1600, N'文件查询', 118, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:query', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\nINSERT sys_menu VALUES (1601, N'文件上传', 118, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:upload', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\nINSERT sys_menu VALUES (1602, N'文件下载', 118, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:download', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\nINSERT sys_menu VALUES (1603, N'文件删除', 118, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:remove', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\nINSERT sys_menu VALUES (1604, N'配置添加', 118, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:add', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\nINSERT sys_menu VALUES (1605, N'配置编辑', 118, 6, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:edit', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\n\nCREATE TABLE sys_notice\n(\n    notice_id      bigint                     NOT NULL,\n    notice_title   nvarchar(50)               NOT NULL,\n    notice_type    nchar(1)                   NOT NULL,\n    notice_content nvarchar(max)              NULL,\n    status         nchar(1)     DEFAULT ('0') NULL,\n    create_by      nvarchar(64) DEFAULT ''    NULL,\n    create_time    datetime2(7)               NULL,\n    update_by      nvarchar(64) DEFAULT ''    NULL,\n    update_time    datetime2(7)               NULL,\n    remark         nvarchar(255)              NULL,\n    CONSTRAINT PK__sys_noti__3E82A5DB0EC94801 PRIMARY KEY CLUSTERED (notice_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nTEXTIMAGE_ON [PRIMARY]\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'公告ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_notice',\n    'COLUMN', N'notice_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'公告标题' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_notice',\n    'COLUMN', N'notice_title'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'公告类型（1通知 2公告）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_notice',\n    'COLUMN', N'notice_type'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'公告内容' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_notice',\n    'COLUMN', N'notice_content'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'公告状态（0正常 1关闭）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_notice',\n    'COLUMN', N'status'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_notice',\n    'COLUMN', N'create_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_notice',\n    'COLUMN', N'create_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_notice',\n    'COLUMN', N'update_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_notice',\n    'COLUMN', N'update_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'备注' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_notice',\n    'COLUMN', N'remark'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'通知公告表' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_notice'\nGO\n\nINSERT sys_notice VALUES (1, N'温馨提醒：2018-07-01 若依新版本发布啦', N'2', N'新版本内容', N'0', N'admin', getdate(), N'admin', getdate(), N'管理员')\nGO\nINSERT sys_notice VALUES (2, N'维护通知：2018-07-01 若依系统凌晨维护', N'1', N'维护内容', N'0', N'admin', getdate(), N'admin', getdate(), N'管理员')\nGO\n\nCREATE TABLE sys_oper_log\n(\n    oper_id        bigint                       NOT NULL,\n    title          nvarchar(50)   DEFAULT ''    NULL,\n    business_type  int            DEFAULT ((0)) NULL,\n    method         nvarchar(100)  DEFAULT ''    NULL,\n    request_method nvarchar(10)   DEFAULT ''    NULL,\n    operator_type  int            DEFAULT ((0)) NULL,\n    oper_name      nvarchar(50)   DEFAULT ''    NULL,\n    dept_name      nvarchar(50)   DEFAULT ''    NULL,\n    oper_url       nvarchar(255)  DEFAULT ''    NULL,\n    oper_ip        nvarchar(128)  DEFAULT ''    NULL,\n    oper_location  nvarchar(255)  DEFAULT ''    NULL,\n    oper_param     nvarchar(2000) DEFAULT ''    NULL,\n    json_result    nvarchar(2000) DEFAULT ''    NULL,\n    status         int            DEFAULT ((0)) NULL,\n    error_msg      nvarchar(2000) DEFAULT ''    NULL,\n    oper_time      datetime2(7)                 NULL,\n    CONSTRAINT PK__sys_oper__34723BF9BD954573 PRIMARY KEY CLUSTERED (oper_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nCREATE NONCLUSTERED INDEX idx_sys_oper_log_bt ON sys_oper_log (business_type)\nGO\nCREATE NONCLUSTERED INDEX idx_sys_oper_log_s ON sys_oper_log (status)\nGO\nCREATE NONCLUSTERED INDEX idx_sys_oper_log_ot ON sys_oper_log (oper_time)\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'日志主键' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'oper_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'模块标题' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'title'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'业务类型（0其它 1新增 2修改 3删除）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'business_type'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'方法名称' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'method'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'请求方式' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'request_method'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'操作类别（0其它 1后台用户 2手机端用户）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'operator_type'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'操作人员' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'oper_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'部门名称' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'dept_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'请求URL' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'oper_url'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'主机地址' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'oper_ip'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'操作地点' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'oper_location'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'请求参数' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'oper_param'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'返回参数' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'json_result'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'操作状态（0正常 1异常）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'status'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'错误消息' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'error_msg'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'操作时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log',\n    'COLUMN', N'oper_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'操作日志记录' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oper_log'\nGO\n\nCREATE TABLE sys_post\n(\n    post_id     bigint                  NOT NULL,\n    post_code   nvarchar(64)            NOT NULL,\n    post_name   nvarchar(50)            NOT NULL,\n    post_sort   int                     NOT NULL,\n    status      nchar(1)                NOT NULL,\n    create_by   nvarchar(64) DEFAULT '' NULL,\n    create_time datetime2(7)            NULL,\n    update_by   nvarchar(64) DEFAULT '' NULL,\n    update_time datetime2(7)            NULL,\n    remark      nvarchar(500)           NULL,\n    CONSTRAINT PK__sys_post__3ED7876668E2D081 PRIMARY KEY CLUSTERED (post_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'岗位ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_post',\n    'COLUMN', N'post_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'岗位编码' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_post',\n    'COLUMN', N'post_code'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'岗位名称' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_post',\n    'COLUMN', N'post_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'显示顺序' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_post',\n    'COLUMN', N'post_sort'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'状态（0正常 1停用）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_post',\n    'COLUMN', N'status'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_post',\n    'COLUMN', N'create_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_post',\n    'COLUMN', N'create_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_post',\n    'COLUMN', N'update_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_post',\n    'COLUMN', N'update_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'备注' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_post',\n    'COLUMN', N'remark'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'岗位信息表' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_post'\nGO\n\nINSERT sys_post VALUES (1, N'ceo', N'董事长', 1, N'0', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_post VALUES (2, N'se', N'项目经理', 2, N'0', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_post VALUES (3, N'hr', N'人力资源', 3, N'0', N'admin', getdate(), N'', NULL, N'')\nGO\nINSERT sys_post VALUES (4, N'user', N'普通员工', 4, N'0', N'admin', getdate(), N'', NULL, N'')\nGO\n\nCREATE TABLE sys_role\n(\n    role_id             bigint                     NOT NULL,\n    role_name           nvarchar(30)               NOT NULL,\n    role_key            nvarchar(100)              NOT NULL,\n    role_sort           int                        NOT NULL,\n    data_scope          nchar(1)     DEFAULT ('1') NULL,\n    menu_check_strictly tinyint      DEFAULT ((1)) NULL,\n    dept_check_strictly tinyint      DEFAULT ((1)) NULL,\n    status              nchar(1)                   NOT NULL,\n    del_flag            nchar(1)     DEFAULT ('0') NULL,\n    create_by           nvarchar(64) DEFAULT ''    NULL,\n    create_time         datetime2(7)               NULL,\n    update_by           nvarchar(64) DEFAULT ''    NULL,\n    update_time         datetime2(7)               NULL,\n    remark              nvarchar(500)              NULL,\n    CONSTRAINT PK__sys_role__760965CCF9383145 PRIMARY KEY CLUSTERED (role_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'角色ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role',\n    'COLUMN', N'role_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'角色名称' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role',\n    'COLUMN', N'role_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'角色权限字符串' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role',\n    'COLUMN', N'role_key'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'显示顺序' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role',\n    'COLUMN', N'role_sort'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'数据范围（1：全部数据权限 2：自定数据权限 3：本部门数据权限 4：本部门及以下数据权限）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role',\n    'COLUMN', N'data_scope'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'菜单树选择项是否关联显示' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role',\n    'COLUMN', N'menu_check_strictly'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'部门树选择项是否关联显示' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role',\n    'COLUMN', N'dept_check_strictly'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'角色状态（0正常 1停用）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role',\n    'COLUMN', N'status'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'删除标志（0代表存在 2代表删除）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role',\n    'COLUMN', N'del_flag'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role',\n    'COLUMN', N'create_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role',\n    'COLUMN', N'create_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role',\n    'COLUMN', N'update_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role',\n    'COLUMN', N'update_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'备注' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role',\n    'COLUMN', N'remark'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'角色信息表' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role'\nGO\n\nINSERT sys_role VALUES (1, N'超级管理员', N'admin', 1, N'1', 1, 1, N'0', N'0', N'admin', getdate(), N'', NULL, N'超级管理员')\nGO\nINSERT sys_role VALUES (2, N'普通角色', N'common', 2, N'2', 1, 1, N'0', N'0', N'admin', getdate(), N'', NULL, N'普通角色')\nGO\n\nCREATE TABLE sys_role_dept\n(\n    role_id bigint NOT NULL,\n    dept_id bigint NOT NULL,\n    CONSTRAINT PK__sys_role__2BC3005BABBCA08A PRIMARY KEY CLUSTERED (role_id, dept_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'角色ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role_dept',\n    'COLUMN', N'role_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'部门ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role_dept',\n    'COLUMN', N'dept_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'角色和部门关联表' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role_dept'\nGO\n\nINSERT sys_role_dept VALUES (2, 100)\nGO\nINSERT sys_role_dept VALUES (2, 101)\nGO\nINSERT sys_role_dept VALUES (2, 105)\nGO\n\nCREATE TABLE sys_role_menu\n(\n    role_id bigint NOT NULL,\n    menu_id bigint NOT NULL,\n    CONSTRAINT PK__sys_role__A2C36A6187BA4B17 PRIMARY KEY CLUSTERED (role_id, menu_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'角色ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role_menu',\n    'COLUMN', N'role_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'菜单ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role_menu',\n    'COLUMN', N'menu_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'角色和菜单关联表' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_role_menu'\nGO\n\nINSERT sys_role_menu VALUES (2, 1)\nGO\nINSERT sys_role_menu VALUES (2, 2)\nGO\nINSERT sys_role_menu VALUES (2, 3)\nGO\nINSERT sys_role_menu VALUES (2, 100)\nGO\nINSERT sys_role_menu VALUES (2, 101)\nGO\nINSERT sys_role_menu VALUES (2, 102)\nGO\nINSERT sys_role_menu VALUES (2, 103)\nGO\nINSERT sys_role_menu VALUES (2, 104)\nGO\nINSERT sys_role_menu VALUES (2, 105)\nGO\nINSERT sys_role_menu VALUES (2, 106)\nGO\nINSERT sys_role_menu VALUES (2, 107)\nGO\nINSERT sys_role_menu VALUES (2, 108)\nGO\nINSERT sys_role_menu VALUES (2, 109)\nGO\nINSERT sys_role_menu VALUES (2, 110)\nGO\nINSERT sys_role_menu VALUES (2, 111)\nGO\nINSERT sys_role_menu VALUES (2, 112)\nGO\nINSERT sys_role_menu VALUES (2, 113)\nGO\nINSERT sys_role_menu VALUES (2, 114)\nGO\nINSERT sys_role_menu VALUES (2, 115)\nGO\nINSERT sys_role_menu VALUES (2, 116)\nGO\nINSERT sys_role_menu VALUES (2, 500)\nGO\nINSERT sys_role_menu VALUES (2, 501)\nGO\nINSERT sys_role_menu VALUES (2, 1001)\nGO\nINSERT sys_role_menu VALUES (2, 1002)\nGO\nINSERT sys_role_menu VALUES (2, 1003)\nGO\nINSERT sys_role_menu VALUES (2, 1004)\nGO\nINSERT sys_role_menu VALUES (2, 1005)\nGO\nINSERT sys_role_menu VALUES (2, 1006)\nGO\nINSERT sys_role_menu VALUES (2, 1007)\nGO\nINSERT sys_role_menu VALUES (2, 1008)\nGO\nINSERT sys_role_menu VALUES (2, 1009)\nGO\nINSERT sys_role_menu VALUES (2, 1010)\nGO\nINSERT sys_role_menu VALUES (2, 1011)\nGO\nINSERT sys_role_menu VALUES (2, 1012)\nGO\nINSERT sys_role_menu VALUES (2, 1013)\nGO\nINSERT sys_role_menu VALUES (2, 1014)\nGO\nINSERT sys_role_menu VALUES (2, 1015)\nGO\nINSERT sys_role_menu VALUES (2, 1016)\nGO\nINSERT sys_role_menu VALUES (2, 1017)\nGO\nINSERT sys_role_menu VALUES (2, 1018)\nGO\nINSERT sys_role_menu VALUES (2, 1019)\nGO\nINSERT sys_role_menu VALUES (2, 1020)\nGO\nINSERT sys_role_menu VALUES (2, 1021)\nGO\nINSERT sys_role_menu VALUES (2, 1022)\nGO\nINSERT sys_role_menu VALUES (2, 1023)\nGO\nINSERT sys_role_menu VALUES (2, 1024)\nGO\nINSERT sys_role_menu VALUES (2, 1025)\nGO\nINSERT sys_role_menu VALUES (2, 1026)\nGO\nINSERT sys_role_menu VALUES (2, 1027)\nGO\nINSERT sys_role_menu VALUES (2, 1028)\nGO\nINSERT sys_role_menu VALUES (2, 1029)\nGO\nINSERT sys_role_menu VALUES (2, 1030)\nGO\nINSERT sys_role_menu VALUES (2, 1031)\nGO\nINSERT sys_role_menu VALUES (2, 1032)\nGO\nINSERT sys_role_menu VALUES (2, 1033)\nGO\nINSERT sys_role_menu VALUES (2, 1034)\nGO\nINSERT sys_role_menu VALUES (2, 1035)\nGO\nINSERT sys_role_menu VALUES (2, 1036)\nGO\nINSERT sys_role_menu VALUES (2, 1037)\nGO\nINSERT sys_role_menu VALUES (2, 1038)\nGO\nINSERT sys_role_menu VALUES (2, 1039)\nGO\nINSERT sys_role_menu VALUES (2, 1040)\nGO\nINSERT sys_role_menu VALUES (2, 1041)\nGO\nINSERT sys_role_menu VALUES (2, 1042)\nGO\nINSERT sys_role_menu VALUES (2, 1043)\nGO\nINSERT sys_role_menu VALUES (2, 1044)\nGO\nINSERT sys_role_menu VALUES (2, 1045)\nGO\nINSERT sys_role_menu VALUES (2, 1046)\nGO\nINSERT sys_role_menu VALUES (2, 1047)\nGO\nINSERT sys_role_menu VALUES (2, 1048)\nGO\nINSERT sys_role_menu VALUES (2, 1049)\nGO\nINSERT sys_role_menu VALUES (2, 1050)\nGO\nINSERT sys_role_menu VALUES (2, 1051)\nGO\nINSERT sys_role_menu VALUES (2, 1052)\nGO\nINSERT sys_role_menu VALUES (2, 1053)\nGO\nINSERT sys_role_menu VALUES (2, 1054)\nGO\nINSERT sys_role_menu VALUES (2, 1055)\nGO\nINSERT sys_role_menu VALUES (2, 1056)\nGO\nINSERT sys_role_menu VALUES (2, 1057)\nGO\nINSERT sys_role_menu VALUES (2, 1058)\nGO\nINSERT sys_role_menu VALUES (2, 1059)\nGO\nINSERT sys_role_menu VALUES (2, 1060)\nGO\n\nCREATE TABLE sys_user\n(\n    user_id     bigint                             NOT NULL,\n    dept_id     bigint                             NULL,\n    user_name   nvarchar(30)                       NOT NULL,\n    nick_name   nvarchar(30)                       NOT NULL,\n    user_type   nvarchar(10)  DEFAULT ('sys_user') NULL,\n    email       nvarchar(50)  DEFAULT ''           NULL,\n    phonenumber nvarchar(11)  DEFAULT ''           NULL,\n    sex         nchar(1)      DEFAULT ('0')        NULL,\n    avatar      nvarchar(100) DEFAULT ''           NULL,\n    password    nvarchar(100) DEFAULT ''           NULL,\n    status      nchar(1)      DEFAULT ('0')        NULL,\n    del_flag    nchar(1)      DEFAULT ('0')        NULL,\n    login_ip    nvarchar(128) DEFAULT ''           NULL,\n    login_date  datetime2(7)                       NULL,\n    create_by   nvarchar(64)  DEFAULT ''           NULL,\n    create_time datetime2(7)                       NULL,\n    update_by   nvarchar(64)  DEFAULT ''           NULL,\n    update_time datetime2(7)                       NULL,\n    remark      nvarchar(500)                      NULL,\n    CONSTRAINT PK__sys_user__B9BE370F79170B6A PRIMARY KEY CLUSTERED (user_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'用户ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'user_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'部门ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'dept_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'用户账号' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'user_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'用户昵称' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'nick_name'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'用户类型（sys_user系统用户）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'user_type'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'用户邮箱' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'email'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'手机号码' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'phonenumber'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'用户性别（0男 1女 2未知）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'sex'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'头像地址' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'avatar'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'密码' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'password'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'帐号状态（0正常 1停用）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'status'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'删除标志（0代表存在 2代表删除）' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'del_flag'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'最后登录IP' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'login_ip'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'最后登录时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'login_date'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'create_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'创建时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'create_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新者' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'update_by'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'更新时间' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'update_time'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'备注' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user',\n    'COLUMN', N'remark'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'用户信息表' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user'\nGO\n\nINSERT sys_user VALUES (1, 103, N'admin', N'疯狂的狮子Li', N'sys_user', N'crazyLionLi@163.com', N'15888888888', N'1', N'', N'$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', N'0', N'0', N'127.0.0.1', getdate(), N'admin', getdate(), N'', getdate(), N'管理员')\nGO\nINSERT sys_user VALUES (2, 105, N'lionli', N'疯狂的狮子Li', N'sys_user', N'crazyLionLi@qq.com', N'15666666666', N'1', N'', N'$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', N'0', N'0', N'127.0.0.1', getdate(), N'admin', getdate(), N'admin', getdate(), N'测试员')\nGO\n\nCREATE TABLE sys_user_post\n(\n    user_id bigint NOT NULL,\n    post_id bigint NOT NULL,\n    CONSTRAINT PK__sys_user__CA534F799C04589B PRIMARY KEY CLUSTERED (user_id, post_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'用户ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user_post',\n    'COLUMN', N'user_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'岗位ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user_post',\n    'COLUMN', N'post_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'用户与岗位关联表' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user_post'\nGO\n\nINSERT sys_user_post VALUES (1, 1)\nGO\nINSERT sys_user_post VALUES (2, 2)\nGO\n\nCREATE TABLE sys_user_role\n(\n    user_id bigint NOT NULL,\n    role_id bigint NOT NULL,\n    CONSTRAINT PK__sys_user__6EDEA153FB34D8F0 PRIMARY KEY CLUSTERED (user_id, role_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'用户ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user_role',\n    'COLUMN', N'user_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'角色ID' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user_role',\n    'COLUMN', N'role_id'\nGO\nEXEC sys.sp_addextendedproperty\n    'MS_Description', N'用户和角色关联表' ,\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_user_role'\nGO\n\nINSERT sys_user_role VALUES (1, 1)\nGO\nINSERT sys_user_role VALUES (2, 2)\nGO\n\nCREATE TABLE sys_oss\n(\n    oss_id        bigint                          NOT NULL,\n    file_name     nvarchar(255) DEFAULT ''        NOT NULL,\n    original_name nvarchar(255) DEFAULT ''        NOT NULL,\n    file_suffix   nvarchar(10)  DEFAULT ''        NOT NULL,\n    url           nvarchar(500)                   NOT NULL,\n    create_time   datetime2(7)                    NULL,\n    create_by     nvarchar(64)  DEFAULT ''        NULL,\n    update_time   datetime2(7)                    NULL,\n    update_by     nvarchar(64)  DEFAULT ''        NULL,\n    service       nvarchar(20)  DEFAULT ('minio') NOT NULL,\n    CONSTRAINT PK__sys_oss__91241EA442389F0D PRIMARY KEY CLUSTERED (oss_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sp_addextendedproperty\n    'MS_Description', N'对象存储主键',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss',\n    'COLUMN', N'oss_id'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'文件名',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss',\n    'COLUMN', N'file_name'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'原名',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss',\n    'COLUMN', N'original_name'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'文件后缀名',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss',\n    'COLUMN', N'file_suffix'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'URL地址',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss',\n    'COLUMN', N'url'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'创建时间',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss',\n    'COLUMN', N'create_time'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'上传人',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss',\n    'COLUMN', N'create_by'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'更新时间',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss',\n    'COLUMN', N'update_time'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'更新人',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss',\n    'COLUMN', N'update_by'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'服务商',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss',\n    'COLUMN', N'service'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'OSS对象存储表',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss'\nGO\n\nCREATE TABLE sys_oss_config\n(\n    oss_config_id bigint                      NOT NULL,\n    config_key    nvarchar(20)  DEFAULT ''    NOT NULL,\n    access_key    nvarchar(255) DEFAULT ''    NULL,\n    secret_key    nvarchar(255) DEFAULT ''    NULL,\n    bucket_name   nvarchar(255) DEFAULT ''    NULL,\n    prefix        nvarchar(255) DEFAULT ''    NULL,\n    endpoint      nvarchar(255) DEFAULT ''    NULL,\n    domain        nvarchar(255) DEFAULT ''    NULL,\n    is_https      nchar(1)      DEFAULT ('N') NULL,\n    region        nvarchar(255) DEFAULT ''    NULL,\n    access_policy nchar(1)      DEFAULT ('1') NOT NULL,\n    status        nchar(1)      DEFAULT ('1') NULL,\n    ext1          nvarchar(255) DEFAULT ''    NULL,\n    create_by     nvarchar(64)  DEFAULT ''    NULL,\n    create_time   datetime2(7)                NULL,\n    update_by     nvarchar(64)  DEFAULT ''    NULL,\n    update_time   datetime2(7)                NULL,\n    remark        nvarchar(500)               NULL,\n    CONSTRAINT PK__sys_oss___BFBDE87009ED2882 PRIMARY KEY CLUSTERED (oss_config_id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sp_addextendedproperty\n    'MS_Description', N'主建',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'oss_config_id'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'配置key',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'config_key'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'accessKey',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'access_key'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'秘钥',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'secret_key'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'桶名称',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'bucket_name'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'前缀',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'prefix'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'访问站点',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'endpoint'\nGO\nEXEC sp_addextendedproperty\n     'MS_Description', N'自定义域名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'sys_oss_config',\n     'COLUMN', N'domain'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'是否https（Y=是,N=否）',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'is_https'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'域',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'region'\nGO\nEXEC sp_addextendedproperty\n     'MS_Description', N'桶权限类型(0=private 1=public 2=custom)',\n     'SCHEMA', N'dbo',\n     'TABLE', N'sys_oss_config',\n     'COLUMN', N'access_policy'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'是否默认（0=是,1=否）',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'status'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'扩展字段',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'ext1'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'创建者',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'create_by'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'创建时间',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'create_time'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'更新者',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'update_by'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'更新时间',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'update_time'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'备注',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config',\n    'COLUMN', N'remark'\nGO\nEXEC sp_addextendedproperty\n    'MS_Description', N'对象存储配置表',\n    'SCHEMA', N'dbo',\n    'TABLE', N'sys_oss_config'\nGO\n\nINSERT INTO sys_oss_config VALUES (N'1', N'minio', N'ruoyi',            N'ruoyi123',        N'ruoyi',            N'', N'127.0.0.1:9000',                    N'',N'N', N'',           N'1', N'0', N'', N'admin', getdate(), N'admin', getdate(), NULL)\nGO\nINSERT INTO sys_oss_config VALUES (N'2', N'qiniu', N'XXXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi',            N'', N's3-cn-north-1.qiniucs.com',         N'',N'N', N'',           N'1', N'1', N'', N'admin', getdate(), N'admin', getdate(), NULL)\nGO\nINSERT INTO sys_oss_config VALUES (N'3', N'aliyun', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi',            N'', N'oss-cn-beijing.aliyuncs.com',       N'',N'N', N'',           N'1', N'1', N'', N'admin', getdate(), N'admin', getdate(), NULL)\nGO\nINSERT INTO sys_oss_config VALUES (N'4', N'qcloud', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi-1250000000', N'', N'cos.ap-beijing.myqcloud.com',       N'',N'N', N'ap-beijing', N'1', N'1', N'', N'admin', getdate(), N'admin', getdate(), NULL)\nGO\nINSERT INTO sys_oss_config VALUES (N'5', N'image',  N'ruoyi',           N'ruoyi123',        N'ruoyi',            N'image', N'127.0.0.1:9000',               N'',N'N', N'',           N'1', N'1', N'', N'admin', getdate(), N'admin', getdate(), NULL)\nGO\n"
  },
  {
    "path": "script/sql/sqlserver/sqlserver_test.sql",
    "content": "CREATE TABLE test_demo\n(\n    id          bigint            NOT NULL,\n    dept_id     bigint            NULL,\n    user_id     bigint            NULL,\n    order_num   int DEFAULT ((0)) NULL,\n    test_key    nvarchar(255)     NULL,\n    value       nvarchar(255)     NULL,\n    version     int DEFAULT ((0)) NULL,\n    create_time datetime2(0)      NULL,\n    create_by   nvarchar(64)      NULL,\n    update_time datetime2(0)      NULL,\n    update_by   nvarchar(64)      NULL,\n    del_flag    int DEFAULT ((0)) NULL,\n    CONSTRAINT PK__test_dem__3213E83F176051C8 PRIMARY KEY CLUSTERED (id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'主键',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_demo',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'部门id',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_demo',\n     'COLUMN', N'dept_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户id',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_demo',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'排序号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_demo',\n     'COLUMN', N'order_num'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'key键',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_demo',\n     'COLUMN', N'test_key'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'值',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_demo',\n     'COLUMN', N'value'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'版本',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_demo',\n     'COLUMN', N'version'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_demo',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建人',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_demo',\n     'COLUMN', N'create_by'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_demo',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新人',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_demo',\n     'COLUMN', N'update_by'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'删除标志',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_demo',\n     'COLUMN', N'del_flag'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'测试单表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_demo'\nGO\n\nCREATE TABLE test_tree\n(\n    id          bigint               NOT NULL,\n    parent_id   bigint DEFAULT ((0)) NULL,\n    dept_id     bigint               NULL,\n    user_id     bigint               NULL,\n    tree_name   nvarchar(255)        NULL,\n    version     int    DEFAULT ((0)) NULL,\n    create_time datetime2(0)         NULL,\n    create_by   nvarchar(64)         NULL,\n    update_time datetime2(0)         NULL,\n    update_by   nvarchar(64)         NULL,\n    del_flag    int    DEFAULT ((0)) NULL,\n    CONSTRAINT PK__test_tre__3213E83FC75A1B63 PRIMARY KEY CLUSTERED (id)\n        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n        ON [PRIMARY]\n)\nON [PRIMARY]\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'主键',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_tree',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'父id',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_tree',\n     'COLUMN', N'parent_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'部门id',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_tree',\n     'COLUMN', N'dept_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户id',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_tree',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'值',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_tree',\n     'COLUMN', N'tree_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'版本',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_tree',\n     'COLUMN', N'version'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_tree',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建人',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_tree',\n     'COLUMN', N'create_by'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_tree',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新人',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_tree',\n     'COLUMN', N'update_by'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'删除标志',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_tree',\n     'COLUMN', N'del_flag'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'测试树表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'test_tree'\nGO\n\nINSERT sys_user VALUES (3, 108, N'test', N'本部门及以下 密码666666', N'sys_user', N'', N'', N'0', N'', N'$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', N'0', N'0', N'127.0.0.1', getdate(), N'admin', getdate(), N'test', getdate(), NULL);\nGO\nINSERT sys_user VALUES (4, 102, N'test1', N'仅本人 密码666666', N'sys_user', N'', N'', N'0', N'', N'$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', N'0', N'0', N'127.0.0.1', getdate(), N'admin', getdate(), N'test1', getdate(), NULL);\nGO\n\nINSERT sys_menu VALUES (5, N'测试菜单', 0, 5, N'demo', NULL, 1, 0, N'M', N'0', N'0', NULL, N'star', N'admin', getdate(), NULL, NULL, N'');\nGO\n\nINSERT sys_menu VALUES (1500, N'测试单表', 5, 1, N'demo', N'demo/demo/index', 1, 0, N'C', N'0', N'0', N'demo:demo:list', N'#', N'admin', getdate(), N'', NULL, N'测试单表菜单');\nGO\nINSERT sys_menu VALUES (1501, N'测试单表查询', 1500, 1, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:query', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\nINSERT sys_menu VALUES (1502, N'测试单表新增', 1500, 2, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:add', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\nINSERT sys_menu VALUES (1503, N'测试单表修改', 1500, 3, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:edit', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\nINSERT sys_menu VALUES (1504, N'测试单表删除', 1500, 4, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:remove', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\nINSERT sys_menu VALUES (1505, N'测试单表导出', 1500, 5, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:export', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\n\nINSERT sys_menu VALUES (1506, N'测试树表', 5, 1, N'tree', N'demo/tree/index', 1, 0, N'C', N'0', N'0', N'demo:tree:list', N'#', N'admin', getdate(), N'', NULL, N'测试树表菜单');\nGO\nINSERT sys_menu VALUES (1507, N'测试树表查询', 1506, 1, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:query', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\nINSERT sys_menu VALUES (1508, N'测试树表新增', 1506, 2, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:add', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\nINSERT sys_menu VALUES (1509, N'测试树表修改', 1506, 3, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:edit', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\nINSERT sys_menu VALUES (1510, N'测试树表删除', 1506, 4, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:remove', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\nINSERT sys_menu VALUES (1511, N'测试树表导出', 1506, 5, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:export', N'#', N'admin', getdate(), N'', NULL, N'');\nGO\n\nINSERT sys_role VALUES (3, N'本部门及以下', N'test1', 3, N'4', 1, 1, N'0', N'0', N'admin', getdate(), N'admin', NULL, NULL);\nGO\nINSERT sys_role VALUES (4, N'仅本人', N'test2', 4, N'5', 1, 1, N'0', N'0', N'admin', getdate(), N'admin', NULL, NULL);\nGO\n\nINSERT sys_role_menu VALUES (3, 1);\nGO\nINSERT sys_role_menu VALUES (3, 5);\nGO\nINSERT sys_role_menu VALUES (3, 100);\nGO\nINSERT sys_role_menu VALUES (3, 101);\nGO\nINSERT sys_role_menu VALUES (3, 102);\nGO\nINSERT sys_role_menu VALUES (3, 103);\nGO\nINSERT sys_role_menu VALUES (3, 104);\nGO\nINSERT sys_role_menu VALUES (3, 105);\nGO\nINSERT sys_role_menu VALUES (3, 106);\nGO\nINSERT sys_role_menu VALUES (3, 107);\nGO\nINSERT sys_role_menu VALUES (3, 108);\nGO\nINSERT sys_role_menu VALUES (3, 500);\nGO\nINSERT sys_role_menu VALUES (3, 501);\nGO\nINSERT sys_role_menu VALUES (3, 1001);\nGO\nINSERT sys_role_menu VALUES (3, 1002);\nGO\nINSERT sys_role_menu VALUES (3, 1003);\nGO\nINSERT sys_role_menu VALUES (3, 1004);\nGO\nINSERT sys_role_menu VALUES (3, 1005);\nGO\nINSERT sys_role_menu VALUES (3, 1006);\nGO\nINSERT sys_role_menu VALUES (3, 1007);\nGO\nINSERT sys_role_menu VALUES (3, 1008);\nGO\nINSERT sys_role_menu VALUES (3, 1009);\nGO\nINSERT sys_role_menu VALUES (3, 1010);\nGO\nINSERT sys_role_menu VALUES (3, 1011);\nGO\nINSERT sys_role_menu VALUES (3, 1012);\nGO\nINSERT sys_role_menu VALUES (3, 1013);\nGO\nINSERT sys_role_menu VALUES (3, 1014);\nGO\nINSERT sys_role_menu VALUES (3, 1015);\nGO\nINSERT sys_role_menu VALUES (3, 1016);\nGO\nINSERT sys_role_menu VALUES (3, 1017);\nGO\nINSERT sys_role_menu VALUES (3, 1018);\nGO\nINSERT sys_role_menu VALUES (3, 1019);\nGO\nINSERT sys_role_menu VALUES (3, 1020);\nGO\nINSERT sys_role_menu VALUES (3, 1021);\nGO\nINSERT sys_role_menu VALUES (3, 1022);\nGO\nINSERT sys_role_menu VALUES (3, 1023);\nGO\nINSERT sys_role_menu VALUES (3, 1024);\nGO\nINSERT sys_role_menu VALUES (3, 1025);\nGO\nINSERT sys_role_menu VALUES (3, 1026);\nGO\nINSERT sys_role_menu VALUES (3, 1027);\nGO\nINSERT sys_role_menu VALUES (3, 1028);\nGO\nINSERT sys_role_menu VALUES (3, 1029);\nGO\nINSERT sys_role_menu VALUES (3, 1030);\nGO\nINSERT sys_role_menu VALUES (3, 1031);\nGO\nINSERT sys_role_menu VALUES (3, 1032);\nGO\nINSERT sys_role_menu VALUES (3, 1033);\nGO\nINSERT sys_role_menu VALUES (3, 1034);\nGO\nINSERT sys_role_menu VALUES (3, 1035);\nGO\nINSERT sys_role_menu VALUES (3, 1036);\nGO\nINSERT sys_role_menu VALUES (3, 1037);\nGO\nINSERT sys_role_menu VALUES (3, 1038);\nGO\nINSERT sys_role_menu VALUES (3, 1039);\nGO\nINSERT sys_role_menu VALUES (3, 1040);\nGO\nINSERT sys_role_menu VALUES (3, 1041);\nGO\nINSERT sys_role_menu VALUES (3, 1042);\nGO\nINSERT sys_role_menu VALUES (3, 1043);\nGO\nINSERT sys_role_menu VALUES (3, 1044);\nGO\nINSERT sys_role_menu VALUES (3, 1045);\nGO\nINSERT sys_role_menu VALUES (3, 1500);\nGO\nINSERT sys_role_menu VALUES (3, 1501);\nGO\nINSERT sys_role_menu VALUES (3, 1502);\nGO\nINSERT sys_role_menu VALUES (3, 1503);\nGO\nINSERT sys_role_menu VALUES (3, 1504);\nGO\nINSERT sys_role_menu VALUES (3, 1505);\nGO\nINSERT sys_role_menu VALUES (3, 1506);\nGO\nINSERT sys_role_menu VALUES (3, 1507);\nGO\nINSERT sys_role_menu VALUES (3, 1508);\nGO\nINSERT sys_role_menu VALUES (3, 1509);\nGO\nINSERT sys_role_menu VALUES (3, 1510);\nGO\nINSERT sys_role_menu VALUES (3, 1511);\nGO\nINSERT sys_role_menu VALUES (4, 5);\nGO\nINSERT sys_role_menu VALUES (4, 1500);\nGO\nINSERT sys_role_menu VALUES (4, 1501);\nGO\nINSERT sys_role_menu VALUES (4, 1502);\nGO\nINSERT sys_role_menu VALUES (4, 1503);\nGO\nINSERT sys_role_menu VALUES (4, 1504);\nGO\nINSERT sys_role_menu VALUES (4, 1505);\nGO\nINSERT sys_role_menu VALUES (4, 1506);\nGO\nINSERT sys_role_menu VALUES (4, 1507);\nGO\nINSERT sys_role_menu VALUES (4, 1508);\nGO\nINSERT sys_role_menu VALUES (4, 1509);\nGO\nINSERT sys_role_menu VALUES (4, 1510);\nGO\nINSERT sys_role_menu VALUES (4, 1511);\nGO\n\nINSERT sys_user_role VALUES (3, 3);\nGO\nINSERT sys_user_role VALUES (4, 4);\nGO\n\nINSERT test_demo VALUES (1, 102, 4, 1, N'测试数据权限', N'测试', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_demo VALUES (2, 102, 3, 2, N'子节点1', N'111', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_demo VALUES (3, 102, 3, 3, N'子节点2', N'222', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_demo VALUES (4, 108, 4, 4, N'测试数据', N'demo', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_demo VALUES (5, 108, 3, 13, N'子节点11', N'1111', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_demo VALUES (6, 108, 3, 12, N'子节点22', N'2222', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_demo VALUES (7, 108, 3, 11, N'子节点33', N'3333', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_demo VALUES (8, 108, 3, 10, N'子节点44', N'4444', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_demo VALUES (9, 108, 3, 9, N'子节点55', N'5555', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_demo VALUES (10, 108, 3, 8, N'子节点66', N'6666', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_demo VALUES (11, 108, 3, 7, N'子节点77', N'7777', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_demo VALUES (12, 108, 3, 6, N'子节点88', N'8888', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_demo VALUES (13, 108, 3, 5, N'子节点99', N'9999', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\n\nINSERT test_tree VALUES (1, 0, 102, 4, N'测试数据权限', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_tree VALUES (2, 1, 102, 3, N'子节点1', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_tree VALUES (3, 2, 102, 3, N'子节点2', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_tree VALUES (4, 0, 108, 4, N'测试树1', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_tree VALUES (5, 4, 108, 3, N'子节点11', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_tree VALUES (6, 4, 108, 3, N'子节点22', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_tree VALUES (7, 4, 108, 3, N'子节点33', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_tree VALUES (8, 5, 108, 3, N'子节点44', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_tree VALUES (9, 6, 108, 3, N'子节点55', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_tree VALUES (10, 7, 108, 3, N'子节点66', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_tree VALUES (11, 7, 108, 3, N'子节点77', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_tree VALUES (12, 10, 108, 3, N'子节点88', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\nINSERT test_tree VALUES (13, 10, 108, 3, N'子节点99', 0, getdate(), N'admin', NULL, NULL, 0);\nGO\n"
  },
  {
    "path": "script/sql/tables_xxl_job.sql",
    "content": "#\n# XXL-JOB v2.3.0\n# Copyright (c) 2015-present, xuxueli.\n\nSET NAMES utf8mb4;\n\nCREATE TABLE `xxl_job_info` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `job_group` int(11) NOT NULL COMMENT '执行器主键ID',\n  `job_desc` varchar(255) NOT NULL,\n  `add_time` datetime DEFAULT NULL,\n  `update_time` datetime DEFAULT NULL,\n  `author` varchar(64) DEFAULT NULL COMMENT '作者',\n  `alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件',\n  `schedule_type` varchar(50) NOT NULL DEFAULT 'NONE' COMMENT '调度类型',\n  `schedule_conf` varchar(128) DEFAULT NULL COMMENT '调度配置，值含义取决于调度类型',\n  `misfire_strategy` varchar(50) NOT NULL DEFAULT 'DO_NOTHING' COMMENT '调度过期策略',\n  `executor_route_strategy` varchar(50) DEFAULT NULL COMMENT '执行器路由策略',\n  `executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',\n  `executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数',\n  `executor_block_strategy` varchar(50) DEFAULT NULL COMMENT '阻塞处理策略',\n  `executor_timeout` int(11) NOT NULL DEFAULT '0' COMMENT '任务执行超时时间，单位秒',\n  `executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数',\n  `glue_type` varchar(50) NOT NULL COMMENT 'GLUE类型',\n  `glue_source` mediumtext COMMENT 'GLUE源代码',\n  `glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注',\n  `glue_updatetime` datetime DEFAULT NULL COMMENT 'GLUE更新时间',\n  `child_jobid` varchar(255) DEFAULT NULL COMMENT '子任务ID，多个逗号分隔',\n  `trigger_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '调度状态：0-停止，1-运行',\n  `trigger_last_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '上次调度时间',\n  `trigger_next_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '下次调度时间',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n\nCREATE TABLE `xxl_job_log` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `job_group` int(11) NOT NULL COMMENT '执行器主键ID',\n  `job_id` int(11) NOT NULL COMMENT '任务，主键ID',\n  `executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址，本次执行的地址',\n  `executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',\n  `executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数',\n  `executor_sharding_param` varchar(20) DEFAULT NULL COMMENT '执行器任务分片参数，格式如 1/2',\n  `executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数',\n  `trigger_time` datetime DEFAULT NULL COMMENT '调度-时间',\n  `trigger_code` int(11) NOT NULL COMMENT '调度-结果',\n  `trigger_msg` text COMMENT '调度-日志',\n  `handle_time` datetime DEFAULT NULL COMMENT '执行-时间',\n  `handle_code` int(11) NOT NULL COMMENT '执行-状态',\n  `handle_msg` text COMMENT '执行-日志',\n  `alarm_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '告警状态：0-默认、1-无需告警、2-告警成功、3-告警失败',\n  PRIMARY KEY (`id`),\n  KEY `I_trigger_time` (`trigger_time`),\n  KEY `I_handle_code` (`handle_code`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n\nCREATE TABLE `xxl_job_log_report` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `trigger_day` datetime DEFAULT NULL COMMENT '调度-时间',\n  `running_count` int(11) NOT NULL DEFAULT '0' COMMENT '运行中-日志数量',\n  `suc_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行成功-日志数量',\n  `fail_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行失败-日志数量',\n  `update_time` datetime DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `i_trigger_day` (`trigger_day`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n\nCREATE TABLE `xxl_job_logglue` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `job_id` int(11) NOT NULL COMMENT '任务，主键ID',\n  `glue_type` varchar(50) DEFAULT NULL COMMENT 'GLUE类型',\n  `glue_source` mediumtext COMMENT 'GLUE源代码',\n  `glue_remark` varchar(128) NOT NULL COMMENT 'GLUE备注',\n  `add_time` datetime DEFAULT NULL,\n  `update_time` datetime DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n\nCREATE TABLE `xxl_job_registry` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `registry_group` varchar(50) NOT NULL,\n  `registry_key` varchar(255) NOT NULL,\n  `registry_value` varchar(255) NOT NULL,\n  `update_time` datetime DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  KEY `i_g_k_v` (`registry_group`,`registry_key`,`registry_value`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n\nCREATE TABLE `xxl_job_group` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `app_name` varchar(64) NOT NULL COMMENT '执行器AppName',\n  `title` varchar(12) NOT NULL COMMENT '执行器名称',\n  `address_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '执行器地址类型：0=自动注册、1=手动录入',\n  `address_list` text COMMENT '执行器地址列表，多地址逗号分隔',\n  `update_time` datetime DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n\nCREATE TABLE `xxl_job_user` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `username` varchar(50) NOT NULL COMMENT '账号',\n  `password` varchar(50) NOT NULL COMMENT '密码',\n  `role` tinyint(4) NOT NULL COMMENT '角色：0-普通用户、1-管理员',\n  `permission` varchar(255) DEFAULT NULL COMMENT '权限：执行器ID列表，多个逗号分割',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `i_username` (`username`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n\nCREATE TABLE `xxl_job_lock` (\n  `lock_name` varchar(50) NOT NULL COMMENT '锁名称',\n  PRIMARY KEY (`lock_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n\nINSERT INTO `xxl_job_group`(`id`, `app_name`, `title`, `address_type`, `address_list`, `update_time`) VALUES (1, 'xxl-job-executor', '示例执行器', 0, NULL, '2018-11-03 22:21:31' );\nINSERT INTO `xxl_job_info`(`id`, `job_group`, `job_desc`, `add_time`, `update_time`, `author`, `alarm_email`, `schedule_type`, `schedule_conf`, `misfire_strategy`, `executor_route_strategy`, `executor_handler`, `executor_param`, `executor_block_strategy`, `executor_timeout`, `executor_fail_retry_count`, `glue_type`, `glue_source`, `glue_remark`, `glue_updatetime`, `child_jobid`) VALUES (1, 1, '测试任务1', '2018-11-03 22:21:31', '2018-11-03 22:21:31', 'XXL', '', 'CRON', '0 0 0 * * ? *', 'DO_NOTHING', 'FIRST', 'demoJobHandler', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2018-11-03 22:21:31', '');\nINSERT INTO `xxl_job_user`(`id`, `username`, `password`, `role`, `permission`) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 1, NULL);\nINSERT INTO `xxl_job_lock` ( `lock_name`) VALUES ( 'schedule_lock');\n\ncommit;\n\n"
  },
  {
    "path": "script/sql/test.sql",
    "content": "DROP TABLE if EXISTS test_demo;\nCREATE TABLE test_demo\n(\n    id          bigint(0)    NOT NULL COMMENT '主键',\n    dept_id     bigint(0)    NULL DEFAULT NULL COMMENT '部门id',\n    user_id     bigint(0)    NULL DEFAULT NULL COMMENT '用户id',\n    order_num   int(0)       NULL DEFAULT 0 COMMENT '排序号',\n    test_key    varchar(255) NULL DEFAULT NULL COMMENT 'key键',\n    value       varchar(255) NULL DEFAULT NULL COMMENT '值',\n    version     int(0)       NULL DEFAULT 0 COMMENT '版本',\n    create_time datetime(0)  NULL DEFAULT NULL COMMENT '创建时间',\n    create_by   varchar(64)  NULL DEFAULT NULL COMMENT '创建人',\n    update_time datetime(0)  NULL DEFAULT NULL COMMENT '更新时间',\n    update_by   varchar(64)  NULL DEFAULT NULL COMMENT '更新人',\n    del_flag    int(0)       NULL DEFAULT 0 COMMENT '删除标志',\n    PRIMARY KEY (id) USING BTREE\n) ENGINE = InnoDB COMMENT = '测试单表';\n\nDROP TABLE if EXISTS test_tree;\nCREATE TABLE test_tree\n(\n    id          bigint(0)    NOT NULL COMMENT '主键',\n    parent_id   bigint(0)    NULL DEFAULT 0 COMMENT '父id',\n    dept_id     bigint(0)    NULL DEFAULT NULL COMMENT '部门id',\n    user_id     bigint(0)    NULL DEFAULT NULL COMMENT '用户id',\n    tree_name   varchar(255) NULL DEFAULT NULL COMMENT '值',\n    version     int(0)       NULL DEFAULT 0 COMMENT '版本',\n    create_time datetime(0)  NULL DEFAULT NULL COMMENT '创建时间',\n    create_by   varchar(64)  NULL DEFAULT NULL COMMENT '创建人',\n    update_time datetime(0)  NULL DEFAULT NULL COMMENT '更新时间',\n    update_by   varchar(64)  NULL DEFAULT NULL COMMENT '更新人',\n    del_flag    int(0)       NULL DEFAULT 0 COMMENT '删除标志',\n    PRIMARY KEY (id) USING BTREE\n) ENGINE = InnoDB COMMENT = '测试树表';\n\nINSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (3, 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), 'test', sysdate(), NULL);\nINSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (4, 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), 'test1', sysdate(), NULL);\n\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (5, '测试菜单', 0, 5, 'demo', NULL, 1, 0, 'M', '0', '0', NULL, 'star', 'admin', sysdate(), NULL, NULL, '');\n\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1500, '测试单表', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 'admin', sysdate(), '', NULL, '测试单表菜单');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1501, '测试单表查询', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 'admin', sysdate(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1502, '测试单表新增', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 'admin', sysdate(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1503, '测试单表修改', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 'admin', sysdate(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1504, '测试单表删除', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 'admin', sysdate(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1505, '测试单表导出', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 'admin', sysdate(), '', NULL, '');\n\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1506, '测试树表', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 'admin', sysdate(), '', NULL, '测试树表菜单');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1507, '测试树表查询', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 'admin', sysdate(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1508, '测试树表新增', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 'admin', sysdate(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1509, '测试树表修改', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 'admin', sysdate(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1510, '测试树表删除', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 'admin', sysdate(), '', NULL, '');\nINSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1511, '测试树表导出', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 'admin', sysdate(), '', NULL, '');\n\nINSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (3, '本部门及以下', 'test1', 3, '4', 1, 1, '0', '0', 'admin', sysdate(), 'admin', NULL, NULL);\nINSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (4, '仅本人', 'test2', 4, '5', 1, 1, '0', '0', 'admin', sysdate(), 'admin', NULL, NULL);\n\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 5);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 100);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 101);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 102);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 103);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 104);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 105);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 106);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 107);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 108);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 500);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 501);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1001);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1002);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1003);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1004);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1005);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1006);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1007);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1008);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1009);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1010);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1011);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1012);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1013);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1014);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1015);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1016);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1017);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1018);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1019);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1020);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1021);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1022);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1023);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1024);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1025);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1026);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1027);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1028);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1029);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1030);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1031);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1032);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1033);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1034);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1035);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1036);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1037);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1038);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1039);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1040);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1041);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1042);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1043);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1044);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1045);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1500);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1501);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1502);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1503);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1504);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1505);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1506);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1507);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1508);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1509);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1510);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1511);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 5);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1500);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1501);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1502);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1503);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1504);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1505);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1506);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1507);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1508);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1509);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1510);\nINSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1511);\n\nINSERT INTO sys_user_role(user_id, role_id) VALUES (3, 3);\nINSERT INTO sys_user_role(user_id, role_id) VALUES (4, 4);\n\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 102, 4, 1, '测试数据权限', '测试', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 102, 3, 2, '子节点1', '111', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 102, 3, 3, '子节点2', '222', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 108, 4, 4, '测试数据', 'demo', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 108, 3, 13, '子节点11', '1111', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 108, 3, 12, '子节点22', '2222', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 108, 3, 11, '子节点33', '3333', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 108, 3, 10, '子节点44', '4444', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 108, 3, 9, '子节点55', '5555', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 108, 3, 8, '子节点66', '6666', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 108, 3, 7, '子节点77', '7777', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 108, 3, 6, '子节点88', '8888', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 108, 3, 5, '子节点99', '9999', 0, sysdate(), 'admin', NULL, NULL, 0);\n\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 0, 102, 4, '测试数据权限', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 1, 102, 3, '子节点1', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 2, 102, 3, '子节点2', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 0, 108, 4, '测试树1', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 4, 108, 3, '子节点11', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 4, 108, 3, '子节点22', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 4, 108, 3, '子节点33', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 5, 108, 3, '子节点44', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 6, 108, 3, '子节点55', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 7, 108, 3, '子节点66', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 7, 108, 3, '子节点77', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 10, 108, 3, '子节点88', 0, sysdate(), 'admin', NULL, NULL, 0);\nINSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 10, 108, 3, '子节点99', 0, sysdate(), 'admin', NULL, NULL, 0);\n"
  },
  {
    "path": "script/sql/update/oracle/update-4.1-4.2.sql",
    "content": "ALTER TABLE \"SYS_OSS_CONFIG\" ADD (\"DOMAIN\" VARCHAR2(255));\n\nCOMMENT ON COLUMN \"SYS_OSS_CONFIG\".\"DOMAIN\" IS '自定义域名';\n\nupdate sys_oss_config set endpoint = '127.0.0.1:9000' where oss_config_id = 1;\nupdate sys_oss_config set endpoint = 's3-cn-north-1.qiniucs.com', region = '' where oss_config_id = 2;\nupdate sys_oss_config set endpoint = 'oss-cn-beijing.aliyuncs.com' where oss_config_id = 3;\nupdate sys_oss_config set endpoint = 'cos.ap-beijing.myqcloud.com' where oss_config_id = 4;\n\ninsert into sys_oss_config values (5, 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', 'N', '', '1', '', NULL, 'admin', sysdate, 'admin', sysdate, '');\n\nALTER TABLE \"GEN_TABLE_COLUMN\" MODIFY (\"TABLE_ID\" NUMBER(20,0));\n"
  },
  {
    "path": "script/sql/update/oracle/update-4.2-4.3.sql",
    "content": "insert into sys_menu values('112',  '缓存列表', '2',   '6', 'cacheList',  'monitor/cache/list',       '', 1, 0, 'C', '0', '0', 'monitor:cache:list',      'redis-list',    'admin', sysdate, '', null, '缓存列表菜单');\n\ndelete from sys_menu WHERE menu_id = 116;\n\nupdate sys_config set config_key = 'sys.account.captchaEnabled' where config_id = 4;\n\ninsert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock',  '#', 'admin', sysdate, '', null, '');\n\ninsert into sys_role_menu values ('2', '1050');\n\ninsert into sys_dict_data values(29, 99, '其他',     '0',       'sys_oper_type',       '',   'info',    'N', '0', 'admin', sysdate, '', null, '其他操作');\n"
  },
  {
    "path": "script/sql/update/oracle/update-4.3-4.4.sql",
    "content": "ALTER TABLE \"SYS_OSS_CONFIG\" ADD (\"ACCESS_POLICY\" CHAR(1) DEFAULT '1' NOT NULL);\n\nCOMMENT ON COLUMN \"SYS_OSS_CONFIG\".\"ACCESS_POLICY\" IS '桶权限类型(0=private 1=public 2=custom)';\n"
  },
  {
    "path": "script/sql/update/postgres/update-4.1-4.2.sql",
    "content": "ALTER TABLE \"sys_oss_config\" ADD COLUMN \"domain\" varchar(255);\n\nCOMMENT ON COLUMN \"sys_oss_config\".\"domain\" IS '自定义域名';\n\nupdate sys_oss_config set endpoint = '127.0.0.1:9000' where oss_config_id = 1;\nupdate sys_oss_config set endpoint = 's3-cn-north-1.qiniucs.com', region = '' where oss_config_id = 2;\nupdate sys_oss_config set endpoint = 'oss-cn-beijing.aliyuncs.com' where oss_config_id = 3;\nupdate sys_oss_config set endpoint = 'cos.ap-beijing.myqcloud.com' where oss_config_id = 4;\n\ninsert into sys_oss_config values (5, 'image',  'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', 'N', '', '1', '', 'admin', now(), 'admin', now(), NULL, '');\n\n"
  },
  {
    "path": "script/sql/update/postgres/update-4.2-4.3.sql",
    "content": "insert into sys_menu values('112',  '缓存列表', '2',   '6', 'cacheList',  'monitor/cache/list',       '', '1', '0', 'C', '0', '0', 'monitor:cache:list',      'redis-list',    'admin', now(), '', null, '缓存列表菜单');\n\ndelete from sys_menu WHERE menu_id = 116;\n\nupdate sys_config set config_key = 'sys.account.captchaEnabled' where config_id = 4;\n\ninsert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:unlock',  '#', 'admin', now(), '', null, '');\n\ninsert into sys_role_menu values ('2', '1050');\n\n-- 字符串自动转时间 避免框架时间查询报错问题\ncreate or replace function cast_varchar_to_timestamp(varchar) returns timestamptz as $$\nselect to_timestamp($1, 'yyyy-mm-dd hh24:mi:ss');\n$$ language sql strict ;\n\ncreate cast (varchar as timestamptz) with function cast_varchar_to_timestamp as IMPLICIT;\n\ninsert into sys_dict_data values(29, 99, '其他',     '0',       'sys_oper_type',       '',   'info',    'N', '0', 'admin', now(), '', null, '其他操作');\n\n"
  },
  {
    "path": "script/sql/update/postgres/update-4.3-4.4.sql",
    "content": "ALTER TABLE \"sys_oss_config\" ADD COLUMN \"access_policy\" char(1) NOT NULL DEFAULT '1'::bpchar;\n\nCOMMENT ON COLUMN \"sys_oss_config\".\"access_policy\" IS '桶权限类型(0=private 1=public 2=custom)';\n"
  },
  {
    "path": "script/sql/update/sqlserver/update-4.1-4.2.sql",
    "content": "ALTER TABLE [sys_oss_config] ADD [domain] nvarchar(255) DEFAULT '' NULL\nGO\n\nEXEC sp_addextendedproperty\n'MS_Description', N'自定义域名',\n'SCHEMA', N'dbo',\n'TABLE', N'sys_oss_config',\n'COLUMN', N'domain'\nGO\n\nUPDATE [sys_oss_config] SET [access_key] = N'ruoyi', [secret_key] = N'ruoyi123', [endpoint] = N'127.0.0.1:9000' WHERE [oss_config_id] = 1\nGO\n\nUPDATE [sys_oss_config] SET [endpoint] = N's3-cn-north-1.qiniucs.com' WHERE [oss_config_id] = 2\nGO\n\nUPDATE [sys_oss_config] SET [endpoint] = N'oss-cn-beijing.aliyuncs.com' WHERE [oss_config_id] = 3\nGO\n\nUPDATE [sys_oss_config] SET [endpoint] = N'cos.ap-beijing.myqcloud.com' WHERE [oss_config_id] = 4\nGO\n\nINSERT INTO [sys_oss_config] ([oss_config_id], [config_key], [access_key], [secret_key], [bucket_name], [prefix], [endpoint], [domain], [is_https], [region], [status], [ext1], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (N'5', N'image',  N'ruoyi', N'ruoyi123', N'ruoyi', N'image', N'127.0.0.1:9000', N'',N'N', N'', N'1', N'', N'admin', getdate(), N'admin', getdate(), NULL)\nGO\n\nALTER TABLE [gen_table_column] ALTER COLUMN [table_id] bigint NULL\nGO\n"
  },
  {
    "path": "script/sql/update/sqlserver/update-4.2-4.3.sql",
    "content": "INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (112, N'缓存列表', 2, 6, N'cacheList', N'monitor/cache/list', N'', 1, 0, N'C', N'0', N'0', N'monitor:cache:list', N'redis-list', N'admin', getdate(), N'', NULL, N'缓存列表菜单')\nGO\n\ndelete from sys_menu WHERE menu_id = 116;\nGO\n\nupdate sys_config set config_key = 'sys.account.captchaEnabled' where config_id = 4\nGO\n\nINSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1050, N'账户解锁', 501, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:unlock',  N'#', N'admin', getdate(), N'', null, N'')\nGO\n\nINSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1050)\nGO\n\nINSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (29, 99, N'其他', N'0', N'sys_oper_type', N'', N'info', N'N', N'0', N'admin', getdate(), N'', NULL, N'其他操作');\nGO\n"
  },
  {
    "path": "script/sql/update/sqlserver/update-4.3-4.4.sql",
    "content": "ALTER TABLE [sys_oss_config] ADD [access_policy] nchar(1) DEFAULT ('1') NOT NULL\nGO\n\nEXEC sp_addextendedproperty\n'MS_Description', N'桶权限类型(0=private 1=public 2=custom)',\n'SCHEMA', N'dbo',\n'TABLE', N'sys_oss_config',\n'COLUMN', N'access_policy'\nGO\n"
  },
  {
    "path": "script/sql/update/update-3.X-4.0.sql",
    "content": "ALTER TABLE `sys_user` MODIFY COLUMN `user_type` varchar(10) DEFAULT 'sys_user' COMMENT '用户类型（sys_user系统用户）' AFTER `nick_name`;\n\nUPDATE `sys_user` SET `nick_name` = '疯狂的狮子Li', `user_type` = 'sys_user', `email` = 'crazyLionLi@163.com' WHERE `user_id` = 1;\nUPDATE `sys_user` SET `user_name` = 'lionli', `nick_name` = '疯狂的狮子Li', `user_type` = 'sys_user', `email` = 'crazyLionLi@163.com' WHERE `user_id` = 2;\nUPDATE `sys_user` SET `nick_name` = '本部门及以下 密码666666', `user_type` = 'sys_user', `password` = '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne' WHERE `user_id` = 3;\nUPDATE `sys_user` SET `nick_name` = '仅本人 密码666666', `user_type` = 'sys_user', `password` = '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne' WHERE `user_id` = 4;\n"
  },
  {
    "path": "script/sql/update/update-4.0-4.1.sql",
    "content": "alter table sys_menu change query query_param varchar(255) default null comment '路由参数';\n\nalter table sys_dept modify column ancestors varchar(500) null default '' comment '祖级列表';\n"
  },
  {
    "path": "script/sql/update/update-4.1-4.2.sql",
    "content": "alter table sys_oss_config add column domain varchar(255) null default '' COMMENT '自定义域名';\n\nupdate sys_oss_config set endpoint = '127.0.0.1:9000' where oss_config_id = 1;\nupdate sys_oss_config set endpoint = 's3-cn-north-1.qiniucs.com', region = '' where oss_config_id = 2;\nupdate sys_oss_config set endpoint = 'oss-cn-beijing.aliyuncs.com' where oss_config_id = 3;\nupdate sys_oss_config set endpoint = 'cos.ap-beijing.myqcloud.com' where oss_config_id = 4;\n\ninsert into sys_oss_config values (5, 'image',  'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', 'N', '', '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL, '');\n\nalter table gen_table_column modify column table_id bigint(0) null default null COMMENT '归属表编号';\n\nalter table sys_notice modify column notice_id bigint(0) not null COMMENT '公告ID';\n\nalter table sys_config modify column config_id bigint(0) not null COMMENT '参数主键';\n"
  },
  {
    "path": "script/sql/update/update-4.2-4.3.sql",
    "content": "insert into sys_menu values('112',  '缓存列表', '2',   '6', 'cacheList',  'monitor/cache/list',       '', 1, 0, 'C', '0', '0', 'monitor:cache:list',      'redis-list',    'admin', sysdate(), '', null, '缓存列表菜单');\n\ndelete from sys_menu WHERE menu_id = 116;\n\nupdate sys_config set config_key = 'sys.account.captchaEnabled' where config_id = 4;\n\ninsert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock',  '#', 'admin', sysdate(), '', null, '');\n\ninsert into sys_role_menu values ('2', '1050');\n\ninsert into sys_dict_data values(29, 99, '其他',     '0',       'sys_oper_type',       '',   'info',    'N', '0', 'admin', sysdate(), '', null, '其他操作');\n"
  },
  {
    "path": "script/sql/update/update-4.3-4.4.sql",
    "content": "ALTER TABLE sys_oss_config ADD COLUMN access_policy char(1) NOT NULL DEFAULT 1 COMMENT '桶权限类型(0=private 1=public 2=custom)' AFTER region;\n"
  }
]