[
  {
    "path": ".gitignore",
    "content": "### gradle ###\n.gradle\n/build/\n!gradle/wrapper/gradle-wrapper.jar\n\n### STS ###\n.settings/\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\nbin/\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\nrebel.xml\n/ginyi-springboot/.idea\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\nnbdist/\n.nb-gradle/\n\n### maven ###\ntarget/\n*.war\n*.ear\n*.zip\n*.tar\n*.tar.gz\n*.versionsBackup\n\n### vscode ###\n.vscode\n\n### logs ###\n/logs/\n*.log\n\n### temp ignore ###\n*.cache\n*.diff\n*.patch\n*.tmp\n*.java~\n*.properties~\n*.xml~\n\n### system ignore ###\n.DS_Store\nThumbs.db\nServers\n.metadata\n\n\n/**/*.idea/**"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\r\n                           Version 2.0, January 2004\r\n                        http://www.apache.org/licenses/\r\n\r\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\r\n\r\n   1. Definitions.\r\n\r\n      \"License\" shall mean the terms and conditions for use, reproduction,\r\n      and distribution as defined by Sections 1 through 9 of this document.\r\n\r\n      \"Licensor\" shall mean the copyright owner or entity authorized by\r\n      the copyright owner that is granting the License.\r\n\r\n      \"Legal Entity\" shall mean the union of the acting entity and all\r\n      other entities that control, are controlled by, or are under common\r\n      control with that entity. For the purposes of this definition,\r\n      \"control\" means (i) the power, direct or indirect, to cause the\r\n      direction or management of such entity, whether by contract or\r\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\r\n      outstanding shares, or (iii) beneficial ownership of such entity.\r\n\r\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\r\n      exercising permissions granted by this License.\r\n\r\n      \"Source\" form shall mean the preferred form for making modifications,\r\n      including but not limited to software source code, documentation\r\n      source, and configuration files.\r\n\r\n      \"Object\" form shall mean any form resulting from mechanical\r\n      transformation or translation of a Source form, including but\r\n      not limited to compiled object code, generated documentation,\r\n      and conversions to other media types.\r\n\r\n      \"Work\" shall mean the work of authorship, whether in Source or\r\n      Object form, made available under the License, as indicated by a\r\n      copyright notice that is included in or attached to the work\r\n      (an example is provided in the Appendix below).\r\n\r\n      \"Derivative Works\" shall mean any work, whether in Source or Object\r\n      form, that is based on (or derived from) the Work and for which the\r\n      editorial revisions, annotations, elaborations, or other modifications\r\n      represent, as a whole, an original work of authorship. For the purposes\r\n      of this License, Derivative Works shall not include works that remain\r\n      separable from, or merely link (or bind by name) to the interfaces of,\r\n      the Work and Derivative Works thereof.\r\n\r\n      \"Contribution\" shall mean any work of authorship, including\r\n      the original version of the Work and any modifications or additions\r\n      to that Work or Derivative Works thereof, that is intentionally\r\n      submitted to Licensor for inclusion in the Work by the copyright owner\r\n      or by an individual or Legal Entity authorized to submit on behalf of\r\n      the copyright owner. For the purposes of this definition, \"submitted\"\r\n      means any form of electronic, verbal, or written communication sent\r\n      to the Licensor or its representatives, including but not limited to\r\n      communication on electronic mailing lists, source code control systems,\r\n      and issue tracking systems that are managed by, or on behalf of, the\r\n      Licensor for the purpose of discussing and improving the Work, but\r\n      excluding communication that is conspicuously marked or otherwise\r\n      designated in writing by the copyright owner as \"Not a Contribution.\"\r\n\r\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\r\n      on behalf of whom a Contribution has been received by Licensor and\r\n      subsequently incorporated within the Work.\r\n\r\n   2. Grant of Copyright License. Subject to the terms and conditions of\r\n      this License, each Contributor hereby grants to You a perpetual,\r\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r\n      copyright license to reproduce, prepare Derivative Works of,\r\n      publicly display, publicly perform, sublicense, and distribute the\r\n      Work and such Derivative Works in Source or Object form.\r\n\r\n   3. Grant of Patent License. Subject to the terms and conditions of\r\n      this License, each Contributor hereby grants to You a perpetual,\r\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r\n      (except as stated in this section) patent license to make, have made,\r\n      use, offer to sell, sell, import, and otherwise transfer the Work,\r\n      where such license applies only to those patent claims licensable\r\n      by such Contributor that are necessarily infringed by their\r\n      Contribution(s) alone or by combination of their Contribution(s)\r\n      with the Work to which such Contribution(s) was submitted. If You\r\n      institute patent litigation against any entity (including a\r\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\r\n      or a Contribution incorporated within the Work constitutes direct\r\n      or contributory patent infringement, then any patent licenses\r\n      granted to You under this License for that Work shall terminate\r\n      as of the date such litigation is filed.\r\n\r\n   4. Redistribution. You may reproduce and distribute copies of the\r\n      Work or Derivative Works thereof in any medium, with or without\r\n      modifications, and in Source or Object form, provided that You\r\n      meet the following conditions:\r\n\r\n      (a) You must give any other recipients of the Work or\r\n          Derivative Works a copy of this License; and\r\n\r\n      (b) You must cause any modified files to carry prominent notices\r\n          stating that You changed the files; and\r\n\r\n      (c) You must retain, in the Source form of any Derivative Works\r\n          that You distribute, all copyright, patent, trademark, and\r\n          attribution notices from the Source form of the Work,\r\n          excluding those notices that do not pertain to any part of\r\n          the Derivative Works; and\r\n\r\n      (d) If the Work includes a \"NOTICE\" text file as part of its\r\n          distribution, then any Derivative Works that You distribute must\r\n          include a readable copy of the attribution notices contained\r\n          within such NOTICE file, excluding those notices that do not\r\n          pertain to any part of the Derivative Works, in at least one\r\n          of the following places: within a NOTICE text file distributed\r\n          as part of the Derivative Works; within the Source form or\r\n          documentation, if provided along with the Derivative Works; or,\r\n          within a display generated by the Derivative Works, if and\r\n          wherever such third-party notices normally appear. The contents\r\n          of the NOTICE file are for informational purposes only and\r\n          do not modify the License. You may add Your own attribution\r\n          notices within Derivative Works that You distribute, alongside\r\n          or as an addendum to the NOTICE text from the Work, provided\r\n          that such additional attribution notices cannot be construed\r\n          as modifying the License.\r\n\r\n      You may add Your own copyright statement to Your modifications and\r\n      may provide additional or different license terms and conditions\r\n      for use, reproduction, or distribution of Your modifications, or\r\n      for any such Derivative Works as a whole, provided Your use,\r\n      reproduction, and distribution of the Work otherwise complies with\r\n      the conditions stated in this License.\r\n\r\n   5. Submission of Contributions. Unless You explicitly state otherwise,\r\n      any Contribution intentionally submitted for inclusion in the Work\r\n      by You to the Licensor shall be under the terms and conditions of\r\n      this License, without any additional terms or conditions.\r\n      Notwithstanding the above, nothing herein shall supersede or modify\r\n      the terms of any separate license agreement you may have executed\r\n      with Licensor regarding such Contributions.\r\n\r\n   6. Trademarks. This License does not grant permission to use the trade\r\n      names, trademarks, service marks, or product names of the Licensor,\r\n      except as required for reasonable and customary use in describing the\r\n      origin of the Work and reproducing the content of the NOTICE file.\r\n\r\n   7. Disclaimer of Warranty. Unless required by applicable law or\r\n      agreed to in writing, Licensor provides the Work (and each\r\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\r\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\r\n      implied, including, without limitation, any warranties or conditions\r\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\r\n      PARTICULAR PURPOSE. You are solely responsible for determining the\r\n      appropriateness of using or redistributing the Work and assume any\r\n      risks associated with Your exercise of permissions under this License.\r\n\r\n   8. Limitation of Liability. In no event and under no legal theory,\r\n      whether in tort (including negligence), contract, or otherwise,\r\n      unless required by applicable law (such as deliberate and grossly\r\n      negligent acts) or agreed to in writing, shall any Contributor be\r\n      liable to You for damages, including any direct, indirect, special,\r\n      incidental, or consequential damages of any character arising as a\r\n      result of this License or out of the use or inability to use the\r\n      Work (including but not limited to damages for loss of goodwill,\r\n      work stoppage, computer failure or malfunction, or any and all\r\n      other commercial damages or losses), even if such Contributor\r\n      has been advised of the possibility of such damages.\r\n\r\n   9. Accepting Warranty or Additional Liability. While redistributing\r\n      the Work or Derivative Works thereof, You may choose to offer,\r\n      and charge a fee for, acceptance of support, warranty, indemnity,\r\n      or other liability obligations and/or rights consistent with this\r\n      License. However, in accepting such obligations, You may act only\r\n      on Your own behalf and on Your sole responsibility, not on behalf\r\n      of any other Contributor, and only if You agree to indemnify,\r\n      defend, and hold each Contributor harmless for any liability\r\n      incurred by, or claims asserted against, such Contributor by reason\r\n      of your accepting any such warranty or additional liability.\r\n\r\n   END OF TERMS AND CONDITIONS\r\n\r\n   APPENDIX: How to apply the Apache License to your work.\r\n\r\n      To apply the Apache License to your work, attach the following\r\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\r\n      replaced with your own identifying information. (Don't include\r\n      the brackets!)  The text should be enclosed in the appropriate\r\n      comment syntax for the file format. We also recommend that a\r\n      file or class name and description of purpose be included on the\r\n      same \"printed page\" as the copyright notice for easier\r\n      identification within third-party archives.\r\n\r\n   Copyright [yyyy] [name of copyright owner]\r\n\r\n   Licensed under the Apache License, Version 2.0 (the \"License\");\r\n   you may not use this file except in compliance with the License.\r\n   You may obtain a copy of the License at\r\n\r\n       http://www.apache.org/licenses/LICENSE-2.0\r\n\r\n   Unless required by applicable law or agreed to in writing, software\r\n   distributed under the License is distributed on an \"AS IS\" BASIS,\r\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n   See the License for the specific language governing permissions and\r\n   limitations under the License.\r\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\" style=\"margin-left: 20px;\">\n\t<img alt=\"logo\" src=\"https://gitee.com/my-images/typora-imgs/raw/master/%E5%93%86%E5%95%A6a%E6%A2%A6test.png\" width=\"80\" height=\"80\">\n</p>\n<h1 align=\"center\" style=\"margin-top: 0;\">Ginyi</h1>\n<h5 align=\"center\">基于对优秀开源项目RuoYi-Vue的喜爱，决定重构若依，打造属于自己的快速开发平台！</h5>\n<p align=\"right\">————向优秀致敬，向榜样学习！</p>\n<p align=\"center\">\n\t<a href=\"https://gitee.com/Ginyi/ginyi-spring-vue\"><img src=\"https://img.shields.io/badge/license-Apache 2-red\"></a>\n\t<a href=\"https://gitee.com/Ginyi/ginyi-spring-vue\"><img src=\"https://img.shields.io/badge/Ginyi-v1.0.0-red\"></a>\n\t<a href=\"https://gitee.com/Ginyi/ginyi-spring-vue\"><img src=\"https://img.shields.io/badge/SpringBoot-Vue3-red\"></a>\n</p>\n\n\n\n\n\n### 平台简介\n\n基于`SpringBoot`和`Vue3`，搭配`Naive UI`组件库，模块清晰，界面美观，可自主选择主题色，提高视觉舒适度！部分功能尚未实现，有时间将持续更新，欢迎Star⭐!\n\n\n\n### 特别鸣谢\n\n- [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue?_from=gitee_search)\n- [Naive UI](https://www.naiveui.com/)\n\n\n\n### 项目地址\n\n演示地址：http://114.132.120.190:3800 （`仅PC端`）\n\n接口地址：http://114.132.120.190:8066/doc.html\n\n| 演示账号 / 密码    | 说明                         |\n| ------------------ | ---------------------------- |\n| `admin / 123456`   | 超级管理员，具有全部权限     |\n| `Ginyi / 123456`   | 系统管理员，具有系统管理权限 |\n| `order / 123456`   | 订单管理员，具有订单管理权限 |\n| `product / 123456` | 商品管理员，具有商品管理权限 |\n\n**注：**学生机服务器，5月份到期~ 请珍惜公用资源，请勿攻击~ 拜托拜托！😘😘😘 \n\n\n\n### 帮助文档\n\n- 没有~不想写文档...如果`Star`数量破`1k`，可以考虑一下！\n- 可以参考`Ruoyi`的文档 http://doc.ruoyi.vip/（仅后端）\n\n\n\n### 技术选型\n\n| 前端                                     | 后端                            |\n| ---------------------------------------- | ------------------------------- |\n| `Node` 项目环境基础                      | `Maven` 构建多模块项目          |\n| `Vite4` 构建项目                         | `Springboot` 项目主框架         |\n| `Vue3` 项目主框架                        | `MySQL` 关系型数据库            |\n| `TypeScript` 全量TS编写，搭配TSX模板渲染 | `MongoDB` 文档型数据库          |\n| `Naive UI` 组件库                        | `Redis` 基于内存数据库          |\n| `Pinia` 全局状态管理                     | `SpringSecurity & JWT` 权限框架 |\n| `Axios` 网络请求库                       | `MyBatis-Plus` ORM框架          |\n| `Monaco` 代码编辑器（vscode）            | `Hutool` 工具类                 |\n| `...`                                    | `...`                           |\n\n\n\n### 内置功能\n\n| 功能         | 介绍                                                         | Ruoyi | Ginyi |\n| ------------ | ------------------------------------------------------------ | ----- | ----- |\n| `用户管理`   | 用户是系统操作者，该功能主要完成系统用户配置                 | ⭕     | ⭕     |\n| `部门管理`   | 配置系统组织机构（公司、部门、小组），树结构展现支持数据权限 | ⭕     | ⭕     |\n| `岗位管理`   | 配置系统用户所属担任职务                                     | ⭕     | ⭕     |\n| `菜单管理`   | 配置系统菜单，操作权限，按钮权限标识等                       | ⭕     | ⭕     |\n| `角色管理`   | 角色菜单权限分配、设置角色按机构进行数据范围权限划分         | ⭕     | ⭕     |\n| `字典管理`   | 对系统中经常使用的一些较为固定的数据进行维护                 | ⭕     | ❎     |\n| `参数管理`   | 对系统动态配置常用参数                                       | ⭕     | ❎     |\n| `通知公告`   | 系统通知公告信息发布维护                                     | ⭕     | ❎     |\n| `操作日志`   | 系统正常操作日志记录和查询；系统异常信息日志记录和查询       | ⭕     | ⭕     |\n| `登录日志`   | 系统登录日志记录查询包含登录异常                             | ⭕     | ⭕     |\n| `在线用户`   | 当前系统中活跃用户状态监控                                   | ⭕     | ⭕     |\n| `定时任务`   | 在线（添加、修改、删除)任务调度包含执行结果日志              | ⭕     | ❎     |\n| `代码生成`   | 前后端代码的生成（java、html、xml、sql）支持CRUD下载         | ⭕     | ❎     |\n| `系统接口`   | 根据业务代码自动生成相关的api接口文档                        | ⭕     | ❎     |\n| `服务监控`   | 监视当前系统CPU、内存、磁盘、堆栈等相关信息                  | ⭕     | ❎     |\n| `缓存监控`   | 对系统的缓存信息查询，命令统计等                             | ⭕     | ❎     |\n| `连接池监视` | 监视当前系统数据库连接池状态，可进行分析SQL找出系统性能瓶颈  | ⭕     | ❎     |\n\n**备注：**\n\n- 尚未实现的功能，有时间将继续更新！\n\n\n\n### 效果预览\n\n<table>\n    <tr>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230305141535709.png\"/></td>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230305141626273.png\"/></td>\n    </tr>\n    <tr>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230304205342498.png\"/></td>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230304205431871.png\"/></td>\n    </tr>\n    <tr>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230304205625969.png\"/></td>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230304205559401.png\"/></td>\n    </tr>\n    <tr>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230305141742131.png\"/></td>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230305141818713.png\"/></td>\n    </tr>\n    <tr>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230304210204690.png\"/></td>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230304210228094.png\"/></td>\n    </tr>\n     <tr>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230305141922643.png\"/></td>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230305142006594.png\"/></td>\n    </tr>\n    <tr>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230304210613278.png\"/></td>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230304210651948.png\"/></td>\n    </tr>\n    <tr>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230305142419340.png\"/></td>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230305142444669.png\"/></td>\n    </tr>\n    <tr>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230304211123371.png\"/></td>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230304211109116.png\"/></td>\n    </tr>\n    <tr>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230305142650617.png\"/></td>\n        <td><img src=\"https://gitee.com/my-images/typora-imgs/raw/master/image-20230305142737090.png\"/></td>\n    </tr>\n</table>\n"
  },
  {
    "path": "ginyi-springboot/.gitignore",
    "content": "### gradle ###\n.gradle\n/build/\n!gradle/wrapper/gradle-wrapper.jar\n\n### STS ###\n.settings/\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\nbin/\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\nrebel.xml\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\nnbdist/\n.nb-gradle/\n\n### maven ###\ntarget/\n*.war\n*.ear\n*.zip\n*.tar\n*.tar.gz\n*.versionsBackup\n\n### vscode ###\n.vscode\n\n### logs ###\n/logs/\n*.log\n\n### temp ignore ###\n*.cache\n*.diff\n*.patch\n*.tmp\n*.java~\n*.properties~\n*.xml~\n\n### system ignore ###\n.DS_Store\nThumbs.db\nServers\n.metadata\n"
  },
  {
    "path": "ginyi-springboot/README.md",
    "content": "### 项目地址\n\n接口文档：http://114.132.120.190:8066/doc.html\n\n代码仓库：https://gitee.com/Ginyi/ginyi-spring-vue\n\n\n\n### 帮助文档\n\n- 没有~不想写文档...如果`Star`数量破`1k`，可以考虑一下！\n- 可以参考`Ruoyi`的文档 http://doc.ruoyi.vip/\n\n\n\n### 项目特色\n\n- 使用主流技术栈`SpringBoot`、`Mybatis-plus`、`Redis`、`MySQL`、`MongoDB`等，易上手！\n- 基于`SpringSecurity`安全框架实现`RBAC`的权限模式，易扩展易维护！\n- 基于`Maven`多模块，可在上层应用中创建多个服务！模块分明，各司其职！\n- 内置多个后台系统常用功能，搭配完善的接口文档：\n  - 系统管理：`用户管理`、`部门管理`、`岗位管理`、`角色管理`、`菜单管理`等\n  - 日志管理：`登录日志`、`操作日志`、`在线用户`等\n\n\n\n### 前置知识\n\n**重点：**使用本项目时，你需要具备以下基础知识：\n\n1. `SpringBoot`框架基础，熟悉`MVC`分层的开发模式！\n2. `Maven`基础，掌握多模块项目下，服务间的依赖引用！\n3. `Redis`、`MongoDB`中间件、`Mybatis`和`Mybatis-plus`等`ORM`框架的使用！\n\n\n\n### 项目环境\n\n**注意：**使用本项目时，为了你能愉快地进行开发，请尽可能使用以下环境进行开发：\n\n1. `JDK v1.8`\n2. `Maven v3.6.3`\n3. `MySQL v8.0`\n4. `IntelliJ IDEA`\n\n\n\n### 项目结构\n\n```bash\nginyi-springboot\n|\n├─ ginyi-common  # 通用模块\n│  ├─ ginyi-common-annotation      # 通用注解\n│  ├─ ginyi-common-constants       # 通用常量\n│  ├─ ginyi-common-enums           # 通用枚举\n│  ├─ ginyi-common-exception       # 通用异常处理\n│  ├─ ginyi-common-mysql           # 通用MySql配置\n│  ├─ ginyi-common-redis           # 通用Redis配置\n│  ├─ ginyi-common-result          # 通用结果集\n│  ├─ ginyi-common-swagger         # 通用在线接口文档配置\n│  └─ ginyi-common-utils           # 通用工具类\n|\n├─ ginyi-framework  # 框架模块\n│  ├─ ginyi-framework-core         # 框架核心\n│  ├─ ginyi-framework-security     # 安全框架\n│  └─ ginyi-framework-websocket    # 实时通讯框架\n|\n├─ ginyi-server   # 服务模块（对外提供api接口）\n│  ├─ ginyi-server-admin           # 后台系统端（包含系统接口）\n│  ├─ ginyi-server-common          # 通用服务\n│  └─ ginyi-server-web             # 前台应用端\n|\n├─ ginyi-system  # 系统服务模块（包含yaml配置、系统Entity、系统Service、系统Mapper等）\n|\n└─ sql  # 数据库文件\n```\n\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-annotation/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>ginyi-common</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-common-annotation</artifactId>\n\n    <properties>\n        <maven.compiler.source>8</maven.compiler.source>\n        <maven.compiler.target>8</maven.compiler.target>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-common-enums</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-annotation/src/main/java/ginyi.common.annotation/Anonymous.java",
    "content": "package ginyi.common.annotation;\n\nimport java.lang.annotation.*;\n\n/**\n * 匿名访问不鉴权注解\n * \n * @author ruoyi\n */\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Anonymous\n{\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-annotation/src/main/java/ginyi.common.annotation/CreateBy.java",
    "content": "package ginyi.common.annotation;\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@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.FIELD})\npublic @interface CreateBy {\n    String value() default \"\";\n}"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-annotation/src/main/java/ginyi.common.annotation/CreateTime.java",
    "content": "package ginyi.common.annotation;\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@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.FIELD})\npublic @interface CreateTime {\n    String value() default \"\";\n}"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-annotation/src/main/java/ginyi.common.annotation/Log.java",
    "content": "package ginyi.common.annotation;\n\nimport ginyi.common.enums.BusinessType;\nimport ginyi.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    public String title() default \"\";\n\n    /**\n     * 功能\n     */\n    public BusinessType businessType() default BusinessType.OTHER;\n\n    /**\n     * 操作人类别\n     */\n    public OperatorType operatorType() default OperatorType.MANAGE;\n\n    /**\n     * 是否保存请求的参数\n     */\n    public boolean isSaveRequestData() default true;\n\n    /**\n     * 是否保存响应的参数\n     */\n    public boolean isSaveResponseData() default true;\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-annotation/src/main/java/ginyi.common.annotation/RepeatSubmit.java",
    "content": "package ginyi.common.annotation;\n\nimport java.lang.annotation.*;\n\n/**\n * 自定义注解防止表单重复提交\n *\n * @author ruoyi\n */\n@Inherited\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface RepeatSubmit {\n    /**\n     * 间隔时间(ms)，小于此时间视为重复提交\n     */\n    public int interval() default 5000;\n\n    /**\n     * 提示消息\n     */\n    public String message() default \"不允许重复提交，请稍候再试\";\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-annotation/src/main/java/ginyi.common.annotation/UpdateBy.java",
    "content": "package ginyi.common.annotation;\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@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.FIELD})\npublic @interface UpdateBy {\n    String value() default \"\";\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-annotation/src/main/java/ginyi.common.annotation/UpdateTime.java",
    "content": "package ginyi.common.annotation;\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@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.FIELD})\npublic @interface UpdateTime {\n    String value() default \"\";\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-constants/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>ginyi-common</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-common-constants</artifactId>\n\n    <properties>\n        <maven.compiler.source>8</maven.compiler.source>\n        <maven.compiler.target>8</maven.compiler.target>\n    </properties>\n\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-constants/src/main/java/ginyi/common/constant/CacheConstants.java",
    "content": "package ginyi.common.constant;\n\n/**\n * 缓存的key 常量\n *\n * @author ruoyi\n */\npublic class CacheConstants {\n    /**\n     * 参数管理 cache key\n     */\n    public static final String SYS_CONFIG_KEY = \"sys_config:\";\n\n    /**\n     * 验证码 redis key\n     */\n    public static final String CAPTCHA_CODE_KEY = \"captcha_codes:\";\n\n    /**\n     * 登录用户 redis key\n     */\n    public static final String LOGIN_TOKEN_KEY = \"login_tokens:\";\n\n    /**\n     * 登录账户密码错误次数 redis key\n     */\n    public static final String PWD_ERR_CNT_KEY = \"pwd_err_cnt:\";\n\n    /**\n     * 防重提交 redis key\n     */\n    public static final String REPEAT_SUBMIT_KEY = \"repeat_submit:\";\n\n    /** 防止恶意请求 */\n    public static final String API_REQUEST = \"api_request:\";\n\n\n\n    /** 菜单前缀，用于批量删除 */\n    public static final String MENU_KEY_PREFIX = \"menu_*\";\n    /** 菜单列表 */\n    public static final String MENU_LIST_KEY = \"menu_list\";\n    /** 用户菜单的菜单列表 */\n    public static final String MENU_USER_LIST_KEY = \"menu_user_list:\";\n    /** 菜单不存在的key */\n    public static final String MENU_NOT_EXIST_KEY = \"menu_not_exist:\";\n    /** 菜单详情 */\n    public static final String MENU_DETAILS_BY_ID_KEY = \"menu_details_by_id:\";\n\n\n    /** 用户前缀，用于批量删除 */\n    public static final String USER_KEY_PREFIX = \"user_*\";\n    /** 用户列表的key */\n    public static final String USER_LIST_KEY = \"user_list\";\n    /** 用户不存在的key */\n    public static final String USER_NOT_EXIST_KEY = \"user_not_exist:\";\n    /** 用户详情 */\n    public static final  String USER_DETAILS_BY_USERID_KEY = \"user_details_by_userId:\";\n\n\n    /** 部门前缀，用于批量删除 */\n    public static final String DEPT_KEY_PREFIX = \"dept_*\";\n    /** 部门列表 */\n    public static final String DEPT_LIST_KEY = \"dept_list\";\n    /** 部门不存在的key */\n    public static final String DEPT_NOT_EXIST_KEY = \"dept_not_exist:\";\n    /** 部门详情 */\n    public static final String DEPT_DETAILS_BY_DEPTID_KEY = \"dept_details_by_deptId:\";\n    /** 同分支下部门名称已被使用 */\n    public static final String DEPT_NAME_USED_KEY = \"dept_name_used_key:\";\n\n\n    /** 岗位前缀，用于批量删除 */\n    public static final String POST_KEY_PREFIX = \"post_*\";\n    /** 岗位列表 */\n    public static final String POST_LIST_KEY = \"post_list\";\n    /** 岗位不存在大的key */\n    public static final String POST_NOT_EXIST_KEY = \"post_not_exist:\";\n    /** 岗位详情 */\n    public static final String POST_DETAILS_BY_POSTID_KEY = \"post_details_by_postId:\";\n\n\n    /** 角色前缀，用于批量删除 */\n    public static final String ROLE_KEY_PREFIX = \"role_*\";\n    /** 角色列表 */\n    public static final String ROLE_LIST_KEY = \"role_list\";\n    /** 角色不存在的key */\n    public static final String ROLE_NOT_EXIST_KEY = \"role_not_exist:\";\n    /** 角色名已被使用 */\n    public static final String ROLE_NAME_USED_KEY = \"role_name_used:\";\n    /** 角色编码已被使用 */\n    public static final String ROLE_CODE_USED_KEY = \"role_code_used:\";\n    /** 角色详情 */\n    public static final String ROLE_DETAILS_BY_ROLEID_KEY = \"role_details_by_roleId:\";\n\n    /** 通知公告不存在的key */\n    public static final String NOTICE_NOT_EXIST_KEY = \"notice_not_exist:\";\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-constants/src/main/java/ginyi/common/constant/CommonMessageConstants.java",
    "content": "package ginyi.common.constant;\n\n/**\n * 错误信息常量\n */\npublic class CommonMessageConstants {\n\n    public static final String LOGIN_SUCCESS = \"登录成功\";\n    public static final String REGISTER_SUCCESS = \"注册成功\";\n    public static final String REDIS_KEY_NOT_EXIST = \"该Key不存在，请仔细检查！\";\n    public static final String REDIS_VALUE_TYPE_NOT_MATCH = \"数据类型不匹配，请联系管理人员！\";\n\n    public static final String SYS_ERROR = \"请联系管理人员！\";\n    public static final String SYS_AUTHENTICATION_VALID = \"权限不足，无法访问系统资源！\";\n    public static final String SYS_REQUEST_ILLEGAL = \"请求参数错误，请仔细检查！\";\n    public static final String SYS_CAPTCHA_NOT_EXIST = \"验证码不能为空！\";\n    public static final String SYS_PREVIEW_ENV = \"演示环境不允许进行此操作！\";\n    public static final String SYS_BED_REQUEST = \"恶意请求！该请求已被终止！\";\n\n    public static final String UPLOAD_FILE_ERROR = \"文件上传业务异常\";\n    public static final String UPLOAD_SIZE_EXCEED = \"单文件大小不得大于5MB，总文件大小不得大于50MB\";\n\n    public static final String VERIFY_EXPIRE = \"验证码已失效！\";\n    public static final String VERiFY_INCORRECT = \"验证码错误！\";\n\n    public static final String USER_PASSWORD_NOT_MATCH = \"用户名密码不匹配！\";\n    public static final String USER_EXIST = \"用户已存在！\";\n    public static final String USER_NOT_EXIST = \"用户不存在！\";\n    public static final String USER_IS_DELETED = \"用户已被注销！\";\n    public static final String USER_IS_FORBIDDEN = \"用户已被停用！\";\n    public static final String USER_IS_LOCKED = \"用户已被停用！\";\n    public static final String USER_NAME_USED = \"用户名已被占用！\";\n    public static final String USER_NOT_FOUND = \"请求参数错误，请仔细检查！\";\n    public static final String USER_ID_NOT_FOUND = \"请求参数错误，用户ID不能为空！\";\n    public static final String USER_STATUS_ILLEGAL = \"请求参数错误，用户状态不合法！\";\n    public static final String USER_IDS_ILLEGAL = \"通知的用户不能为空！\";\n\n\n    public static final String MENU_COMPONENT_NOT_EXIST = \"组件路径不存在！\";\n    public static final String MENU_PATH_NOT_EXIST = \"路由地址不存在！\";\n    public static final String MENU_NAME_USED = \"菜单名称已被使用！\";\n    public static final String MENU_NOT_EXIST = \"菜单不存在！\";\n    public static final String MENU_PARENT_NOT_EXIST = \"上级菜单不存在！\";\n    public static final String MENU_ID_NOT_FOUND = \"请求参数错误，菜单ID不能为空！\";\n    public static final String MENU_STATUS_ILLEGAL = \"请求参数错误，菜单状态不合法！\";\n\n\n    public static final String POST_NOT_EXIST = \"岗位不存在！\";\n    public static final String POST_NAME_USED = \"岗位名称已被使用！\";\n    public static final String POST_CODE_USED = \"岗位编码已被使用！\";\n    public static final String POST_ID_NOT_FOUND = \"请求参数错误，岗位ID不能为空！\";\n    public static final String POST_STATUS_ILLEGAL = \"请求参数错误，岗位状态不合法！\";\n\n\n    public static final String ROLE_NOT_EXIST = \"角色不存在！\";\n    public static final String ROLE_NAME_USED = \"角色名称已被使用！\";\n    public static final String ROLE_PERMISSION_CODE_USED = \"角色权限字符已被使用！\";\n    public static final String ROLE_MENU_NOT_EXIST = \"菜单权限不存在！\";\n    public static final String ROLE_ID_NOT_FOUND = \"请求参数错误，角色ID不能为空！\";\n    public static final String ROLE_STATUS_ILLEGAL = \"请求参数错误，角色状态不合法！\";\n\n\n    public static final String DEPT_NOT_EXIST = \"部门不存在！\";\n    public static final String DEPT_NAME_USED = \"同分支下部门名称已被使用！\";\n    public static final String DEPT_PARENT_NOT_EXIST = \"上级部门不存在！\";\n    public static final String DEPT_ID_NOT_FOUND = \"请求参数错误，部门ID不能为空！\";\n    public static final String DEPT_STATUS_ILLEGAL = \"请求参数错误，部门状态不合法！\";\n\n\n    public static final String NOTICE_NOT_EXIST = \"通知公告不存在！\";\n    public static final String NOTICE_CURRENT_USER_NOT_EXIST = \"当前用户不存在该消息！\";\n    public static final String NOTICE_HAVE_READ = \"当前通知公告已确认，无需二次确认！\";\n    public static final String NOTICE_NOT_ALLOW = \"当前通知公告无法被修改！\";\n\n\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-constants/src/main/java/ginyi/common/constant/HttpStatus.java",
    "content": "package ginyi.common.constant;\n\n/**\n * 返回状态码\n * \n * @author ruoyi\n */\npublic class HttpStatus\n{\n    /**\n     * 操作成功\n     */\n    public static final int SUCCESS = 200;\n\n    /**\n     * 对象创建成功\n     */\n    public static final int CREATED = 201;\n\n    /**\n     * 请求已经被接受\n     */\n    public static final int ACCEPTED = 202;\n\n    /**\n     * 操作已经执行成功，但是没有返回数据\n     */\n    public static final int NO_CONTENT = 204;\n\n    /**\n     * 资源已被移除\n     */\n    public static final int MOVED_PERM = 301;\n\n    /**\n     * 重定向\n     */\n    public static final int SEE_OTHER = 303;\n\n    /**\n     * 资源没有被修改\n     */\n    public static final int NOT_MODIFIED = 304;\n\n    /**\n     * 参数列表错误（缺少，格式不匹配）\n     */\n    public static final int BAD_REQUEST = 400;\n\n    /**\n     * 未授权\n     */\n    public static final int UNAUTHORIZED = 401;\n\n    /**\n     * 访问受限，授权过期\n     */\n    public static final int FORBIDDEN = 403;\n\n    /**\n     * 资源，服务未找到\n     */\n    public static final int NOT_FOUND = 404;\n\n    /**\n     * 不允许的http方法\n     */\n    public static final int BAD_METHOD = 405;\n\n    /**\n     * 资源冲突，或者资源被锁\n     */\n    public static final int CONFLICT = 409;\n\n    /**\n     * 不支持的数据，媒体类型\n     */\n    public static final int UNSUPPORTED_TYPE = 415;\n\n    /**\n     * 系统内部错误\n     */\n    public static final int ERROR = 500;\n\n    /**\n     * 接口未实现\n     */\n    public static final int NOT_IMPLEMENTED = 501;\n\n    /**\n     * 系统警告消息\n     */\n    public static final int WARN = 601;\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-constants/src/main/java/ginyi/common/constant/UserConstants.java",
    "content": "package ginyi.common.constant;\n\n/**\n * 用户常量信息\n *\n * @author ruoyi\n */\npublic class UserConstants {\n    /**\n     * 平台内系统用户的唯一标志\n     */\n    public static final String SYS_USER = \"SYS_USER\";\n\n    /**\n     * 当前线程上的用户\n     */\n    public static final String CURRENT_USER = \"CURRENT_USER\";\n\n    /**\n     * 正常状态\n     */\n    public static final String NORMAL = \"0\";\n\n    /**\n     * 异常状态\n     */\n    public static final String EXCEPTION = \"1\";\n\n    /**\n     * 用户封禁状态\n     */\n    public static final String USER_DISABLE = \"1\";\n\n    /**\n     * 角色封禁状态\n     */\n    public static final String ROLE_DISABLE = \"1\";\n\n    /**\n     * 部门正常状态\n     */\n    public static final String DEPT_NORMAL = \"0\";\n\n    /**\n     * 部门停用状态\n     */\n    public static final String DEPT_DISABLE = \"1\";\n\n    /**\n     * 字典正常状态\n     */\n    public static final String DICT_NORMAL = \"0\";\n\n    /**\n     * 是否为系统默认（是）\n     */\n    public static final String YES = \"Y\";\n\n    /**\n     * 是否菜单外链（是）\n     */\n    public static final String YES_FRAME = \"0\";\n\n    /**\n     * 是否菜单外链（否）\n     */\n    public static final String NO_FRAME = \"1\";\n\n    /**\n     * 菜单类型（目录）\n     */\n    public static final String TYPE_DIR = \"M\";\n\n    /**\n     * 菜单类型（菜单）\n     */\n    public static final String TYPE_MENU = \"C\";\n\n    /**\n     * 菜单类型（按钮）\n     */\n    public static final String TYPE_BUTTON = \"F\";\n\n    /**\n     * Layout组件标识\n     */\n    public final static String LAYOUT = \"Layout\";\n\n    /**\n     * ParentView组件标识\n     */\n    public final static String PARENT_VIEW = \"ParentView\";\n\n    /**\n     * InnerLink组件标识\n     */\n    public final static String INNER_LINK = \"InnerLink\";\n\n    /**\n     * 校验返回结果码\n     */\n    public final static String UNIQUE = \"0\";\n    public final static String NOT_UNIQUE = \"1\";\n\n    /**\n     * 用户名长度限制\n     */\n    public static final int USERNAME_MIN_LENGTH = 2;\n    public static final int USERNAME_MAX_LENGTH = 20;\n\n    /**\n     * 密码长度限制\n     */\n    public static final int PASSWORD_MIN_LENGTH = 5;\n    public static final int PASSWORD_MAX_LENGTH = 20;\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-enums/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>ginyi-common</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-common-enums</artifactId>\n\n    <properties>\n        <maven.compiler.source>8</maven.compiler.source>\n        <maven.compiler.target>8</maven.compiler.target>\n    </properties>\n\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-enums/src/main/java/ginyi/common/enums/BusinessStatus.java",
    "content": "package ginyi.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": "ginyi-springboot/ginyi-common/ginyi-common-enums/src/main/java/ginyi/common/enums/BusinessType.java",
    "content": "package ginyi.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": "ginyi-springboot/ginyi-common/ginyi-common-enums/src/main/java/ginyi/common/enums/HttpMethod.java",
    "content": "package ginyi.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": "ginyi-springboot/ginyi-common/ginyi-common-enums/src/main/java/ginyi/common/enums/OperatorType.java",
    "content": "package ginyi.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": "ginyi-springboot/ginyi-common/ginyi-common-exception/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>ginyi-common</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-common-exception</artifactId>\n\n    <dependencies>\n        <!-- 通用结果集 -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-common-result</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n\n        <!-- SpringBoot Web容器 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 通用的静态常量 -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-common-constants</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n\n    </dependencies>\n\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-exception/src/main/java/ginyi/common/exception/CommonException.java",
    "content": "package ginyi.common.exception;\n\nimport ginyi.common.result.StateCode;\nimport lombok.Data;\n\n/**\n * 通用的异常处理\n */\n@Data\npublic class CommonException extends RuntimeException{\n\n    private StateCode state;\n    private Object data;\n\n    public CommonException(StateCode state, Object data) {\n        this.state = state;\n        this.data = data;\n    }\n\n    public CommonException(StateCode state) {\n        this.state = state;\n        this.data = data;\n    }\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-exception/src/main/java/ginyi/common/exception/UnPermissionException.java",
    "content": "package ginyi.common.exception;\n\nimport ginyi.common.result.StateCode;\nimport lombok.Data;\n\n/**\n * 没有对应的接口权限\n */\n@Data\npublic class UnPermissionException extends RuntimeException{\n    private static final long serialVersionUID = 1L;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-exception/src/main/java/ginyi/common/exception/UserPasswordNotMatchException.java",
    "content": "package ginyi.common.exception;\n\n/**\n * 用户密码不正确或不符合规范异常类\n */\npublic class UserPasswordNotMatchException extends RuntimeException{\n    private static final long serialVersionUID = 1L;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-exception/src/main/java/ginyi/common/exception/UserPasswordRetryLimitExceedException.java",
    "content": "package ginyi.common.exception;\n\nimport ginyi.common.result.StateCode;\nimport lombok.Data;\n\n/**\n * 用户错误最大次数异常类\n */\n@Data\npublic class UserPasswordRetryLimitExceedException extends RuntimeException {\n\n    private static final long serialVersionUID = 1L;\n    private StateCode state;\n    private Object data;\n\n    public UserPasswordRetryLimitExceedException(StateCode state, Object data) {\n        this.state = state;\n        this.data = data;\n    }\n\n    public UserPasswordRetryLimitExceedException(StateCode state) {\n        this.state = state;\n        this.data = data;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-exception/src/main/java/ginyi/common/exception/handler/GlobalExceptionHandler.java",
    "content": "package ginyi.common.exception.handler;\n\nimport com.alibaba.fastjson2.JSON;\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.exception.CommonException;\nimport ginyi.common.exception.UnPermissionException;\nimport ginyi.common.exception.UserPasswordNotMatchException;\nimport ginyi.common.exception.UserPasswordRetryLimitExceedException;\nimport ginyi.common.result.CommonResult;\nimport ginyi.common.result.StateCode;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.validation.ObjectError;\nimport org.springframework.web.bind.MethodArgumentNotValidException;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestControllerAdvice;\nimport org.springframework.web.multipart.MaxUploadSizeExceededException;\nimport org.springframework.web.multipart.MultipartException;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\n\n/**\n * 全局异常处理类\n */\n@RestControllerAdvice\n@Slf4j\npublic class GlobalExceptionHandler {\n\n    /**\n     * 业务异常 ===>>> 处理数据异常处理\n     *\n     * @param e\n     * @return\n     */\n    @ExceptionHandler(CommonException.class)\n    @ResponseStatus(HttpStatus.OK)\n    public CommonResult BusinessExceptionHandler(CommonException e) {\n        if (e.getState() == StateCode.ERROR_SYSTEM) {\n            return CommonResult.error(e.getState(), CommonMessageConstants.SYS_ERROR);\n        }\n        return CommonResult.error(e.getState(), e.getData());\n    }\n\n    /**\n     * 参数校验 ===> 方法参数错误异常\n     *\n     * @param e\n     * @return\n     */\n    @ExceptionHandler(MethodArgumentNotValidException.class)\n    @ResponseStatus(HttpStatus.OK)\n    public CommonResult MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {\n        List<String> errorList = new ArrayList<>();\n        // 从异常对象中获取 ObjectError 对象\n        if (!e.getBindingResult().getAllErrors().isEmpty()) {\n            for (ObjectError error : e.getBindingResult().getAllErrors()) {\n                errorList.add(error.getDefaultMessage());\n            }\n        }\n        HashMap<String, Object> map = new HashMap<>();\n        map.put(\"errorMessageList\", errorList);\n        log.info(JSON.toJSONString(CommonResult.error(StateCode.ERROR_PARAMS, map)));\n        return CommonResult.error(StateCode.ERROR_PARAMS, map);\n    }\n\n    /**\n     * 参数校验 ===>>> 请求参数不合法\n     */\n    @ResponseStatus(HttpStatus.OK)\n    @ExceptionHandler(HttpMessageNotReadableException.class)\n    public CommonResult HttpMessageNotReadableExceptionHandler(HttpMessageNotReadableException e) {\n        return CommonResult.error(StateCode.ERROR_REQUEST_PARAMS, CommonMessageConstants.SYS_REQUEST_ILLEGAL);\n    }\n\n    /**\n     * 登录认证异常 ===> 用户名密码不匹配\n     */\n    @ResponseStatus(HttpStatus.OK)\n    @ExceptionHandler(UserPasswordNotMatchException.class)\n    public CommonResult UserPasswordNotMatchExceptionHandler(UserPasswordNotMatchException e) {\n        return CommonResult.error(StateCode.ERROR_UNAUTHENTICATION, CommonMessageConstants.USER_PASSWORD_NOT_MATCH);\n    }\n\n    /**\n     * 登录认证异常 ===> 用户登录失败次数超最大限制异常\n     */\n    @ResponseStatus(HttpStatus.OK)\n    @ExceptionHandler(UserPasswordRetryLimitExceedException.class)\n    public CommonResult UserPasswordRetryLimitExceedExceptionHandler(UserPasswordRetryLimitExceedException e) {\n        return CommonResult.error(e.getState(), e.getData());\n    }\n\n\n    /**\n     * 上传文件异常\n     *\n     * @param e\n     * @return\n     */\n    @ExceptionHandler(MultipartException.class)\n    @ResponseStatus(HttpStatus.OK)\n    public CommonResult MultipartExceptionHandler(MultipartException e) {\n        if (e instanceof MaxUploadSizeExceededException) {\n            return CommonResult.error(StateCode.ERROR_MULTIPART, CommonMessageConstants.UPLOAD_SIZE_EXCEED);\n        }\n        return CommonResult.error(StateCode.ERROR_MULTIPART, CommonMessageConstants.UPLOAD_FILE_ERROR);\n    }\n\n    /**\n     * 权限异常 ===>>>> 访问接口无权限\n     *\n     * @param e\n     * @return\n     */\n    @ExceptionHandler({UnPermissionException.class})\n    @ResponseStatus(HttpStatus.OK)\n    public CommonResult UnPermissionExceptionHandler(UnPermissionException e) {\n        return CommonResult.error(StateCode.ERROR_NOT_PERMISSION, CommonMessageConstants.SYS_ERROR);\n    }\n\n\n    /**\n     * 全局异常处理 ===> 处理其他所有未知异常\n     *\n     * @param e\n     * @return\n     */\n    @ExceptionHandler(Exception.class)\n    @ResponseStatus(HttpStatus.OK)\n    public CommonResult ExceptionHandler(Exception e) {\n        log.error(\"系统异常\", e);\n        return CommonResult.error(StateCode.ERROR_SYSTEM, CommonMessageConstants.SYS_ERROR);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-mysql/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>ginyi-common</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-common-mysql</artifactId>\n\n    <dependencies>\n        <!-- mysql -->\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n\n        <!-- druid -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid-spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- SpringBoot集成mybatis框架 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- mybatis-plus -->\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus-boot-starter</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-databind</artifactId>\n        </dependency>\n        <!-- 公用常量包 -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-common-constants</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n        <!-- 公用注解包 -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-common-annotation</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-mysql/src/main/java/ginyi/common/mysql/MyPage.java",
    "content": "package ginyi.common.mysql;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport lombok.Data;\n\n/**\n * 分页参数有传，根据分页查询，没传，查询所有\n */\n@Data\npublic class MyPage {\n\n    /**\n     * 当前页\n     */\n    private Long pageNum;\n    /**\n     * 每页条数\n     */\n    private Long pageSize;\n    /**\n     * 是否进行分页\n     */\n    private boolean isPage;\n\n    public MyPage() {\n    }\n\n    public MyPage(Long page, Long pageSize) {\n        if ((page != null && page > 0) && (pageSize != null && pageSize > 0)) {\n            this.pageNum = page;\n            this.pageSize = pageSize;\n            this.isPage = true;\n        }else {\n            this.pageNum = 1L;\n            this.pageSize = 10L;\n            this.isPage = false;\n        }\n    }\n\n    public Page getPage(){\n        return this.isPage ? new Page(this.pageNum, this.pageSize) : new Page().setSize(10000L);\n    }\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-mysql/src/main/java/ginyi/common/mysql/config/MyBatisPlusInterceptorConfig.java",
    "content": "package ginyi.common.mysql.config;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class MyBatisPlusInterceptorConfig {\n\n    @Bean\n    public MybatisPlusInterceptor myBatisPlusInterceptor() {\n        // 定义拦截器\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        // 分页拦截器\n        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));\n        return interceptor;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-mysql/src/main/java/ginyi/common/mysql/handler/JacksonArrayTypeHandler.java",
    "content": "package ginyi.common.mysql.handler;\n\nimport com.alibaba.fastjson.JSON;\nimport com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.List;\n\n@Slf4j\npublic class JacksonArrayTypeHandler extends AbstractJsonTypeHandler<List> {\n\n    private final Class<? extends Object> type;\n\n    private final ObjectMapper objectMapper;\n\n    public JacksonArrayTypeHandler(Class<?> type) {\n        objectMapper = new ObjectMapper();\n        this.type = type;\n    }\n\n\n    @SneakyThrows\n    @Override\n    protected List parse(String json) {\n        if (type.equals(List.class)) {\n            return objectMapper.readValue(json, List.class);\n        } else {\n            return JSON.parseArray(json, type);\n        }\n    }\n\n    @SneakyThrows\n    @Override\n    protected String toJson(List obj) {\n        return objectMapper.writeValueAsString(obj);\n    }\n}\n\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-mysql/src/main/java/ginyi/common/mysql/handler/MyMetaObjectHandler.java",
    "content": "package ginyi.common.mysql.handler;\n\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport ginyi.common.constant.UserConstants;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.springframework.stereotype.Component;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.Date;\n\n@Slf4j\n@Component\npublic class MyMetaObjectHandler implements MetaObjectHandler {\n\n    @Resource\n    private HttpServletRequest request;\n\n    /**\n     * 插入时的填充策略\n     *\n     * @param metaObject\n     */\n    @Override\n    public void insertFill(MetaObject metaObject) {\n        this.setFieldValByName(\"createBy\", request.getAttribute(UserConstants.CURRENT_USER), metaObject);\n        this.setFieldValByName(\"createTime\", new Date(), metaObject);\n        this.setFieldValByName(\"updateBy\", request.getAttribute(UserConstants.CURRENT_USER), metaObject);\n        this.setFieldValByName(\"updateTime\", new Date(), metaObject);\n    }\n\n    /**\n     * 更新时的填充策略\n     *\n     * @param metaObject\n     */\n    @Override\n    public void updateFill(MetaObject metaObject) {\n        this.setFieldValByName(\"updateBy\", request.getAttribute(UserConstants.CURRENT_USER), metaObject);\n        this.setFieldValByName(\"updateTime\", new Date(), metaObject);\n    }\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-mysql/src/main/java/ginyi/common/mysql/interceptor/MyInterceptor.java",
    "content": "package ginyi.common.mysql.interceptor;\n\nimport ginyi.common.annotation.CreateBy;\nimport ginyi.common.annotation.CreateTime;\nimport ginyi.common.annotation.UpdateBy;\nimport ginyi.common.annotation.UpdateTime;\nimport ginyi.common.constant.UserConstants;\nimport org.apache.ibatis.binding.MapperMethod;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.apache.ibatis.plugin.Intercepts;\nimport org.apache.ibatis.plugin.Invocation;\nimport org.apache.ibatis.plugin.Signature;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport java.lang.reflect.Field;\nimport java.util.Date;\nimport java.util.Set;\n\n/**\n * 自定义 Mybatis 插件，自动设置 createTime、createBy 和 updateTime、updateBy 的值。\n * 拦截 update 操作（添加和修改）\n */\n@Component\n@Intercepts({@Signature(\n        type = Executor.class,\n        method = \"update\",\n        args = {MappedStatement.class, Object.class}\n)})\npublic class MyInterceptor implements Interceptor {\n\n    @Resource\n    private HttpServletRequest request;\n\n    @Override\n    public Object intercept(Invocation invocation) throws Throwable {\n        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];\n\n        // 获取 SQL 命令\n        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();\n        //只判断新增和修改\n        if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {\n            // 获取参数\n            Object parameter = invocation.getArgs()[1];\n            //批量操作时\n            if (parameter instanceof MapperMethod.ParamMap) {\n                MapperMethod.ParamMap map = (MapperMethod.ParamMap) parameter;\n                Set keys = map.keySet();\n                for (Object key : keys) {\n                    if (map.get(key) != null) {\n                        setParameter(map.get(key), sqlCommandType);\n                    }\n                }\n            } else {\n                setParameter(parameter, sqlCommandType);\n            }\n        }\n        return invocation.proceed();\n    }\n\n    public void setParameter(Object parameter, SqlCommandType sqlCommandType) throws Throwable {\n        Class<?> aClass = parameter.getClass();\n        Field[] declaredFields = aClass.getSuperclass().getDeclaredFields();\n\n        for (Field field : declaredFields) {\n            if (SqlCommandType.INSERT.equals(sqlCommandType)) {\n                // insert 语句插入 createBy\n                if (field.getAnnotation(CreateBy.class) != null) {\n                    field.setAccessible(true);\n                    field.set(parameter, request.getAttribute(UserConstants.CURRENT_USER));\n                }\n\n                // insert 语句插入 createTime\n                if (field.getAnnotation(CreateTime.class) != null) {\n                    field.setAccessible(true);\n                    field.set(parameter, new Date());\n                }\n            }\n\n            if (SqlCommandType.UPDATE.equals(sqlCommandType)) {\n                // update 语句插入 updateBy\n                if (field.getAnnotation(UpdateBy.class) != null) {\n                    field.setAccessible(true);\n                    field.set(parameter, request.getAttribute(UserConstants.CURRENT_USER));\n                }\n                // update 语句插入 updateTime\n                if (field.getAnnotation(UpdateTime.class) != null) {\n                    field.setAccessible(true);\n                    field.set(parameter, new Date());\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-redis/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>ginyi-common</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-common-redis</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.data</groupId>\n            <artifactId>spring-data-redis</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>redis.clients</groupId>\n            <artifactId>jedis</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-pool2</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alibaba.fastjson2</groupId>\n            <artifactId>fastjson2</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-redis/src/main/java/ginyi/common/redis/cache/RedisCache.java",
    "content": "package ginyi.common.redis.cache;\n\nimport com.alibaba.fastjson.JSON;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.redis.connection.DataType;\nimport org.springframework.data.redis.core.BoundSetOperations;\nimport org.springframework.data.redis.core.HashOperations;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.stereotype.Component;\n\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * spring redis 工具类\n *\n * @author ruoyi\n **/\n@SuppressWarnings(value = {\"unchecked\", \"rawtypes\"})\n@Component\npublic class RedisCache {\n    @Autowired\n    public RedisTemplate redisTemplate;\n\n    /**\n     * 缓存基本的对象，Integer、String、实体类等，时间为30分钟\n     *\n     * @param key   缓存的键值\n     * @param value 缓存的值\n     */\n    public <T> void setCacheObject(final String key, final T value) {\n        redisTemplate.opsForValue().set(key, value, 30, TimeUnit.MINUTES);\n    }\n\n    /**\n     * 缓存基本的对象，Integer、String、实体类等\n     *\n     * @param key      缓存的键值\n     * @param value    缓存的值\n     * @param timeout  时间\n     * @param timeUnit 时间颗粒度\n     */\n    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {\n        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);\n    }\n\n    /**\n     * 设置有效时间\n     *\n     * @param key     Redis键\n     * @param timeout 超时时间\n     * @return true=设置成功；false=设置失败\n     */\n    public boolean expire(final String key, final long timeout) {\n        return expire(key, timeout, TimeUnit.SECONDS);\n    }\n\n    /**\n     * 设置有效时间\n     *\n     * @param key     Redis键\n     * @param timeout 超时时间\n     * @param unit    时间单位\n     * @return true=设置成功；false=设置失败\n     */\n    public boolean expire(final String key, final long timeout, final TimeUnit unit) {\n        return redisTemplate.expire(key, timeout, unit);\n    }\n\n    /**\n     * 获取有效时间\n     *\n     * @param key Redis键\n     * @return 有效时间\n     */\n    public long getExpire(final String key) {\n        return redisTemplate.getExpire(key);\n    }\n\n    /**\n     * 判断 key是否存在\n     *\n     * @param key 键\n     * @return true 存在 false不存在\n     */\n    public Boolean hasKey(String key) {\n        return redisTemplate.hasKey(key);\n    }\n\n    /**\n     * 获取数据类型\n     * @param key\n     * @return\n     */\n    public DataType getType(String key){\n        return redisTemplate.type(key);\n    }\n\n    /**\n     * 获得缓存的基本对象。\n     *\n     * @param key 缓存键值\n     * @return 缓存键值对应的数据\n     */\n    public <T> T getCacheObject(final String key, Class<T> clazz) {\n        Object o = redisTemplate.opsForValue().get(key);\n        if (o == null) {\n            return null;\n        }\n        return JSON.parseObject(JSON.toJSONString(o), clazz);\n    }\n\n    /**\n     * 删除单个对象\n     *\n     * @param key\n     */\n    public boolean removeCacheObject(final String key) {\n        Set<String> keys = redisTemplate.keys(key);\n        return redisTemplate.delete(keys) > 0;\n    }\n\n    /**\n     * 删除集合对象\n     *\n     * @param collection 多个对象\n     * @return\n     */\n    public boolean removeCacheObject(final Collection collection) {\n        return redisTemplate.delete(collection) > 0;\n    }\n\n    /**\n     * 获取指定前缀的所有key\n     *\n     * @param key\n     */\n    public Set<String> getKeys(final String key) {\n        return redisTemplate.keys(key);\n    }\n\n    /**\n     * 获取数据\n     * @param key\n     * @return\n     */\n    public Object getValue(String key){\n        return redisTemplate.opsForValue().get(key);\n    }\n\n    /**\n     * 缓存List数据\n     *\n     * @param key      缓存的键值\n     * @param dataList 待缓存的List数据\n     * @return 缓存的对象\n     */\n    public <T> long setCacheList(final String key, final List<T> dataList) {\n        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);\n        return count == null ? 0 : count;\n    }\n\n    /**\n     * 获得缓存的list对象\n     *\n     * @param key 缓存的键值\n     * @return 缓存键值对应的数据\n     */\n    public <T> List<T> getCacheList(final String key, Class<T> clazz) {\n        ArrayList<T> resultList = new ArrayList<>();\n        List list = redisTemplate.opsForList().range(key, 0, -1);\n        for (Object o : list) {\n            T t = JSON.parseObject(JSON.toJSONString(o), clazz);\n            resultList.add(t);\n        }\n        return resultList;\n    }\n\n    /**\n     * 缓存Set\n     *\n     * @param key     缓存键值\n     * @param dataSet 缓存的数据\n     * @return 缓存数据的对象\n     */\n    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {\n        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);\n        Iterator<T> it = dataSet.iterator();\n        while (it.hasNext()) {\n            setOperation.add(it.next());\n        }\n        return setOperation;\n    }\n\n    /**\n     * 获得缓存的set\n     *\n     * @param key\n     * @return\n     */\n    public <T> Set<T> getCacheSet(final String key) {\n        return redisTemplate.opsForSet().members(key);\n    }\n\n    /**\n     * 缓存Map\n     *\n     * @param key\n     * @param dataMap\n     */\n    public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {\n        if (dataMap != null) {\n            redisTemplate.opsForHash().putAll(key, dataMap);\n        }\n    }\n\n    /**\n     * 获得缓存的Map\n     *\n     * @param key\n     * @return\n     */\n    public <T> Map<String, T> getCacheMap(final String key) {\n        return redisTemplate.opsForHash().entries(key);\n    }\n\n    /**\n     * 往Hash中存入数据\n     *\n     * @param key   Redis键\n     * @param hKey  Hash键\n     * @param value 值\n     */\n    public <T> void setCacheMapValue(final String key, final String hKey, final T value) {\n        redisTemplate.opsForHash().put(key, hKey, value);\n    }\n\n    /**\n     * 获取Hash中的数据\n     *\n     * @param key  Redis键\n     * @param hKey Hash键\n     * @return Hash中的对象\n     */\n    public <T> T getCacheMapValue(final String key, final String hKey) {\n        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();\n        return opsForHash.get(key, hKey);\n    }\n\n    /**\n     * 获取多个Hash中的数据\n     *\n     * @param key   Redis键\n     * @param hKeys Hash键集合\n     * @return Hash对象集合\n     */\n    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {\n        return redisTemplate.opsForHash().multiGet(key, hKeys);\n    }\n\n    /**\n     * 删除Hash中的某条数据\n     *\n     * @param key  Redis键\n     * @param hKey Hash键\n     * @return 是否成功\n     */\n    public boolean deleteCacheMapValue(final String key, final String hKey) {\n        return redisTemplate.opsForHash().delete(key, hKey) > 0;\n    }\n\n    /**\n     * 获得缓存的基本对象列表\n     *\n     * @param pattern 字符串前缀\n     * @return 对象列表\n     */\n    public Collection<String> keys(final String pattern) {\n        return redisTemplate.keys(pattern);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-redis/src/main/java/ginyi/common/redis/config/RedisConfig.java",
    "content": "package ginyi.common.redis.config;\n\nimport com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.data.redis.RedisProperties;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.cache.annotation.CachingConfigurerSupport;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.core.RedisOperations;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.core.script.DefaultRedisScript;\nimport org.springframework.data.redis.serializer.StringRedisSerializer;\n\n@Configuration\n@ConditionalOnClass(RedisOperations.class)\n@EnableConfigurationProperties(RedisProperties.class)\npublic class RedisConfig extends CachingConfigurerSupport {\n\n    @Bean\n    @SuppressWarnings(value = {\"unchecked\", \"rawtypes\"})\n    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {\n        RedisTemplate<Object, Object> template = new RedisTemplate<>();\n\n        //使用fastjson序列化\n        FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);\n\n        // value值的序列化采用fastJsonRedisSerializer\n        template.setValueSerializer(fastJsonRedisSerializer);\n        template.setHashValueSerializer(fastJsonRedisSerializer);\n        // key的序列化采用StringRedisSerializer\n        template.setKeySerializer(new StringRedisSerializer());\n        template.setHashKeySerializer(new StringRedisSerializer());\n\n        template.setConnectionFactory(connectionFactory);\n        return template;\n    }\n\n    @Bean\n    public DefaultRedisScript<Long> limitScript() {\n        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();\n        redisScript.setScriptText(limitScriptText());\n        redisScript.setResultType(Long.class);\n        return redisScript;\n    }\n\n    /**\n     * 限流脚本\n     */\n    private String limitScriptText() {\n        return \"local key = KEYS[1]\\n\" +\n                \"local count = tonumber(ARGV[1])\\n\" +\n                \"local time = tonumber(ARGV[2])\\n\" +\n                \"local current = redis.call('get', key);\\n\" +\n                \"if current and tonumber(current) > count then\\n\" +\n                \"    return tonumber(current);\\n\" +\n                \"end\\n\" +\n                \"current = redis.call('incr', key)\\n\" +\n                \"if tonumber(current) == 1 then\\n\" +\n                \"    redis.call('expire', key, time)\\n\" +\n                \"end\\n\" +\n                \"return tonumber(current);\";\n    }\n}"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-result/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>ginyi-common</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-common-result</artifactId>\n\n    <dependencies>\n        <!-- swagger2接口文档 -->\n        <dependency>\n            <groupId>io.springfox</groupId>\n            <artifactId>springfox-swagger2</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-result/src/main/java/ginyi/common/result/CommonResult.java",
    "content": "package ginyi.common.result;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n@Data\n@ApiModel(value = \"结果类\")\npublic class CommonResult<T> implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"状态码\")\n    private Integer code;\n\n    @ApiModelProperty(value = \"状态信息\")\n    private String msg;\n\n    @ApiModelProperty(value = \"返回数据\")\n    private T data;\n\n    /**\n     * 返回格式\n     * @param state\n     * @param data\n     * @param <T>\n     * @return\n     */\n    private static <T> CommonResult<T> res(StateCode state, T data){\n        CommonResult<T> commonResult = new CommonResult<>();\n        commonResult.setCode(state.getCode());\n        commonResult.setMsg(state.getMessage());\n        commonResult.setData(data);\n        return commonResult;\n    }\n\n    /**\n     * 成功的返回，不需要 data\n     * @param <T>\n     * @return\n     */\n    public static <T> CommonResult<T> success(){\n        return res(StateCode.SUCCESS, null);\n    }\n\n    /**\n     * 成功的返回，需要 data\n     * @param data\n     * @param <T>\n     * @return\n     */\n    public static <T> CommonResult<T> success(T data){\n        return res(StateCode.SUCCESS, data);\n    }\n\n    /**\n     * 失败的返回，不需要 data\n     * @param state\n     * @return\n     */\n    public static <T> CommonResult<T> error(StateCode state){\n        return res(state, null);\n    }\n\n    /**\n     * 失败的返回，需要 data, 即错误信息\n     * @param state\n     * @param <T>\n     * @return\n     */\n    public static <T> CommonResult<T> error(StateCode state, T data){\n        return res(state, data);\n    }\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-result/src/main/java/ginyi/common/result/StateCode.java",
    "content": "package ginyi.common.result;\n\n\npublic enum StateCode {\n\n    SUCCESS(200, \"操作成功\"),\n\n    ERROR_SYSTEM(5000, \"系统异常\"),\n    ERROR_UNAUTHENTICATION(5001, \"登陆授权异常\"),\n    ERROR_TIMEOUT_REQUEST(5002, \"请求超时\"),\n    ERROR_TIMEOUT_TOKEN(5003, \"无效令牌\"),\n    ERROR_INVALID_SIGN(5004, \"无效签名\"),\n    ERROR_AUTHENTICATION_VALID(5005, \"认证失败或令牌已过期\"),\n    ERROR_NOT_PERMISSION(5006, \"权限不足\"),\n    ERROR_LIMITED(5007, \"操作受限\"),\n\n    ERROR_BUSINESS(6000,\"业务逻辑异常\"),\n    ERROR_PARAMS(6001,\"参数校验错误\"),\n    ERROR_REQUEST_PARAMS(6002,\"请求参数异常\"),\n    ERROR_PARAMS_SERVICE(6003,\"参数逻辑校验错误\"),\n    ERROR_DATA_FORMAT(6004, \"数据格式错误\"),\n    ERROR_EXIST(6005, \"数据已存在\"),\n    ERROR_NOT_EXIST(6006, \"数据不存在\"),\n\n    ERROR_NOT_SUPPORT(7000, \"不支持该文件类型\"),\n    ERROR_LIMIT_EXCEEDED(7001, \"超出请求次数上限\"),\n    ERROR_MULTIPART(7002, \"文件上传异常\"),\n    ;\n\n    private final int code;\n    private final String message;\n\n    StateCode(final int code, final String message) {\n        this.code = code;\n        this.message = message;\n    }\n\n    public int getCode(){\n        return this.code;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-swagger/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>ginyi-common</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-common-swagger</artifactId>\n\n    <dependencies>\n        <!-- swagger2接口文档 -->\n        <dependency>\n            <groupId>io.springfox</groupId>\n            <artifactId>springfox-swagger2</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.springfox</groupId>\n            <artifactId>springfox-swagger-ui</artifactId>\n        </dependency>\n\n        <!-- Knife4j -->\n        <dependency>\n            <groupId>com.github.xiaoymin</groupId>\n            <artifactId>knife4j-spring-boot-starter</artifactId>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-swagger/src/main/java/ginyi/common/swagger/AddGroup.java",
    "content": "package ginyi.common.swagger;\n\n/**\n * 此接口不要删除，用于参数校验的分组\n */\npublic interface AddGroup {\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-swagger/src/main/java/ginyi/common/swagger/Swagger2Config.java",
    "content": "package ginyi.common.swagger;\n\nimport com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport springfox.documentation.builders.ApiInfoBuilder;\nimport springfox.documentation.builders.PathSelectors;\nimport springfox.documentation.builders.RequestHandlerSelectors;\nimport springfox.documentation.service.ApiInfo;\nimport springfox.documentation.service.Contact;\nimport springfox.documentation.spi.DocumentationType;\nimport springfox.documentation.spring.web.plugins.Docket;\nimport springfox.documentation.swagger2.annotations.EnableSwagger2;\n\nimport static springfox.documentation.builders.RequestHandlerSelectors.basePackage;\n\n\npublic class Swagger2Config {\n\n    /**\n     * 标题\n     */\n    @Value(\"${swagger.swagger-title}\")\n    private String title;\n\n    /**\n     * 分组\n     */\n    @Value(\"${swagger.group-name}\")\n    private String groupName;\n\n    /**\n     * 描述\n     */\n    @Value(\"${swagger.swagger-description}\")\n    private String description;\n\n    /**\n     * URL\n     */\n    @Value(\"${swagger.swagger-url}\")\n    private String url;\n\n    /**\n     * 作者\n     */\n    @Value(\"${swagger.swagger-contact.name}\")\n    private String contactName;\n\n\n    /**\n     * 作者邮箱\n     */\n    @Value(\"${swagger.swagger-contact.email}\")\n    private String contactEmail;\n\n    /**\n     * 版本\n     */\n    @Value(\"${swagger.swagger-version}\")\n    private String version;\n\n    @Bean\n    public Docket createAdminApiDoc() {\n        return new Docket(DocumentationType.SWAGGER_2)\n                .groupName(groupName)\n                .apiInfo(apiInfo())\n                .select()\n                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))\n                .paths(PathSelectors.any())\n                .build();\n    }\n\n    // 构建 api 文档的详细信息函数\n    private ApiInfo apiInfo() {\n        return new ApiInfoBuilder()\n                .title(title)\n                .description(description)\n                .termsOfServiceUrl(url)\n                .contact(new Contact(contactName, null, contactEmail))\n                .version(version)\n                .build();\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-swagger/src/main/java/ginyi/common/swagger/UpdateGroup.java",
    "content": "package ginyi.common.swagger;\n\n/**\n * 此接口不要删除，用于参数校验的分组\n */\npublic interface UpdateGroup {\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/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>ginyi-common</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-common-utils</artifactId>\n\n    <properties>\n        <maven.compiler.source>8</maven.compiler.source>\n        <maven.compiler.target>8</maven.compiler.target>\n    </properties>\n\n    <dependencies>\n        <!-- SpringBoot Web容器 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!--常用工具类 -->\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n\n        <!-- Token生成与解析-->\n        <dependency>\n            <groupId>io.jsonwebtoken</groupId>\n            <artifactId>jjwt</artifactId>\n            <version>${jwt.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alibaba.fastjson2</groupId>\n            <artifactId>fastjson2</artifactId>\n        </dependency>\n\n        <!-- 邮箱 -->\n        <dependency>\n            <groupId>com.sun.mail</groupId>\n            <artifactId>javax.mail</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/Constants.java",
    "content": "package ginyi.common.utils;\n\n/**\n * 通用常量信息\n *\n * @author ruoyi\n */\npublic class Constants {\n    /**\n     * UTF-8 字符集\n     */\n    public static final String UTF8 = \"UTF-8\";\n\n    /**\n     * GBK 字符集\n     */\n    public static final String GBK = \"GBK\";\n\n    /**\n     * 通用成功标识\n     */\n    public static final String SUCCESS = \"0\";\n\n    /**\n     * 通用失败标识\n     */\n    public static final String FAIL = \"1\";\n\n    /**\n     * 登录成功\n     */\n    public static final String LOGIN_SUCCESS = \"Success\";\n\n    /**\n     * 注销\n     */\n    public static final String LOGOUT = \"Logout\";\n\n    /**\n     * 注册\n     */\n    public static final String REGISTER = \"Register\";\n\n    /**\n     * 登录失败\n     */\n    public static final String LOGIN_FAIL = \"Error\";\n\n\n    /**\n     * 令牌前缀\n     */\n    public static final String TOKEN_PREFIX = \"Bearer \";\n\n    /**\n     * 令牌前缀\n     */\n    public static final String LOGIN_USER_KEY = \"login_user_key\";\n\n    /**\n     * 资源映射路径 前缀\n     */\n    public static final String RESOURCE_PREFIX = \"/profile\";\n\n    /**\n     * 令牌\n     */\n    public static final String TOKEN = \"token\";\n\n\n    /**\n     * 验证码有效期（分钟）\n     */\n    public static final Integer CAPTCHA_EXPIRATION = 2;\n\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/DateUtils.java",
    "content": "package ginyi.common.utils;\n\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.Date;\n\n/**\n * 时间工具类\n *\n * @author ruoyi\n */\npublic class DateUtils extends org.apache.commons.lang3.time.DateUtils {\n    public static String YYYY = \"yyyy\";\n\n    public static String YYYY_MM = \"yyyy-MM\";\n\n    public static String YYYY_MM_DD = \"yyyy-MM-dd\";\n\n    public static String YYYYMMDDHHMMSS = \"yyyyMMddHHmmss\";\n\n    public static String YYYY_MM_DD_HH_MM_SS = \"yyyy-MM-dd HH:mm:ss\";\n\n    private static String[] parsePatterns = {\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 final String getTime() {\n        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);\n    }\n\n    public static final String dateTimeNow() {\n        return dateTimeNow(YYYYMMDDHHMMSS);\n    }\n\n    public static final String dateTimeNow(final String format) {\n        return parseDateToStr(format, new Date());\n    }\n\n    public static final String dateTime(final Date date) {\n        return parseDateToStr(YYYY_MM_DD, date);\n    }\n\n    public static final String parseDateToStr(final String format, final Date date) {\n        return new SimpleDateFormat(format).format(date);\n    }\n\n    public static final 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 final String datePath() {\n        Date now = new Date();\n        return DateFormatUtils.format(now, \"yyyy/MM/dd\");\n    }\n\n    /**\n     * 日期路径 即年/月/日 如20180808\n     */\n    public static final 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(), parsePatterns);\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"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/EmailUtils.java",
    "content": "package ginyi.common.utils;\n\nimport cn.hutool.extra.mail.MailUtil;\n\n/**\n * 邮箱工具类\n * 如何使用：\n *   1、需要在对应的应用服务模块下（如ginyi-server-admin）的resource目录下，新建config目录，再新建mail.setting配置文件\n *   2、具体配置信息参考 https://www.hutool.cn/docs/#/extra/邮件工具-MailUtil\n */\npublic class EmailUtils {\n\n    /**\n     * 单点发送\n     * @param to 目标\n     * @param subject 标题\n     * @param message 信息\n     * @param isHtml 是否为html信息\n     * @return\n     */\n    public static boolean sendToOne(String to, String subject, String message, Boolean isHtml) {\n        String result = MailUtil.send(to, subject, message, isHtml);\n        return result != null;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/LogUtils.java",
    "content": "package ginyi.common.utils;\n\n/**\n * 处理并记录日志文件\n *\n * @author ruoyi\n */\npublic class LogUtils {\n    public static String getBlock(Object msg) {\n        if (msg == null) {\n            msg = \"\";\n        }\n        return \"[\" + msg.toString() + \"]\";\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/LunarCalendarUtil.java",
    "content": "package ginyi.common.utils;\n\nimport java.util.Date;\nimport java.util.GregorianCalendar;\n\n/**\n * 工具类，实现阴阳历互转\n */\npublic class LunarCalendarUtil {\n    /**\n     * 支持转换的最小农历年份\n     */\n    public static final int MIN_YEAR = 1900;\n\n    /**\n     * 支持转换的最大农历年份\n     */\n    public static final int MAX_YEAR = 2099;\n\n    /**\n     * 公历每月前的天数\n     */\n    private static final int[] DAYS_BEFORE_MONTH = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};\n    /**\n     * 用来表示1900年到2099年间农历年份的相关信息，共24位bit的16进制表示，其中：\n     * 1. 前4位表示该年闰哪个月；\n     * 2. 5-17位表示农历年份13个月的大小月分布，0表示小，1表示大；\n     * 3. 最后7位表示农历年首（正月初一）对应的公历日期。\n     * 以2014年的数据0x955ABF为例说明：\n     * 1001 0101 0101 1010 1011 1111\n     * 闰九月   农历正月初一对应公历1月31号\n     */\n    private static final int[] LUNAR_INFO = {\n            /*1900*/\n            0x84B6BF,\n            /*1901-1910*/\n            0x04AE53, 0x0A5748, 0x5526BD, 0x0D2650, 0x0D9544, 0x46AAB9, 0x056A4D, 0x09AD42, 0x24AEB6, 0x04AE4A,\n            /*1911-1920*/\n            0x6A4DBE, 0x0A4D52, 0x0D2546, 0x5D52BA, 0x0B544E, 0x0D6A43, 0x296D37, 0x095B4B, 0x749BC1, 0x049754,\n            /*1921-1930*/\n            0x0A4B48, 0x5B25BC, 0x06A550, 0x06D445, 0x4ADAB8, 0x02B64D, 0x095742, 0x2497B7, 0x04974A, 0x664B3E,\n            /*1931-1940*/\n            0x0D4A51, 0x0EA546, 0x56D4BA, 0x05AD4E, 0x02B644, 0x393738, 0x092E4B, 0x7C96BF, 0x0C9553, 0x0D4A48,\n            /*1941-1950*/\n            0x6DA53B, 0x0B554F, 0x056A45, 0x4AADB9, 0x025D4D, 0x092D42, 0x2C95B6, 0x0A954A, 0x7B4ABD, 0x06CA51,\n            /*1951-1960*/\n            0x0B5546, 0x555ABB, 0x04DA4E, 0x0A5B43, 0x352BB8, 0x052B4C, 0x8A953F, 0x0E9552, 0x06AA48, 0x6AD53C,\n            /*1961-1970*/\n            0x0AB54F, 0x04B645, 0x4A5739, 0x0A574D, 0x052642, 0x3E9335, 0x0D9549, 0x75AABE, 0x056A51, 0x096D46,\n            /*1971-1980*/\n            0x54AEBB, 0x04AD4F, 0x0A4D43, 0x4D26B7, 0x0D254B, 0x8D52BF, 0x0B5452, 0x0B6A47, 0x696D3C, 0x095B50,\n            /*1981-1990*/\n            0x049B45, 0x4A4BB9, 0x0A4B4D, 0xAB25C2, 0x06A554, 0x06D449, 0x6ADA3D, 0x0AB651, 0x095746, 0x5497BB,\n            /*1991-2000*/\n            0x04974F, 0x064B44, 0x36A537, 0x0EA54A, 0x86B2BF, 0x05AC53, 0x0AB647, 0x5936BC, 0x092E50, 0x0C9645,\n            /*2001-2010*/\n            0x4D4AB8, 0x0D4A4C, 0x0DA541, 0x25AAB6, 0x056A49, 0x7AADBD, 0x025D52, 0x092D47, 0x5C95BA, 0x0A954E,\n            /*2011-2020*/\n            0x0B4A43, 0x4B5537, 0x0AD54A, 0x955ABF, 0x04BA53, 0x0A5B48, 0x652BBC, 0x052B50, 0x0A9345, 0x474AB9,\n            /*2021-2030*/\n            0x06AA4C, 0x0AD541, 0x24DAB6, 0x04B64A, 0x6a573D, 0x0A4E51, 0x0D2646, 0x5E933A, 0x0D534D, 0x05AA43,\n            /*2031-2040*/\n            0x36B537, 0x096D4B, 0xB4AEBF, 0x04AD53, 0x0A4D48, 0x6D25BC, 0x0D254F, 0x0D5244, 0x5DAA38, 0x0B5A4C,\n            /*2041-2050*/\n            0x056D41, 0x24ADB6, 0x049B4A, 0x7A4BBE, 0x0A4B51, 0x0AA546, 0x5B52BA, 0x06D24E, 0x0ADA42, 0x355B37,\n            /*2051-2060*/\n            0x09374B, 0x8497C1, 0x049753, 0x064B48, 0x66A53C, 0x0EA54F, 0x06AA44, 0x4AB638, 0x0AAE4C, 0x092E42,\n            /*2061-2070*/\n            0x3C9735, 0x0C9649, 0x7D4ABD, 0x0D4A51, 0x0DA545, 0x55AABA, 0x056A4E, 0x0A6D43, 0x452EB7, 0x052D4B,\n            /*2071-2080*/\n            0x8A95BF, 0x0A9553, 0x0B4A47, 0x6B553B, 0x0AD54F, 0x055A45, 0x4A5D38, 0x0A5B4C, 0x052B42, 0x3A93B6,\n            /*2081-2090*/\n            0x069349, 0x7729BD, 0x06AA51, 0x0AD546, 0x54DABA, 0x04B64E, 0x0A5743, 0x452738, 0x0D264A, 0x8E933E,\n            /*2091-2099*/\n            0x0D5252, 0x0DAA47, 0x66B53B, 0x056D4F, 0x04AE45, 0x4A4EB9, 0x0A4D4C, 0x0D1541, 0x2D92B5\n    };\n\n    /**\n     * 将农历日期转换为公历日期\n     *\n     * @param year        农历年份\n     * @param month       农历月\n     * @param monthDay    农历日\n     * @param isLeapMonth 该月是否是闰月(该参数可以根据本类中leapMonth()方法，先判断一下要查询的年份是否有闰月，并且闰的几月)\n     * @return 返回农历日期对应的公历日期，year0, month1, day2.\n     */\n    public static final int[] lunarToSolar(int year, int month, int monthDay, boolean isLeapMonth) {\n        int dayOffset;\n        int leapMonth;\n        int i;\n        if (year < MIN_YEAR || year > MAX_YEAR || month < 1 || month > 12\n                || monthDay < 1 || monthDay > 30) {\n            throw new IllegalArgumentException(\n                    \"Illegal lunar date, must be like that:\\n\\t\" +\n                            \"year : 1900~2099\\n\\t\" +\n                            \"month : 1~12\\n\\t\" +\n                            \"day : 1~30\");\n        }\n        dayOffset = (LUNAR_INFO[year - MIN_YEAR] & 0x001F) - 1;\n        if (((LUNAR_INFO[year - MIN_YEAR] & 0x0060) >> 5) == 2) {\n            dayOffset += 31;\n        }\n        for (i = 1; i < month; i++) {\n            if ((LUNAR_INFO[year - MIN_YEAR] & (0x80000 >> (i - 1))) == 0) {\n                dayOffset += 29;\n            } else {\n                dayOffset += 30;\n            }\n        }\n        dayOffset += monthDay;\n        leapMonth = (LUNAR_INFO[year - MIN_YEAR] & 0xf00000) >> 20;\n        // 这一年有闰月\n        if (leapMonth != 0) {\n            if (month > leapMonth || (month == leapMonth && isLeapMonth)) {\n                if ((LUNAR_INFO[year - MIN_YEAR] & (0x80000 >> (month - 1))) == 0) {\n                    dayOffset += 29;\n                } else {\n                    dayOffset += 30;\n                }\n            }\n        }\n        if (dayOffset > 366 || (year % 4 != 0 && dayOffset > 365)) {\n            year += 1;\n            if (year % 4 == 1) {\n                dayOffset -= 366;\n            } else {\n                dayOffset -= 365;\n            }\n        }\n        int[] solarInfo = new int[3];\n        for (i = 1; i < 13; i++) {\n            int iPos = DAYS_BEFORE_MONTH[i];\n            if (year % 4 == 0 && i > 2) {\n                iPos += 1;\n            }\n            if (year % 4 == 0 && i == 2 && iPos + 1 == dayOffset) {\n                solarInfo[1] = i;\n                solarInfo[2] = dayOffset - 31;\n                break;\n            }\n            if (iPos >= dayOffset) {\n                solarInfo[1] = i;\n                iPos = DAYS_BEFORE_MONTH[i - 1];\n                if (year % 4 == 0 && i > 2) {\n                    iPos += 1;\n                }\n                if (dayOffset > iPos) {\n                    solarInfo[2] = dayOffset - iPos;\n                } else if (dayOffset == iPos) {\n                    if (year % 4 == 0 && i == 2) {\n                        solarInfo[2] = DAYS_BEFORE_MONTH[i] - DAYS_BEFORE_MONTH[i - 1] + 1;\n                    } else {\n                        solarInfo[2] = DAYS_BEFORE_MONTH[i] - DAYS_BEFORE_MONTH[i - 1];\n                    }\n                } else {\n                    solarInfo[2] = dayOffset;\n                }\n                break;\n            }\n        }\n        solarInfo[0] = year;\n        return solarInfo;\n    }\n\n    /**\n     * 将公历日期转换为农历日期，且标识是否是闰月\n     * (如果当年有闰月，被闰的阴历月份所对应的阳历日期计算出来不对，后期自己根据本方法leap3 返回值判断如果为1，给月份+1，如果为0就是正确的，不用在操作，阴历闰月对应的阳历日期计算出来的阴历闰月日期正确，leap3为0)\n     *\n     * @param year\n     * @param month    传入需要为单数如 08 为8\n     * @param monthDay\n     * @return 返回公历日期对应的农历日期，year0，month1，day2，leap3(0 为计算正常  1 月份会比正确少一个月，需要月份+1)\n     */\n    public static final int[] solarToLunar(int year, int month, int monthDay) {\n        int[] lunarDate = new int[4];\n        Date baseDate = new GregorianCalendar(1900, 0, 31).getTime();\n        Date objDate = new GregorianCalendar(year, month - 1, monthDay).getTime();\n        int offset = (int) ((objDate.getTime() - baseDate.getTime()) / 86400000L);\n        // 用offset减去每农历年的天数计算当天是农历第几天\n        // iYear最终结果是农历的年份, offset是当年的第几天\n        int iYear, daysOfYear = 0;\n        for (iYear = MIN_YEAR; iYear <= MAX_YEAR && offset > 0; iYear++) {\n            daysOfYear = daysInLunarYear(iYear);\n            offset -= daysOfYear;\n        }\n        if (offset < 0) {\n            offset += daysOfYear;\n            iYear--;\n        }\n        // 农历年份\n        lunarDate[0] = iYear;\n        int leapMonth = leapMonth(iYear);\n        // 闰哪个月,1-12\n        boolean isLeap = false;\n        // 用当年的天数offset,逐个减去每月（农历）的天数，求出当天是本月的第几天\n        int iMonth, daysOfMonth = 0;\n        for (iMonth = 1; iMonth <= 13 && offset > 0; iMonth++) {\n            daysOfMonth = daysInLunarMonth(iYear, iMonth);\n            offset -= daysOfMonth;\n        }\n        // 当前月超过闰月，要校正\n        if (leapMonth != 0 && iMonth > leapMonth) {\n            --iMonth;\n            if (iMonth == leapMonth) {\n                isLeap = true;\n            }\n        }\n        // offset小于0时，也要校正\n        if (offset < 0) {\n            offset += daysOfMonth;\n            --iMonth;\n        }\n        lunarDate[1] = iMonth;\n        lunarDate[2] = offset + 1;\n        lunarDate[3] = isLeap ? 1 : 0;\n        return lunarDate;\n    }\n\n    /**\n     * 传回农历year年month月的总天数\n     *\n     * @param year  要计算的年份\n     * @param month 要计算的月\n     * @return 传回天数\n     */\n    final public static int daysInMonth(int year, int month) {\n        return daysInMonth(year, month, false);\n    }\n\n    /**\n     * 传回农历year年month月的总天数\n     *\n     * @param year  要计算的年份\n     * @param month 要计算的月\n     * @param leap  当月是否是闰月\n     * @return 传回天数，如果闰月是错误的，返回0.\n     */\n    public static final int daysInMonth(int year, int month, boolean leap) {\n        int leapMonth = leapMonth(year);\n        int offset = 0;\n        // 如果本年有闰月且month大于闰月时，需要校正\n        if (leapMonth != 0 && month > leapMonth) {\n            offset = 1;\n        }\n        // 不考虑闰月\n        if (!leap) {\n            return daysInLunarMonth(year, month + offset);\n        } else {\n            // 传入的闰月是正确的月份\n            if (leapMonth != 0 && leapMonth == month) {\n                return daysInLunarMonth(year, month + 1);\n            }\n        }\n        return 0;\n    }\n\n    /**\n     * 传回农历 year年的总天数\n     *\n     * @param year 将要计算的年份\n     * @return 返回传入年份的总天数\n     */\n    private static int daysInLunarYear(int year) {\n        int i, sum = 348;\n        if (leapMonth(year) != 0) {\n            sum = 377;\n        }\n        int monthInfo = LUNAR_INFO[year - MIN_YEAR] & 0x0FFF80;\n        for (i = 0x80000; i > 0x7; i >>= 1) {\n            if ((monthInfo & i) != 0) {\n                sum += 1;\n            }\n        }\n        return sum;\n    }\n\n    /**\n     * 传回农历 year年month月的总天数，总共有13个月包括闰月\n     *\n     * @param year  将要计算的年份\n     * @param month 将要计算的月份\n     * @return 传回农历 year年month月的总天数\n     */\n    public static int daysInLunarMonth(int year, int month) {\n        if ((LUNAR_INFO[year - MIN_YEAR] & (0x100000 >> month)) == 0) {\n            return 29;\n        } else {\n            return 30;\n        }\n    }\n\n    /**\n     * 传回农历 year年闰哪个月 1-12 , 没闰传回 0\n     *\n     * @param year 将要计算的年份\n     * @return 传回农历 year年闰哪个月1-12, 没闰传回 0\n     */\n    public static int leapMonth(int year) {\n        return ((LUNAR_INFO[year - MIN_YEAR] & 0xF00000)) >> 20;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/MessageUtils.java",
    "content": "package ginyi.common.utils;\n\nimport ginyi.common.utils.spring.SpringUtils;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.i18n.LocaleContextHolder;\n\n/**\n * 获取i18n资源文件\n *\n * @author ruoyi\n */\npublic class MessageUtils {\n    /**\n     * 根据消息键和参数 获取消息 委托给spring messageSource\n     *\n     * @param code 消息键\n     * @param args 参数\n     * @return 获取国际化翻译值\n     */\n    public static String message(String code, Object... args) {\n        MessageSource messageSource = SpringUtils.getBean(MessageSource.class);\n        return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/NumberUtils.java",
    "content": "package ginyi.common.utils;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\n\npublic class NumberUtils {\n\n    public static double round(double number, int decimalPlaces) {\n        BigDecimal bigDecimal = new BigDecimal(Double.toString(number));\n        bigDecimal = bigDecimal.setScale(decimalPlaces, RoundingMode.HALF_UP);\n        return bigDecimal.doubleValue();\n    }\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/ServletUtils.java",
    "content": "package ginyi.common.utils;\n\nimport ginyi.common.utils.text.Convert;\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.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 客户端工具类\n *\n * @author ruoyi\n */\npublic class ServletUtils {\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(), \",\"));\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(200);\n            response.setContentType(\"application/json\");\n            response.setCharacterEncoding(\"utf-8\");\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        String accept = request.getHeader(\"accept\");\n        if (accept != null && accept.contains(\"application/json\")) {\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.inStringIgnoreCase(uri, \".json\", \".xml\")) {\n            return true;\n        }\n\n        String ajax = request.getParameter(\"__ajax\");\n        return StringUtils.inStringIgnoreCase(ajax, \"json\", \"xml\");\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"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/StringUtils.java",
    "content": "package ginyi.common.utils;\n\nimport ginyi.common.utils.text.StrFormatter;\n\n/**\n * 字符串工具类\n *\n * @author ruoyi\n */\npublic class StringUtils extends org.apache.commons.lang3.StringUtils {\n\n    /**\n     * 空字符串\n     */\n    private static final String NULLSTR = \"\";\n\n\n    /**\n     * * 判断一个字符串是否为空串\n     *\n     * @param str String\n     * @return true：为空 false：非空\n     */\n    public static boolean isEmpty(String str) {\n        return isNull(str) || NULLSTR.equals(str.trim());\n    }\n\n    /**\n     * * 判断一个对象是否为空\n     *\n     * @param object Object\n     * @return true：为空 false：非空\n     */\n    public static boolean isNull(Object object) {\n        return object == null;\n    }\n\n    /**\n     * * 判断一个对象是否非空\n     *\n     * @param object Object\n     * @return true：非空 false：空\n     */\n    public static boolean isNotNull(Object object) {\n        return !isNull(object);\n    }\n\n    /**\n     * 获取参数不为空值\n     *\n     * @param value defaultValue 要判断的value\n     * @return value 返回值\n     */\n    public static <T> T nvl(T value, T defaultValue) {\n        return value != null ? value : defaultValue;\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        if (str != null && strs != null) {\n            for (String s : strs) {\n                if (str.equalsIgnoreCase(trim(s))) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 去空格\n     */\n    public static String trim(String str) {\n        return (str == null ? \"\" : str.trim());\n    }\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        if (isEmpty(params) || isEmpty(template)) {\n            return template;\n        }\n        return StrFormatter.format(template, params);\n    }\n\n    /**\n     * * 判断一个对象数组是否为空\n     *\n     * @param objects 要判断的对象数组\n     *                * @return true：为空 false：非空\n     */\n    public static boolean isEmpty(Object[] objects) {\n        return isNull(objects) || (objects.length == 0);\n    }\n\n    /**\n     * * 判断一个对象数组是否非空\n     *\n     * @param objects 要判断的对象数组\n     * @return true：非空 false：空\n     */\n    public static boolean isNotEmpty(Object[] objects) {\n        return !isEmpty(objects);\n    }\n}"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/Threads.java",
    "content": "package ginyi.common.utils;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.concurrent.*;\n\n/**\n * 线程相关工具类.\n *\n * @author ruoyi\n */\npublic class Threads {\n    private static final Logger logger = LoggerFactory.getLogger(Threads.class);\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                        logger.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            logger.error(t.getMessage(), t);\n        }\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/enums/UserStatus.java",
    "content": "package ginyi.common.utils.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": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/http/HttpHelper.java",
    "content": "package ginyi.common.utils.http;\n\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.servlet.ServletRequest;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * 通用http工具封装\n *\n * @author ruoyi\n */\npublic class HttpHelper {\n    private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class);\n\n    public static String getBodyString(ServletRequest request) {\n        StringBuilder sb = new StringBuilder();\n        BufferedReader reader = null;\n        try (InputStream inputStream = request.getInputStream()) {\n            reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));\n            String line = \"\";\n            while ((line = reader.readLine()) != null) {\n                sb.append(line);\n            }\n        } catch (IOException e) {\n            LOGGER.warn(\"getBodyString出现问题！\");\n        } finally {\n            if (reader != null) {\n                try {\n                    reader.close();\n                } catch (IOException e) {\n                    LOGGER.error(ExceptionUtils.getMessage(e));\n                }\n            }\n        }\n        return sb.toString();\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/http/HttpUtils.java",
    "content": "package ginyi.common.utils.http;\n\nimport ginyi.common.utils.Constants;\nimport ginyi.common.utils.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.net.ssl.*;\nimport java.io.*;\nimport java.net.ConnectException;\nimport java.net.SocketTimeoutException;\nimport java.net.URL;\nimport java.net.URLConnection;\nimport java.nio.charset.StandardCharsets;\nimport java.security.cert.X509Certificate;\n\n/**\n * 通用http发送方法\n *\n * @author ruoyi\n */\npublic class HttpUtils {\n    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);\n\n    /**\n     * 向指定 URL 发送GET方法的请求\n     *\n     * @param url 发送请求的 URL\n     * @return 所代表远程资源的响应结果\n     */\n    public static String sendGet(String url) {\n        return sendGet(url, StringUtils.EMPTY);\n    }\n\n    /**\n     * 向指定 URL 发送GET方法的请求\n     *\n     * @param url   发送请求的 URL\n     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。\n     * @return 所代表远程资源的响应结果\n     */\n    public static String sendGet(String url, String param) {\n        return sendGet(url, param, Constants.UTF8);\n    }\n\n    /**\n     * 向指定 URL 发送GET方法的请求\n     *\n     * @param url         发送请求的 URL\n     * @param param       请求参数，请求参数应该是 name1=value1&name2=value2 的形式。\n     * @param contentType 编码类型\n     * @return 所代表远程资源的响应结果\n     */\n    public static String sendGet(String url, String param, String contentType) {\n        StringBuilder result = new StringBuilder();\n        BufferedReader in = null;\n        try {\n            String urlNameString = StringUtils.isNotBlank(param) ? url + \"?\" + param : url;\n            log.info(\"sendGet - {}\", urlNameString);\n            URL realUrl = new URL(urlNameString);\n            URLConnection connection = realUrl.openConnection();\n            connection.setRequestProperty(\"accept\", \"*/*\");\n            connection.setRequestProperty(\"connection\", \"Keep-Alive\");\n            connection.setRequestProperty(\"user-agent\", \"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)\");\n            connection.connect();\n            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));\n            String line;\n            while ((line = in.readLine()) != null) {\n                result.append(line);\n            }\n            log.info(\"recv - {}\", result);\n        } catch (ConnectException e) {\n            log.error(\"调用HttpUtils.sendGet ConnectException, url=\" + url + \",param=\" + param, e);\n        } catch (SocketTimeoutException e) {\n            log.error(\"调用HttpUtils.sendGet SocketTimeoutException, url=\" + url + \",param=\" + param, e);\n        } catch (IOException e) {\n            log.error(\"调用HttpUtils.sendGet IOException, url=\" + url + \",param=\" + param, e);\n        } catch (Exception e) {\n            log.error(\"调用HttpsUtil.sendGet Exception, url=\" + url + \",param=\" + param, e);\n        } finally {\n            try {\n                if (in != null) {\n                    in.close();\n                }\n            } catch (Exception ex) {\n                log.error(\"调用in.close Exception, url=\" + url + \",param=\" + param, ex);\n            }\n        }\n        return result.toString();\n    }\n\n    /**\n     * 向指定 URL 发送POST方法的请求\n     *\n     * @param url   发送请求的 URL\n     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。\n     * @return 所代表远程资源的响应结果\n     */\n    public static String sendPost(String url, String param) {\n        PrintWriter out = null;\n        BufferedReader in = null;\n        StringBuilder result = new StringBuilder();\n        try {\n            log.info(\"sendPost - {}\", url);\n            URL realUrl = new URL(url);\n            URLConnection conn = realUrl.openConnection();\n            conn.setRequestProperty(\"accept\", \"*/*\");\n            conn.setRequestProperty(\"connection\", \"Keep-Alive\");\n            conn.setRequestProperty(\"user-agent\", \"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)\");\n            conn.setRequestProperty(\"Accept-Charset\", \"utf-8\");\n            conn.setRequestProperty(\"contentType\", \"utf-8\");\n            conn.setDoOutput(true);\n            conn.setDoInput(true);\n            out = new PrintWriter(conn.getOutputStream());\n            out.print(param);\n            out.flush();\n            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));\n            String line;\n            while ((line = in.readLine()) != null) {\n                result.append(line);\n            }\n            log.info(\"recv - {}\", result);\n        } catch (ConnectException e) {\n            log.error(\"调用HttpUtils.sendPost ConnectException, url=\" + url + \",param=\" + param, e);\n        } catch (SocketTimeoutException e) {\n            log.error(\"调用HttpUtils.sendPost SocketTimeoutException, url=\" + url + \",param=\" + param, e);\n        } catch (IOException e) {\n            log.error(\"调用HttpUtils.sendPost IOException, url=\" + url + \",param=\" + param, e);\n        } catch (Exception e) {\n            log.error(\"调用HttpsUtil.sendPost Exception, url=\" + url + \",param=\" + param, e);\n        } finally {\n            try {\n                if (out != null) {\n                    out.close();\n                }\n                if (in != null) {\n                    in.close();\n                }\n            } catch (IOException ex) {\n                log.error(\"调用in.close Exception, url=\" + url + \",param=\" + param, ex);\n            }\n        }\n        return result.toString();\n    }\n\n    public static String sendSSLPost(String url, String param) {\n        StringBuilder result = new StringBuilder();\n        String urlNameString = url + \"?\" + param;\n        try {\n            log.info(\"sendSSLPost - {}\", urlNameString);\n            SSLContext sc = SSLContext.getInstance(\"SSL\");\n            sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());\n            URL console = new URL(urlNameString);\n            HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();\n            conn.setRequestProperty(\"accept\", \"*/*\");\n            conn.setRequestProperty(\"connection\", \"Keep-Alive\");\n            conn.setRequestProperty(\"user-agent\", \"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)\");\n            conn.setRequestProperty(\"Accept-Charset\", \"utf-8\");\n            conn.setRequestProperty(\"contentType\", \"utf-8\");\n            conn.setDoOutput(true);\n            conn.setDoInput(true);\n\n            conn.setSSLSocketFactory(sc.getSocketFactory());\n            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());\n            conn.connect();\n            InputStream is = conn.getInputStream();\n            BufferedReader br = new BufferedReader(new InputStreamReader(is));\n            String ret = \"\";\n            while ((ret = br.readLine()) != null) {\n                if (ret != null && !\"\".equals(ret.trim())) {\n                    result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));\n                }\n            }\n            log.info(\"recv - {}\", result);\n            conn.disconnect();\n            br.close();\n        } catch (ConnectException e) {\n            log.error(\"调用HttpUtils.sendSSLPost ConnectException, url=\" + url + \",param=\" + param, e);\n        } catch (SocketTimeoutException e) {\n            log.error(\"调用HttpUtils.sendSSLPost SocketTimeoutException, url=\" + url + \",param=\" + param, e);\n        } catch (IOException e) {\n            log.error(\"调用HttpUtils.sendSSLPost IOException, url=\" + url + \",param=\" + param, e);\n        } catch (Exception e) {\n            log.error(\"调用HttpsUtil.sendSSLPost Exception, url=\" + url + \",param=\" + param, e);\n        }\n        return result.toString();\n    }\n\n    private static class TrustAnyTrustManager implements X509TrustManager {\n        @Override\n        public void checkClientTrusted(X509Certificate[] chain, String authType) {\n        }\n\n        @Override\n        public void checkServerTrusted(X509Certificate[] chain, String authType) {\n        }\n\n        @Override\n        public X509Certificate[] getAcceptedIssuers() {\n            return new X509Certificate[]{};\n        }\n    }\n\n    private static class TrustAnyHostnameVerifier implements HostnameVerifier {\n        @Override\n        public boolean verify(String hostname, SSLSession session) {\n            return true;\n        }\n    }\n}"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/ip/AddressUtils.java",
    "content": "package ginyi.common.utils.ip;\n\nimport com.alibaba.fastjson2.JSON;\nimport com.alibaba.fastjson2.JSONObject;\nimport ginyi.common.utils.Constants;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.common.utils.http.HttpUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 获取地址类\n *\n * @author ruoyi\n */\npublic class AddressUtils {\n    private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);\n\n    // IP地址查询\n    public static final String IP_URL = \"http://whois.pconline.com.cn/ipJson.jsp\";\n\n    // 未知地址\n    public static final String UNKNOWN = \"XX XX\";\n\n    public static String getRealAddressByIP(String ip) {\n        // 内网不查询\n        if (IpUtils.internalIp(ip)) {\n            return \"内网IP\";\n        }\n        try {\n            String rspStr = HttpUtils.sendGet(IP_URL, \"ip=\" + ip + \"&json=true\", Constants.GBK);\n            if (StringUtils.isEmpty(rspStr)) {\n                log.error(\"获取地理位置异常 {}\", ip);\n                return UNKNOWN;\n            }\n            JSONObject obj = JSON.parseObject(rspStr);\n            String region = obj.getString(\"pro\");\n            String city = obj.getString(\"city\");\n            return String.format(\"%s %s\", region, city);\n        } catch (Exception e) {\n            log.error(\"获取地理位置异常 {}\", ip);\n        }\n        return UNKNOWN;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/ip/IpUtils.java",
    "content": "package ginyi.common.utils.ip;\n\nimport ginyi.common.utils.StringUtils;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\n\n/**\n * 获取IP方法\n *\n * @author ruoyi\n */\npublic class IpUtils {\n    /**\n     * 获取客户端IP\n     *\n     * @param request 请求对象\n     * @return IP地址\n     */\n    public static String getIpAddr(HttpServletRequest request) {\n        if (request == null) {\n            return \"unknown\";\n        }\n        String ip = request.getHeader(\"x-forwarded-for\");\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getHeader(\"Proxy-Client-IP\");\n        }\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getHeader(\"X-Forwarded-For\");\n        }\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getHeader(\"WL-Proxy-Client-IP\");\n        }\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getHeader(\"X-Real-IP\");\n        }\n\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getRemoteAddr();\n        }\n\n        return \"0:0:0:0:0:0:0:1\".equals(ip) ? \"127.0.0.1\" : getMultistageReverseProxyIp(ip);\n    }\n\n    /**\n     * 检查是否为内部IP地址\n     *\n     * @param ip IP地址\n     * @return 结果\n     */\n    public static boolean internalIp(String ip) {\n        byte[] addr = textToNumericFormatV4(ip);\n        return internalIp(addr) || \"127.0.0.1\".equals(ip);\n    }\n\n    /**\n     * 检查是否为内部IP地址\n     *\n     * @param addr byte地址\n     * @return 结果\n     */\n    private static boolean internalIp(byte[] addr) {\n        if (StringUtils.isNull(addr) || addr.length < 2) {\n            return true;\n        }\n        final byte b0 = addr[0];\n        final byte b1 = addr[1];\n        // 10.x.x.x/8\n        final byte SECTION_1 = 0x0A;\n        // 172.16.x.x/12\n        final byte SECTION_2 = (byte) 0xAC;\n        final byte SECTION_3 = (byte) 0x10;\n        final byte SECTION_4 = (byte) 0x1F;\n        // 192.168.x.x/16\n        final byte SECTION_5 = (byte) 0xC0;\n        final byte SECTION_6 = (byte) 0xA8;\n        switch (b0) {\n            case SECTION_1:\n                return true;\n            case SECTION_2:\n                if (b1 >= SECTION_3 && b1 <= SECTION_4) {\n                    return true;\n                }\n            case SECTION_5:\n                switch (b1) {\n                    case SECTION_6:\n                        return true;\n                }\n            default:\n                return false;\n        }\n    }\n\n    /**\n     * 将IPv4地址转换成字节\n     *\n     * @param text IPv4地址\n     * @return byte 字节\n     */\n    public static byte[] textToNumericFormatV4(String text) {\n        if (text.length() == 0) {\n            return null;\n        }\n\n        byte[] bytes = new byte[4];\n        String[] elements = text.split(\"\\\\.\", -1);\n        try {\n            long l;\n            int i;\n            switch (elements.length) {\n                case 1:\n                    l = Long.parseLong(elements[0]);\n                    if ((l < 0L) || (l > 4294967295L)) {\n                        return null;\n                    }\n                    bytes[0] = (byte) (int) (l >> 24 & 0xFF);\n                    bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);\n                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);\n                    bytes[3] = (byte) (int) (l & 0xFF);\n                    break;\n                case 2:\n                    l = Integer.parseInt(elements[0]);\n                    if ((l < 0L) || (l > 255L)) {\n                        return null;\n                    }\n                    bytes[0] = (byte) (int) (l & 0xFF);\n                    l = Integer.parseInt(elements[1]);\n                    if ((l < 0L) || (l > 16777215L)) {\n                        return null;\n                    }\n                    bytes[1] = (byte) (int) (l >> 16 & 0xFF);\n                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);\n                    bytes[3] = (byte) (int) (l & 0xFF);\n                    break;\n                case 3:\n                    for (i = 0; i < 2; ++i) {\n                        l = Integer.parseInt(elements[i]);\n                        if ((l < 0L) || (l > 255L)) {\n                            return null;\n                        }\n                        bytes[i] = (byte) (int) (l & 0xFF);\n                    }\n                    l = Integer.parseInt(elements[2]);\n                    if ((l < 0L) || (l > 65535L)) {\n                        return null;\n                    }\n                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);\n                    bytes[3] = (byte) (int) (l & 0xFF);\n                    break;\n                case 4:\n                    for (i = 0; i < 4; ++i) {\n                        l = Integer.parseInt(elements[i]);\n                        if ((l < 0L) || (l > 255L)) {\n                            return null;\n                        }\n                        bytes[i] = (byte) (int) (l & 0xFF);\n                    }\n                    break;\n                default:\n                    return null;\n            }\n        } catch (NumberFormatException e) {\n            return null;\n        }\n        return bytes;\n    }\n\n    /**\n     * 获取IP地址\n     *\n     * @return 本地IP地址\n     */\n    public static String getHostIp() {\n        try {\n            return InetAddress.getLocalHost().getHostAddress();\n        } catch (UnknownHostException e) {\n        }\n        return \"127.0.0.1\";\n    }\n\n    /**\n     * 获取主机名\n     *\n     * @return 本地主机名\n     */\n    public static String getHostName() {\n        try {\n            return InetAddress.getLocalHost().getHostName();\n        } catch (UnknownHostException e) {\n        }\n        return \"未知\";\n    }\n\n    /**\n     * 从多级反向代理中获得第一个非unknown IP地址\n     *\n     * @param ip 获得的IP地址\n     * @return 第一个非unknown IP地址\n     */\n    public static String getMultistageReverseProxyIp(String ip) {\n        // 多级反向代理检测\n        if (ip != null && ip.indexOf(\",\") > 0) {\n            final String[] ips = ip.trim().split(\",\");\n            for (String subIp : ips) {\n                if (false == isUnknown(subIp)) {\n                    ip = subIp;\n                    break;\n                }\n            }\n        }\n        return ip;\n    }\n\n    /**\n     * 检测给定字符串是否为未知，多用于检测HTTP请求相关\n     *\n     * @param checkString 被检测的字符串\n     * @return 是否未知\n     */\n    public static boolean isUnknown(String checkString) {\n        return StringUtils.isBlank(checkString) || \"unknown\".equalsIgnoreCase(checkString);\n    }\n}"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/spring/SpringUtils.java",
    "content": "package ginyi.common.utils.spring;\n\nimport ginyi.common.utils.StringUtils;\nimport org.springframework.aop.framework.AopContext;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.beans.factory.config.BeanFactoryPostProcessor;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.stereotype.Component;\n\n/**\n * spring工具类 方便在非spring管理环境中获取bean\n *\n * @author ruoyi\n */\n@Component\npublic final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware {\n\n    /**\n     * Spring应用上下文环境\n     */\n    private static ConfigurableListableBeanFactory beanFactory;\n\n    private static ApplicationContext applicationContext;\n\n    @Override\n    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {\n        SpringUtils.beanFactory = beanFactory;\n    }\n\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        SpringUtils.applicationContext = applicationContext;\n    }\n\n    /**\n     * 获取对象\n     *\n     * @param name\n     * @return Object 一个以所给名字注册的bean的实例\n     * @throws org.springframework.beans.BeansException\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T getBean(String name) throws BeansException {\n        return (T) beanFactory.getBean(name);\n    }\n\n    /**\n     * 获取类型为requiredType的对象\n     *\n     * @param clz\n     * @return\n     * @throws org.springframework.beans.BeansException\n     */\n    public static <T> T getBean(Class<T> clz) throws BeansException {\n        T result = (T) beanFactory.getBean(clz);\n        return result;\n    }\n\n    /**\n     * 如果BeanFactory包含一个与所给名称匹配的bean定义，则返回true\n     *\n     * @param name\n     * @return boolean\n     */\n    public static boolean containsBean(String name) {\n        return beanFactory.containsBean(name);\n    }\n\n    /**\n     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到，将会抛出一个异常（NoSuchBeanDefinitionException）\n     *\n     * @param name\n     * @return boolean\n     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException\n     */\n    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {\n        return beanFactory.isSingleton(name);\n    }\n\n    /**\n     * @param name\n     * @return Class 注册对象的类型\n     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException\n     */\n    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {\n        return beanFactory.getType(name);\n    }\n\n    /**\n     * 如果给定的bean名字在bean定义中有别名，则返回这些别名\n     *\n     * @param name\n     * @return\n     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException\n     */\n    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {\n        return beanFactory.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     * 获取当前的环境配置，无配置返回null\n     *\n     * @return 当前的环境配置\n     */\n    public static String[] getActiveProfiles() {\n        return applicationContext.getEnvironment().getActiveProfiles();\n    }\n\n    /**\n     * 获取当前的环境配置，当有多个环境配置时，只获取第一个\n     *\n     * @return 当前的环境配置\n     */\n    public static String getActiveProfile() {\n        final String[] activeProfiles = getActiveProfiles();\n        return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;\n    }\n\n    /**\n     * 获取配置文件中的值\n     *\n     * @param key 配置文件的key\n     * @return 当前的配置文件的值\n     */\n    public static String getRequiredProperty(String key) {\n        return applicationContext.getEnvironment().getRequiredProperty(key);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/text/CharsetKit.java",
    "content": "package ginyi.common.utils.text;\n\nimport ginyi.common.utils.StringUtils;\n\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * 字符集工具类\n *\n * @author ruoyi\n */\npublic class CharsetKit {\n    /**\n     * ISO-8859-1\n     */\n    public static final String ISO_8859_1 = \"ISO-8859-1\";\n    /**\n     * UTF-8\n     */\n    public static final String UTF_8 = \"UTF-8\";\n    /**\n     * GBK\n     */\n    public static final String GBK = \"GBK\";\n\n    /**\n     * ISO-8859-1\n     */\n    public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);\n    /**\n     * UTF-8\n     */\n    public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);\n    /**\n     * GBK\n     */\n    public static final Charset CHARSET_GBK = Charset.forName(GBK);\n\n    /**\n     * 转换为Charset对象\n     *\n     * @param charset 字符集，为空则返回默认字符集\n     * @return Charset\n     */\n    public static Charset charset(String charset) {\n        return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);\n    }\n\n    /**\n     * 转换字符串的字符集编码\n     *\n     * @param source      字符串\n     * @param srcCharset  源字符集，默认ISO-8859-1\n     * @param destCharset 目标字符集，默认UTF-8\n     * @return 转换后的字符集\n     */\n    public static String convert(String source, String srcCharset, String destCharset) {\n        return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));\n    }\n\n    /**\n     * 转换字符串的字符集编码\n     *\n     * @param source      字符串\n     * @param srcCharset  源字符集，默认ISO-8859-1\n     * @param destCharset 目标字符集，默认UTF-8\n     * @return 转换后的字符集\n     */\n    public static String convert(String source, Charset srcCharset, Charset destCharset) {\n        if (null == srcCharset) {\n            srcCharset = StandardCharsets.ISO_8859_1;\n        }\n\n        if (null == destCharset) {\n            destCharset = StandardCharsets.UTF_8;\n        }\n\n        if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) {\n            return source;\n        }\n        return new String(source.getBytes(srcCharset), destCharset);\n    }\n\n    /**\n     * @return 系统字符集编码\n     */\n    public static String systemCharset() {\n        return Charset.defaultCharset().name();\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/text/Convert.java",
    "content": "package ginyi.common.utils.text;\n\nimport ginyi.common.utils.StringUtils;\nimport org.apache.commons.lang3.ArrayUtils;\n\nimport java.nio.ByteBuffer;\nimport java.nio.charset.Charset;\n\npublic class Convert {\n\n    /**\n     * 转换为字符串<br>\n     * 如果给定的值为null，或者转换失败，返回默认值<br>\n     * 转换失败不会报错\n     *\n     * @param value        被转换的值\n     * @param defaultValue 转换错误时的默认值\n     * @return 结果\n     */\n    public static String toStr(Object value, String defaultValue) {\n        if (null == value) {\n            return defaultValue;\n        }\n        if (value instanceof String) {\n            return (String) value;\n        }\n        return value.toString();\n    }\n\n    /**\n     * 转换为字符串<br>\n     * 如果给定的值为<code>null</code>，或者转换失败，返回默认值<code>null</code><br>\n     * 转换失败不会报错\n     *\n     * @param value 被转换的值\n     * @return 结果\n     */\n    public static String toStr(Object value) {\n        return toStr(value, null);\n    }\n\n\n    /**\n     * 转换为boolean<br>\n     * 如果给定的值为空，或者转换失败，返回默认值<code>null</code><br>\n     * 转换失败不会报错\n     *\n     * @param value 被转换的值\n     * @return 结果\n     */\n    public static Boolean toBool(Object value) {\n        return toBool(value, null);\n    }\n\n    /**\n     * 转换为boolean<br>\n     * String支持的值为：true、false、yes、ok、no，1,0 如果给定的值为空，或者转换失败，返回默认值<br>\n     * 转换失败不会报错\n     *\n     * @param value        被转换的值\n     * @param defaultValue 转换错误时的默认值\n     * @return 结果\n     */\n    public static Boolean toBool(Object value, Boolean defaultValue) {\n        if (value == null) {\n            return defaultValue;\n        }\n        if (value instanceof Boolean) {\n            return (Boolean) value;\n        }\n        String valueStr = toStr(value, null);\n        if (StringUtils.isEmpty(valueStr)) {\n            return defaultValue;\n        }\n        valueStr = valueStr.trim().toLowerCase();\n        switch (valueStr) {\n            case \"true\":\n            case \"yes\":\n            case \"ok\":\n            case \"1\":\n                return true;\n            case \"false\":\n            case \"no\":\n            case \"0\":\n                return false;\n            default:\n                return defaultValue;\n        }\n    }\n\n    /**\n     * 转换为int<br>\n     * 如果给定的值为<code>null</code>，或者转换失败，返回默认值<code>null</code><br>\n     * 转换失败不会报错\n     *\n     * @param value 被转换的值\n     * @return 结果\n     */\n    public static Integer toInt(Object value) {\n        return toInt(value, null);\n    }\n\n    /**\n     * 转换为int<br>\n     * 如果给定的值为空，或者转换失败，返回默认值<br>\n     * 转换失败不会报错\n     *\n     * @param value        被转换的值\n     * @param defaultValue 转换错误时的默认值\n     * @return 结果\n     */\n    public static Integer toInt(Object value, Integer defaultValue) {\n        if (value == null) {\n            return defaultValue;\n        }\n        if (value instanceof Integer) {\n            return (Integer) value;\n        }\n        if (value instanceof Number) {\n            return ((Number) value).intValue();\n        }\n        final String valueStr = toStr(value, null);\n        if (StringUtils.isEmpty(valueStr)) {\n            return defaultValue;\n        }\n        try {\n            return Integer.parseInt(valueStr.trim());\n        } catch (Exception e) {\n            return defaultValue;\n        }\n    }\n\n    /**\n     * 将对象转为字符串<br>\n     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法\n     *\n     * @param obj 对象\n     * @return 字符串\n     */\n    public static String utf8Str(Object obj) {\n        return str(obj, CharsetKit.CHARSET_UTF_8);\n    }\n\n    /**\n     * 将对象转为字符串<br>\n     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法\n     *\n     * @param obj     对象\n     * @param charset 字符集\n     * @return 字符串\n     */\n    public static String str(Object obj, Charset charset) {\n        if (null == obj) {\n            return null;\n        }\n\n        if (obj instanceof String) {\n            return (String) obj;\n        } else if (obj instanceof byte[]) {\n            return str((byte[]) obj, charset);\n        } else if (obj instanceof Byte[]) {\n            byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj);\n            return str(bytes, charset);\n        } else if (obj instanceof ByteBuffer) {\n            return str((ByteBuffer) obj, charset);\n        }\n        return obj.toString();\n    }\n\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/text/StrFormatter.java",
    "content": "package ginyi.common.utils.text;\n\nimport ginyi.common.utils.StringUtils;\n\n/**\n * 字符串格式化\n *\n * @author ruoyi\n */\npublic class StrFormatter {\n    public static final String EMPTY_JSON = \"{}\";\n    public static final char C_BACKSLASH = '\\\\';\n    public static final char C_DELIM_START = '{';\n    public static final char C_DELIM_END = '}';\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 strPattern 字符串模板\n     * @param argArray   参数列表\n     * @return 结果\n     */\n    public static String format(final String strPattern, final Object... argArray) {\n        if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) {\n            return strPattern;\n        }\n        final int strPatternLength = strPattern.length();\n\n        // 初始化定义好的长度以获得更好的性能\n        StringBuilder sbuf = new StringBuilder(strPatternLength + 50);\n\n        int handledPosition = 0;\n        int delimIndex;// 占位符所在位置\n        for (int argIndex = 0; argIndex < argArray.length; argIndex++) {\n            delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);\n            if (delimIndex == -1) {\n                if (handledPosition == 0) {\n                    return strPattern;\n                } else { // 字符串模板剩余部分不再包含占位符，加入剩余部分后返回结果\n                    sbuf.append(strPattern, handledPosition, strPatternLength);\n                    return sbuf.toString();\n                }\n            } else {\n                if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) {\n                    if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) {\n                        // 转义符之前还有一个转义符，占位符依旧有效\n                        sbuf.append(strPattern, handledPosition, delimIndex - 1);\n                        sbuf.append(Convert.utf8Str(argArray[argIndex]));\n                        handledPosition = delimIndex + 2;\n                    } else {\n                        // 占位符被转义\n                        argIndex--;\n                        sbuf.append(strPattern, handledPosition, delimIndex - 1);\n                        sbuf.append(C_DELIM_START);\n                        handledPosition = delimIndex + 1;\n                    }\n                } else {\n                    // 正常占位符\n                    sbuf.append(strPattern, handledPosition, delimIndex);\n                    sbuf.append(Convert.utf8Str(argArray[argIndex]));\n                    handledPosition = delimIndex + 2;\n                }\n            }\n        }\n        // 加入最后一个占位符后所有的字符\n        sbuf.append(strPattern, handledPosition, strPattern.length());\n\n        return sbuf.toString();\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/uuid/IdUtils.java",
    "content": "package ginyi.common.utils.uuid;\n\n/**\n * ID生成器工具类\n *\n * @author ruoyi\n */\npublic class IdUtils {\n    /**\n     * 获取随机UUID\n     *\n     * @return 随机UUID\n     */\n    public static String randomUUID() {\n        return UUID.randomUUID().toString();\n    }\n\n    /**\n     * 简化的UUID，去掉了横线\n     *\n     * @return 简化的UUID，去掉了横线\n     */\n    public static String simpleUUID() {\n        return UUID.randomUUID().toString(true);\n    }\n\n    /**\n     * 获取随机UUID，使用性能更好的ThreadLocalRandom生成UUID\n     *\n     * @return 随机UUID\n     */\n    public static String fastUUID() {\n        return UUID.fastUUID().toString();\n    }\n\n    /**\n     * 简化的UUID，去掉了横线，使用性能更好的ThreadLocalRandom生成UUID\n     *\n     * @return 简化的UUID，去掉了横线\n     */\n    public static String fastSimpleUUID() {\n        return UUID.fastUUID().toString(true);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/uuid/UUID.java",
    "content": "package ginyi.common.utils.uuid;\n\nimport cn.hutool.core.exceptions.UtilException;\n\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.SecureRandom;\nimport java.util.Random;\nimport java.util.concurrent.ThreadLocalRandom;\n\n/**\n * 提供通用唯一识别码（universally unique identifier）（UUID）实现\n *\n * @author ruoyi\n */\npublic final class UUID implements java.io.Serializable, Comparable<UUID> {\n    private static final long serialVersionUID = -1185015143654744140L;\n\n    /**\n     * SecureRandom 的单例\n     */\n    private static class Holder {\n        static final SecureRandom numberGenerator = getSecureRandom();\n    }\n\n    /**\n     * 此UUID的最高64有效位\n     */\n    private final long mostSigBits;\n\n    /**\n     * 此UUID的最低64有效位\n     */\n    private final long leastSigBits;\n\n    /**\n     * 私有构造\n     *\n     * @param data 数据\n     */\n    private UUID(byte[] data) {\n        long msb = 0;\n        long lsb = 0;\n        assert data.length == 16 : \"data must be 16 bytes in length\";\n        for (int i = 0; i < 8; i++) {\n            msb = (msb << 8) | (data[i] & 0xff);\n        }\n        for (int i = 8; i < 16; i++) {\n            lsb = (lsb << 8) | (data[i] & 0xff);\n        }\n        this.mostSigBits = msb;\n        this.leastSigBits = lsb;\n    }\n\n    /**\n     * 使用指定的数据构造新的 UUID。\n     *\n     * @param mostSigBits  用于 {@code UUID} 的最高有效 64 位\n     * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位\n     */\n    public UUID(long mostSigBits, long leastSigBits) {\n        this.mostSigBits = mostSigBits;\n        this.leastSigBits = leastSigBits;\n    }\n\n    /**\n     * 获取类型 4（伪随机生成的）UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。\n     *\n     * @return 随机生成的 {@code UUID}\n     */\n    public static UUID fastUUID() {\n        return randomUUID(false);\n    }\n\n    /**\n     * 获取类型 4（伪随机生成的）UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。\n     *\n     * @return 随机生成的 {@code UUID}\n     */\n    public static UUID randomUUID() {\n        return randomUUID(true);\n    }\n\n    /**\n     * 获取类型 4（伪随机生成的）UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。\n     *\n     * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码，否则可以得到更好的性能\n     * @return 随机生成的 {@code UUID}\n     */\n    public static UUID randomUUID(boolean isSecure) {\n        final Random ng = isSecure ? Holder.numberGenerator : getRandom();\n\n        byte[] randomBytes = new byte[16];\n        ng.nextBytes(randomBytes);\n        randomBytes[6] &= 0x0f; /* clear version */\n        randomBytes[6] |= 0x40; /* set to version 4 */\n        randomBytes[8] &= 0x3f; /* clear variant */\n        randomBytes[8] |= 0x80; /* set to IETF variant */\n        return new UUID(randomBytes);\n    }\n\n    /**\n     * 根据指定的字节数组获取类型 3（基于名称的）UUID 的静态工厂。\n     *\n     * @param name 用于构造 UUID 的字节数组。\n     * @return 根据指定数组生成的 {@code UUID}\n     */\n    public static UUID nameUUIDFromBytes(byte[] name) {\n        MessageDigest md;\n        try {\n            md = MessageDigest.getInstance(\"MD5\");\n        } catch (NoSuchAlgorithmException nsae) {\n            throw new InternalError(\"MD5 not supported\");\n        }\n        byte[] md5Bytes = md.digest(name);\n        md5Bytes[6] &= 0x0f; /* clear version */\n        md5Bytes[6] |= 0x30; /* set to version 3 */\n        md5Bytes[8] &= 0x3f; /* clear variant */\n        md5Bytes[8] |= 0x80; /* set to IETF variant */\n        return new UUID(md5Bytes);\n    }\n\n    /**\n     * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。\n     *\n     * @param name 指定 {@code UUID} 字符串\n     * @return 具有指定值的 {@code UUID}\n     * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常\n     */\n    public static UUID fromString(String name) {\n        String[] components = name.split(\"-\");\n        if (components.length != 5) {\n            throw new IllegalArgumentException(\"Invalid UUID string: \" + name);\n        }\n        for (int i = 0; i < 5; i++) {\n            components[i] = \"0x\" + components[i];\n        }\n\n        long mostSigBits = Long.decode(components[0]).longValue();\n        mostSigBits <<= 16;\n        mostSigBits |= Long.decode(components[1]).longValue();\n        mostSigBits <<= 16;\n        mostSigBits |= Long.decode(components[2]).longValue();\n\n        long leastSigBits = Long.decode(components[3]).longValue();\n        leastSigBits <<= 48;\n        leastSigBits |= Long.decode(components[4]).longValue();\n\n        return new UUID(mostSigBits, leastSigBits);\n    }\n\n    /**\n     * 返回此 UUID 的 128 位值中的最低有效 64 位。\n     *\n     * @return 此 UUID 的 128 位值中的最低有效 64 位。\n     */\n    public long getLeastSignificantBits() {\n        return leastSigBits;\n    }\n\n    /**\n     * 返回此 UUID 的 128 位值中的最高有效 64 位。\n     *\n     * @return 此 UUID 的 128 位值中最高有效 64 位。\n     */\n    public long getMostSignificantBits() {\n        return mostSigBits;\n    }\n\n    /**\n     * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。\n     * <p>\n     * 版本号具有以下含意:\n     * <ul>\n     * <li>1 基于时间的 UUID\n     * <li>2 DCE 安全 UUID\n     * <li>3 基于名称的 UUID\n     * <li>4 随机生成的 UUID\n     * </ul>\n     *\n     * @return 此 {@code UUID} 的版本号\n     */\n    public int version() {\n        // Version is bits masked by 0x000000000000F000 in MS long\n        return (int) ((mostSigBits >> 12) & 0x0f);\n    }\n\n    /**\n     * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。\n     * <p>\n     * 变体号具有以下含意：\n     * <ul>\n     * <li>0 为 NCS 向后兼容保留\n     * <li>2 <a href=\"http://www.ietf.org/rfc/rfc4122.txt\">IETF&nbsp;RFC&nbsp;4122</a>(Leach-Salz), 用于此类\n     * <li>6 保留，微软向后兼容\n     * <li>7 保留供以后定义使用\n     * </ul>\n     *\n     * @return 此 {@code UUID} 相关联的变体号\n     */\n    public int variant() {\n        // This field is composed of a varying number of bits.\n        // 0 - - Reserved for NCS backward compatibility\n        // 1 0 - The IETF aka Leach-Salz variant (used by this class)\n        // 1 1 0 Reserved, Microsoft backward compatibility\n        // 1 1 1 Reserved for future definition.\n        return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));\n    }\n\n    /**\n     * 与此 UUID 相关联的时间戳值。\n     *\n     * <p>\n     * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br>\n     * 所得到的时间戳以 100 毫微秒为单位，从 UTC（通用协调时间） 1582 年 10 月 15 日零时开始。\n     *\n     * <p>\n     * 时间戳值仅在在基于时间的 UUID（其 version 类型为 1）中才有意义。<br>\n     * 如果此 {@code UUID} 不是基于时间的 UUID，则此方法抛出 UnsupportedOperationException。\n     *\n     * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。\n     */\n    public long timestamp() throws UnsupportedOperationException {\n        checkTimeBase();\n        return (mostSigBits & 0x0FFFL) << 48//\n                | ((mostSigBits >> 16) & 0x0FFFFL) << 32//\n                | mostSigBits >>> 32;\n    }\n\n    /**\n     * 与此 UUID 相关联的时钟序列值。\n     *\n     * <p>\n     * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。\n     * <p>\n     * {@code clockSequence} 值仅在基于时间的 UUID（其 version 类型为 1）中才有意义。 如果此 UUID 不是基于时间的 UUID，则此方法抛出\n     * UnsupportedOperationException。\n     *\n     * @return 此 {@code UUID} 的时钟序列\n     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1\n     */\n    public int clockSequence() throws UnsupportedOperationException {\n        checkTimeBase();\n        return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);\n    }\n\n    /**\n     * 与此 UUID 相关的节点值。\n     *\n     * <p>\n     * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址，该地址用于生成此 UUID 以保证空间唯一性。\n     * <p>\n     * 节点值仅在基于时间的 UUID（其 version 类型为 1）中才有意义。<br>\n     * 如果此 UUID 不是基于时间的 UUID，则此方法抛出 UnsupportedOperationException。\n     *\n     * @return 此 {@code UUID} 的节点值\n     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1\n     */\n    public long node() throws UnsupportedOperationException {\n        checkTimeBase();\n        return leastSigBits & 0x0000FFFFFFFFFFFFL;\n    }\n\n    /**\n     * 返回此{@code UUID} 的字符串表现形式。\n     *\n     * <p>\n     * UUID 的字符串表示形式由此 BNF 描述：\n     *\n     * <pre>\n     * {@code\n     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>\n     * time_low               = 4*<hexOctet>\n     * time_mid               = 2*<hexOctet>\n     * time_high_and_version  = 2*<hexOctet>\n     * variant_and_sequence   = 2*<hexOctet>\n     * node                   = 6*<hexOctet>\n     * hexOctet               = <hexDigit><hexDigit>\n     * hexDigit               = [0-9a-fA-F]\n     * }\n     * </pre>\n     *\n     * </blockquote>\n     *\n     * @return 此{@code UUID} 的字符串表现形式\n     * @see #toString(boolean)\n     */\n    @Override\n    public String toString() {\n        return toString(false);\n    }\n\n    /**\n     * 返回此{@code UUID} 的字符串表现形式。\n     *\n     * <p>\n     * UUID 的字符串表示形式由此 BNF 描述：\n     *\n     * <pre>\n     * {@code\n     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>\n     * time_low               = 4*<hexOctet>\n     * time_mid               = 2*<hexOctet>\n     * time_high_and_version  = 2*<hexOctet>\n     * variant_and_sequence   = 2*<hexOctet>\n     * node                   = 6*<hexOctet>\n     * hexOctet               = <hexDigit><hexDigit>\n     * hexDigit               = [0-9a-fA-F]\n     * }\n     * </pre>\n     *\n     * </blockquote>\n     *\n     * @param isSimple 是否简单模式，简单模式为不带'-'的UUID字符串\n     * @return 此{@code UUID} 的字符串表现形式\n     */\n    public String toString(boolean isSimple) {\n        final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);\n        // time_low\n        builder.append(digits(mostSigBits >> 32, 8));\n        if (!isSimple) {\n            builder.append('-');\n        }\n        // time_mid\n        builder.append(digits(mostSigBits >> 16, 4));\n        if (!isSimple) {\n            builder.append('-');\n        }\n        // time_high_and_version\n        builder.append(digits(mostSigBits, 4));\n        if (!isSimple) {\n            builder.append('-');\n        }\n        // variant_and_sequence\n        builder.append(digits(leastSigBits >> 48, 4));\n        if (!isSimple) {\n            builder.append('-');\n        }\n        // node\n        builder.append(digits(leastSigBits, 12));\n\n        return builder.toString();\n    }\n\n    /**\n     * 返回此 UUID 的哈希码。\n     *\n     * @return UUID 的哈希码值。\n     */\n    @Override\n    public int hashCode() {\n        long hilo = mostSigBits ^ leastSigBits;\n        return ((int) (hilo >> 32)) ^ (int) hilo;\n    }\n\n    /**\n     * 将此对象与指定对象比较。\n     * <p>\n     * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值（每一位均相同）时，结果才为 {@code true}。\n     *\n     * @param obj 要与之比较的对象\n     * @return 如果对象相同，则返回 {@code true}；否则返回 {@code false}\n     */\n    @Override\n    public boolean equals(Object obj) {\n        if ((null == obj) || (obj.getClass() != UUID.class)) {\n            return false;\n        }\n        UUID id = (UUID) obj;\n        return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);\n    }\n\n    // Comparison Operations\n\n    /**\n     * 将此 UUID 与指定的 UUID 比较。\n     *\n     * <p>\n     * 如果两个 UUID 不同，且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段，则第一个 UUID 大于第二个 UUID。\n     *\n     * @param val 与此 UUID 比较的 UUID\n     * @return 在此 UUID 小于、等于或大于 val 时，分别返回 -1、0 或 1。\n     */\n    @Override\n    public int compareTo(UUID val) {\n        // The ordering is intentionally set up so that the UUIDs\n        // can simply be numerically compared as two numbers\n        return (this.mostSigBits < val.mostSigBits ? -1 : //\n                (this.mostSigBits > val.mostSigBits ? 1 : //\n                        (this.leastSigBits < val.leastSigBits ? -1 : //\n                                (this.leastSigBits > val.leastSigBits ? 1 : //\n                                        0))));\n    }\n\n    // -------------------------------------------------------------------------------------------------------------------\n    // Private method start\n\n    /**\n     * 返回指定数字对应的hex值\n     *\n     * @param val    值\n     * @param digits 位\n     * @return 值\n     */\n    private static String digits(long val, int digits) {\n        long hi = 1L << (digits * 4);\n        return Long.toHexString(hi | (val & (hi - 1))).substring(1);\n    }\n\n    /**\n     * 检查是否为time-based版本UUID\n     */\n    private void checkTimeBase() {\n        if (version() != 1) {\n            throw new UnsupportedOperationException(\"Not a time-based UUID\");\n        }\n    }\n\n    /**\n     * 获取{@link SecureRandom}，类提供加密的强随机数生成器 (RNG)\n     *\n     * @return {@link SecureRandom}\n     */\n    public static SecureRandom getSecureRandom() {\n        try {\n            return SecureRandom.getInstance(\"SHA1PRNG\");\n        } catch (NoSuchAlgorithmException e) {\n            throw new UtilException(e);\n        }\n    }\n\n    /**\n     * 获取随机数生成器对象<br>\n     * ThreadLocalRandom是JDK 7之后提供并发产生随机数，能够解决多个线程发生的竞争争夺。\n     *\n     * @return {@link ThreadLocalRandom}\n     */\n    public static ThreadLocalRandom getRandom() {\n        return ThreadLocalRandom.current();\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-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>ginyi-springboot</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-common</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>ginyi-common-mysql</module>\n        <module>ginyi-common-redis</module>\n        <module>ginyi-common-exception</module>\n        <module>ginyi-common-result</module>\n        <module>ginyi-common-utils</module>\n        <module>ginyi-common-swagger</module>\n        <module>ginyi-common-constants</module>\n        <module>ginyi-common-annotation</module>\n        <module>ginyi-common-enums</module>\n    </modules>\n\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-core/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>ginyi-framework</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-framework-core</artifactId>\n\n    <properties>\n        <maven.compiler.source>8</maven.compiler.source>\n        <maven.compiler.target>8</maven.compiler.target>\n    </properties>\n\n    <dependencies>\n        <!-- aop -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-core/src/main/java/ginyi/framework/core/config/AppConfig.java",
    "content": "package ginyi.framework.core.config;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.fusesource.jansi.Ansi;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.core.env.ConfigurableEnvironment;\n\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\n\n@Slf4j\npublic class AppConfig {\n\n    /**\n     * 彩色打印字体\n     */\n    public static String colorPrint(String s, Ansi.Color color) {\n        return Ansi.ansi().eraseScreen().fg(color).a(s).reset().toString();\n    }\n\n    public static void printAppInfo(ConfigurableApplicationContext context) throws UnknownHostException {\n        // 获取环境配置信息\n        ConfigurableEnvironment env = context.getEnvironment();\n        String projectName = env.getProperty(\"ginyi.project-name\");\n        String contextPath = env.getProperty(\"server.servlet.context-path\");\n        String port = env.getProperty(\"server.port\");\n        String baseUrl = \"http://\" + InetAddress.getLocalHost().getHostAddress() + \":\" + port + contextPath;\n        String swaggerDoc = baseUrl + \"swagger-ui.html\";\n        String knife4jDoc = baseUrl + \"doc.html\";\n\n        String startSuccess = \"\\n\" +\n                \"  _______  __   __   __  ____    ____  __     \\n\" +\n                \" /  _____||  | |  \\\\ |  | \\\\   \\\\  /   / |  | 项目名称：\" + projectName + \"\\n\" +\n                \"|  |  __  |  | |   \\\\|  |  \\\\   \\\\/   /  |  | 服务地址：\" + baseUrl + \"\\n\" +\n                \"|  | |_ | |  | |       |   \\\\_    _/   |  | 接口文档：\" + knife4jDoc + \" or \" + swaggerDoc + \"\\n\" +\n                \"|  |__| | |  | |  |\\\\   |     |  |     |  | 如果你喜欢这个项目，欢迎Star！https://gitee.com/Ginyi/ginyi-spring-vue \\n\" +\n                \" \\\\______| |__| |__| \\\\__|     |__|     |__| 启动成功...\\n\";\n\n        log.info(\"\\n{}\", colorPrint(startSuccess, Ansi.Color.CYAN));\n    }\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-core/src/main/java/ginyi/framework/core/config/GinyiConfig.java",
    "content": "package ginyi.framework.core.config;\n\nimport lombok.Data;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n/**\n * 读取项目相关配置\n *\n * @author ruoyi\n */\n@Component\npublic class GinyiConfig {\n\n    /**\n     * 上传路径\n     */\n    @Value(\"${ginyi.profile}\")\n    public static String profile;\n\n    /**\n     * 获取导入上传路径\n     */\n    public static String getImportPath() {\n        return profile + \"/import\";\n    }\n\n    /**\n     * 获取头像上传路径\n     */\n    public static String getAvatarPath() {\n        return profile + \"/avatar\";\n    }\n\n    /**\n     * 获取下载路径\n     */\n    public static String getDownloadPath() {\n        return profile + \"/download/\";\n    }\n\n    /**\n     * 获取上传路径\n     */\n    public static String getUploadPath() {\n        return profile + \"/upload\";\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/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>ginyi-framework</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-framework-security</artifactId>\n\n    <properties>\n        <maven.compiler.source>8</maven.compiler.source>\n        <maven.compiler.target>8</maven.compiler.target>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-framework-core</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n\n        <!-- 解析客户端操作系统、浏览器等 -->\n        <dependency>\n            <groupId>eu.bitwalker</groupId>\n            <artifactId>UserAgentUtils</artifactId>\n        </dependency>\n        <!-- 公用枚举包 -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-common-enums</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/aspectj/LogAspect.java",
    "content": "package ginyi.framework.security.aspectj;\n\nimport cn.hutool.core.date.DateUtil;\nimport com.alibaba.fastjson2.JSON;\nimport ginyi.common.annotation.Log;\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.enums.BusinessStatus;\nimport ginyi.common.enums.HttpMethod;\nimport ginyi.common.exception.CommonException;\nimport ginyi.common.exception.UnPermissionException;\nimport ginyi.common.exception.UserPasswordNotMatchException;\nimport ginyi.common.exception.UserPasswordRetryLimitExceedException;\nimport ginyi.common.result.StateCode;\nimport ginyi.common.utils.ServletUtils;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.common.utils.ip.IpUtils;\nimport ginyi.framework.security.filter.PropertyPreExcludeFilter;\nimport ginyi.framework.security.manager.AsyncManager;\nimport ginyi.framework.security.manager.factory.AsyncFactory;\nimport ginyi.framework.security.utils.SecurityUtils;\nimport ginyi.system.domain.LoginUser;\nimport ginyi.system.domain.SysLogOperation;\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.fusesource.jansi.Ansi;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.stereotype.Component;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.validation.ObjectError;\nimport org.springframework.web.bind.MethodArgumentNotValidException;\nimport org.springframework.web.multipart.MaxUploadSizeExceededException;\nimport org.springframework.web.multipart.MultipartException;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.*;\n\n/**\n * 操作日志记录处理\n *\n * @author ruoyi\n */\n@Aspect\n@Component\n@Slf4j\npublic class LogAspect {\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            LoginUser loginUser = SecurityUtils.getLoginUser();\n            // 数据库日志\n            SysLogOperation operationLog = new SysLogOperation();\n            operationLog.setStatus(BusinessStatus.SUCCESS.ordinal());\n            // 请求的地址\n            String ip = IpUtils.getIpAddr(ServletUtils.getRequest());\n            operationLog.setOperationIp(ip);\n            operationLog.setOperationUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));\n            operationLog.setCreateTime(new Date());\n            if (loginUser != null) {\n                operationLog.setOperationName(loginUser.getUsername());\n            }\n            // 设置方法名称\n            String className = joinPoint.getTarget().getClass().getName();\n            String methodName = joinPoint.getSignature().getName();\n            operationLog.setMethod(className + \".\" + methodName + \"()\");\n            // 设置请求方式\n            operationLog.setRequestMethod(ServletUtils.getRequest().getMethod());\n            // 处理设置注解上的参数\n            getControllerMethodDescription(joinPoint, controllerLog, operationLog, jsonResult);\n            log.info(\"[ Log日志记录 ] 操作人员: {}\", operationLog.getOperationName());\n            log.info(\"[ Log日志记录 ] 操作主机: {}\", operationLog.getOperationIp());\n            log.info(\"[ Log日志记录 ] 请求时间: {}\", DateUtil.date(System.currentTimeMillis()));\n            log.info(\"[ Log日志记录 ] 请求模块: {}\", operationLog.getTitle());\n            log.info(\"[ Log日志记录 ] 请求地址: {}\", operationLog.getOperationUrl());\n            log.info(\"[ Log日志记录 ] 方法类型: {}\", operationLog.getRequestMethod());\n            log.info(\"[ Log日志记录 ] 操作类型: {}\", operationLog.getBusinessType() == 0 ? \"其他\" :\n                    operationLog.getBusinessType() == 1 ? \"新增\" : operationLog.getBusinessType() == 2 ? \"修改\" : \"删除\");\n            log.info(\"[ Log日志记录 ] 方法名称: {}\", operationLog.getMethod());\n            log.info(\"[ Log日志记录 ] 请求参数: {}\", operationLog.getOperationParam());\n\n            if (e != null) {\n                String msg = handleException(e);\n                operationLog.setStatus(BusinessStatus.FAIL.ordinal());\n                operationLog.setErrorMsg(msg);\n                log.info(\"[ Log日志记录 ] 请求结果: {}\", operationLog.getStatus() == 0 ? colorPrint(\"成功\", Ansi.Color.GREEN) : colorPrint(\"失败\", Ansi.Color.RED));\n                log.info(\"[ Log日志记录 ] 响应结果: {}\\n\", msg);\n            } else {\n                log.info(\"[ Log日志记录 ] 请求结果: {}\", operationLog.getStatus() == 0 ? colorPrint(\"成功\", Ansi.Color.GREEN) : colorPrint(\"失败\", Ansi.Color.RED));\n                log.info(\"[ Log日志记录 ] 响应结果: {}\\n\", operationLog.getJsonResult());\n            }\n            // 保存数据库\n            AsyncManager.me().execute(AsyncFactory.recordOper(operationLog));\n        } catch (Exception exp) {\n            // 记录本地异常日志\n            log.error(\"记录本地日志时发生异常，异常信息:{}\", exp.getMessage());\n            exp.printStackTrace();\n        }\n    }\n\n    /**\n     * 处理并异常信息\n     *\n     * @param e\n     * @return\n     */\n    private String handleException(Exception e) {\n        HashMap<String, Object> map = new HashMap<>();\n        // 公用异常\n        if (e instanceof CommonException) {\n            CommonException ce = (CommonException) e;\n            map.put(\"code\", ce.getState().getCode());\n            map.put(\"msg\", ce.getState().getMessage());\n            map.put(\"data\", ce.getData());\n            return JSON.toJSONString(map);\n        }\n        // 请求参数不合法\n        if (e instanceof HttpMessageNotReadableException) {\n            HttpMessageNotReadableException he = (HttpMessageNotReadableException) e;\n            map.put(\"code\", StateCode.ERROR_REQUEST_PARAMS.getCode());\n            map.put(\"msg\", StateCode.ERROR_REQUEST_PARAMS.getMessage());\n            map.put(\"data\", CommonMessageConstants.SYS_REQUEST_ILLEGAL);\n            return JSON.toJSONString(map);\n        }\n        // 用户名密码不匹配\n        if (e instanceof UserPasswordNotMatchException) {\n            UserPasswordNotMatchException ue = (UserPasswordNotMatchException) e;\n            map.put(\"code\", StateCode.ERROR_UNAUTHENTICATION.getCode());\n            map.put(\"msg\", StateCode.ERROR_UNAUTHENTICATION.getMessage());\n            map.put(\"data\", CommonMessageConstants.USER_PASSWORD_NOT_MATCH);\n            return JSON.toJSONString(map);\n        }\n        // 用户登录失败次数超最大限制异常\n        if (e instanceof UserPasswordRetryLimitExceedException) {\n            UserPasswordRetryLimitExceedException upe = (UserPasswordRetryLimitExceedException) e;\n            map.put(\"code\", upe.getState().getCode());\n            map.put(\"msg\", upe.getState().getMessage());\n            map.put(\"data\", upe.getData());\n            return JSON.toJSONString(map);\n        }\n        // 上传文件异常\n        if (e instanceof MultipartException) {\n            MultipartException mte = (MultipartException) e;\n            map.put(\"code\", StateCode.ERROR_MULTIPART.getCode());\n            map.put(\"msg\", StateCode.ERROR_MULTIPART.getMessage());\n            if (mte instanceof MaxUploadSizeExceededException) {\n                map.put(\"data\", CommonMessageConstants.UPLOAD_SIZE_EXCEED);\n                return JSON.toJSONString(map);\n            }\n            map.put(\"data\", CommonMessageConstants.UPLOAD_FILE_ERROR);\n            return JSON.toJSONString(map);\n        }\n        // 访问接口无权限\n        if (e instanceof UnPermissionException) {\n            UnPermissionException upe = (UnPermissionException) e;\n            map.put(\"code\", StateCode.ERROR_NOT_PERMISSION.getCode());\n            map.put(\"msg\", StateCode.ERROR_NOT_PERMISSION.getMessage());\n            map.put(\"data\", CommonMessageConstants.SYS_ERROR);\n            return JSON.toJSONString(map);\n        }\n        // 处理其他所有未知异常\n        map.put(\"code\", StateCode.ERROR_SYSTEM.getCode());\n        map.put(\"msg\", StateCode.ERROR_SYSTEM.getMessage());\n        map.put(\"data\", CommonMessageConstants.SYS_ERROR);\n        return JSON.toJSONString(map);\n    }\n\n    /**\n     * 获取注解中对方法的描述信息 用于Controller层注解\n     *\n     * @param log          日志\n     * @param operationLog 操作日志\n     * @throws Exception\n     */\n    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysLogOperation operationLog, Object jsonResult) throws Exception {\n        // 设置action动作\n        operationLog.setBusinessType(log.businessType().ordinal());\n        // 设置标题\n        operationLog.setTitle(log.title());\n        // 设置操作人类别\n        operationLog.setOperatorType(log.operatorType().ordinal());\n        // 是否需要保存request，参数和值\n        if (log.isSaveRequestData()) {\n            // 获取参数的信息，传入到数据库中。\n            setRequestValue(joinPoint, operationLog);\n        }\n        // 是否需要保存response，参数和值\n        if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) {\n            operationLog.setJsonResult(JSON.toJSONString(jsonResult));\n        }\n    }\n\n    /**\n     * 获取请求的参数，放到log中\n     *\n     * @param operationLog 操作日志\n     * @throws Exception 异常\n     */\n    private void setRequestValue(JoinPoint joinPoint, SysLogOperation operationLog) throws Exception {\n        String requestMethod = operationLog.getRequestMethod();\n        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {\n            String params = argsArrayToString(joinPoint.getArgs());\n            operationLog.setOperationParam(params);\n        } else {\n            Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());\n            operationLog.setOperationParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter()), 0, 2000));\n        }\n    }\n\n    /**\n     * 参数拼装\n     */\n    private String argsArrayToString(Object[] paramsArray) {\n        String params = \"\";\n        if (paramsArray != null && paramsArray.length > 0) {\n            for (Object o : paramsArray) {\n                if (StringUtils.isNotNull(o) && !isFilterObject(o)) {\n                    try {\n                        String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter());\n                        params += jsonObj.toString() + \" \";\n                    } catch (Exception e) {\n                    }\n                }\n            }\n        }\n        return params.trim();\n    }\n\n    /**\n     * 忽略敏感属性\n     */\n    public PropertyPreExcludeFilter excludePropertyPreFilter() {\n        return new PropertyPreExcludeFilter().addExcludes(EXCLUDE_PROPERTIES);\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     * 彩色打印字体\n     */\n    public static String colorPrint(String s, Ansi.Color color) {\n        return Ansi.ansi().eraseScreen().fg(color).a(s).reset().toString();\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/config/ResourcesConfig.java",
    "content": "package ginyi.framework.security.config;\n\n\nimport ginyi.framework.core.config.GinyiConfig;\nimport ginyi.common.utils.Constants;\nimport ginyi.framework.security.interceptor.PreviewEnvInterceptor;\nimport ginyi.framework.security.interceptor.RepeatSubmitInterceptor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.CacheControl;\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\nimport javax.annotation.Resource;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 通用配置\n *\n * @author ruoyi\n */\n@Configuration\npublic class ResourcesConfig implements WebMvcConfigurer {\n\n    @Resource\n    private RepeatSubmitInterceptor repeatSubmitInterceptor;\n    @Resource\n    private PreviewEnvInterceptor previewEnvInterceptor;\n\n    @Override\n    public void addResourceHandlers(ResourceHandlerRegistry registry) {\n        /** 本地文件上传路径 */\n        registry.addResourceHandler(Constants.RESOURCE_PREFIX + \"/**\")\n                .addResourceLocations(\"file:\" + GinyiConfig.profile + \"/\");\n\n        /** swagger配置 */\n        registry.addResourceHandler(\"/swagger-ui/**\")\n                .addResourceLocations(\"classpath:/META-INF/resources/webjars/springfox-swagger-ui/\")\n                .setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());\n        ;\n    }\n\n    /**\n     * 自定义拦截规则\n     */\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns(\"/**\");\n        registry.addInterceptor(previewEnvInterceptor).addPathPatterns(\"/**\");\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}"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/config/SecurityConfig.java",
    "content": "package ginyi.framework.security.config;\n\nimport ginyi.framework.security.filter.JwtAuthenticationTokenFilter;\nimport ginyi.framework.security.handle.AuthenticationEntryPointImpl;\nimport ginyi.framework.security.handle.LogoutSuccessHandlerImpl;\nimport ginyi.framework.security.properties.PermitAllUrlProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.authentication.logout.LogoutFilter;\nimport org.springframework.web.filter.CorsFilter;\n\nimport javax.annotation.Resource;\n\n/**\n * spring security配置\n *\n * @author ruoyi\n */\n@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n    /**\n     * 自定义用户认证逻辑\n     */\n    @Resource\n    private UserDetailsService userDetailsService;\n\n    /**\n     * 认证失败处理类\n     */\n    @Resource\n    private AuthenticationEntryPointImpl unauthorizedHandler;\n\n    /**\n     * 退出处理类\n     */\n    @Resource\n    private LogoutSuccessHandlerImpl logoutSuccessHandler;\n\n    /**\n     * token认证过滤器\n     */\n    @Resource\n    private JwtAuthenticationTokenFilter authenticationTokenFilter;\n\n    /**\n     * 跨域过滤器\n     */\n    @Resource\n    private CorsFilter corsFilter;\n\n    /**\n     * 允许匿名访问的地址\n     */\n    @Resource\n    private PermitAllUrlProperties permitAllUrl;\n\n    /**\n     * 解决 无法直接注入 AuthenticationManager\n     *\n     * @return\n     * @throws Exception\n     */\n    @Bean\n    @Override\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n\n    /**\n     * anyRequest          |   匹配所有请求路径\n     * access              |   SpringEl表达式结果为true时可以访问\n     * anonymous           |   匿名可以访问\n     * denyAll             |   用户不能访问\n     * fullyAuthenticated  |   用户完全认证可以访问（非remember-me下自动登录）\n     * hasAnyAuthority     |   如果有参数，参数表示权限，则其中任何一个权限可以访问\n     * hasAnyRole          |   如果有参数，参数表示角色，则其中任何一个角色可以访问\n     * hasAuthority        |   如果有参数，参数表示权限，则其权限可以访问\n     * hasIpAddress        |   如果有参数，参数表示IP地址，如果用户IP和参数匹配，则可以访问\n     * hasRole             |   如果有参数，参数表示角色，则其角色可以访问\n     * permitAll           |   用户可以任意访问\n     * rememberMe          |   允许通过remember-me登录的用户访问\n     * authenticated       |   用户登录后可访问\n     */\n    @Override\n    protected void configure(HttpSecurity httpSecurity) throws Exception {\n        // 注解标记允许匿名访问的url\n        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();\n        permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());\n\n        httpSecurity\n                // CSRF禁用，因为不使用session\n                .csrf().disable()\n                // 禁用HTTP响应标头\n                .headers().cacheControl().disable().and()\n                // 认证失败处理类\n                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()\n                // 基于token，所以不需要session\n                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()\n                // 过滤请求\n                .authorizeRequests()\n                // 对于登录login 注册register 验证码captchaImage 允许匿名访问\n                .antMatchers(\"/api/user/login\", \"/api/user/register\", \"/api/verify/**\").permitAll()\n                // 静态资源，可匿名访问\n                .antMatchers(HttpMethod.GET, \"/\", \"/*.html\", \"/**/*.html\", \"/**/*.css\", \"/**/*.js\", \"/profile/**\").permitAll()\n                .antMatchers(\"/swagger-ui.html\", \"/swagger-resources/**\", \"/webjars/**\", \"/*/api-docs\", \"/druid/**\").permitAll()\n                // 放行websocket\n                .antMatchers(\"/websocket/**\").permitAll()\n                // 除上面外的所有请求全部需要鉴权认证\n                .anyRequest().authenticated()\n                .and()\n                .headers().frameOptions().disable();\n        // 添加Logout filter\n        httpSecurity.logout().logoutUrl(\"/**/logout\").logoutSuccessHandler(logoutSuccessHandler);\n        // 添加JWT filter\n        httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);\n        // 添加CORS filter\n        httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);\n        httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);\n    }\n\n    /**\n     * 强散列哈希加密实现\n     */\n    @Bean\n    public BCryptPasswordEncoder bCryptPasswordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n\n    /**\n     * 身份认证接口\n     */\n    @Override\n    protected void configure(AuthenticationManagerBuilder auth) throws Exception {\n        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/config/ThreadPoolConfig.java",
    "content": "package ginyi.framework.security.config;\n\nimport ginyi.common.utils.Threads;\nimport org.apache.commons.lang3.concurrent.BasicThreadFactory;\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 ruoyi\n **/\n@Configuration\npublic class ThreadPoolConfig {\n    // 核心线程池大小\n    private int corePoolSize = 50;\n\n    // 最大可创建的线程数\n    private int maxPoolSize = 200;\n\n    // 队列最大长度\n    private int queueCapacity = 1000;\n\n    // 线程池维护线程所允许的空闲时间\n    private int keepAliveSeconds = 300;\n\n    @Bean(name = \"threadPoolTaskExecutor\")\n    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {\n        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();\n        executor.setMaxPoolSize(maxPoolSize);\n        executor.setCorePoolSize(corePoolSize);\n        executor.setQueueCapacity(queueCapacity);\n        executor.setKeepAliveSeconds(keepAliveSeconds);\n        // 线程池对拒绝任务(无线程可用)的处理策略\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(corePoolSize,\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": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/context/AuthenticationContextHolder.java",
    "content": "package ginyi.framework.security.context;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * 身份验证信息\n *\n * @author ruoyi\n */\npublic class AuthenticationContextHolder {\n    private static final ThreadLocal<Authentication> contextHolder = new ThreadLocal<>();\n\n    public static Authentication getContext() {\n        return contextHolder.get();\n    }\n\n    public static void setContext(Authentication context) {\n        contextHolder.set(context);\n    }\n\n    public static void clearContext() {\n        contextHolder.remove();\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/context/PermissionContextHolder.java",
    "content": "package ginyi.framework.security.context;\n\nimport ginyi.common.utils.text.Convert;\nimport org.springframework.web.context.request.RequestAttributes;\nimport org.springframework.web.context.request.RequestContextHolder;\n\n/**\n * 权限信息\n *\n * @author ruoyi\n */\npublic class PermissionContextHolder {\n    private static final String PERMISSION_CONTEXT_ATTRIBUTES = \"PERMISSION_CONTEXT\";\n\n    public static void setContext(String permission) {\n        RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission,\n                RequestAttributes.SCOPE_REQUEST);\n    }\n\n    public static String getContext() {\n        return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES,\n                RequestAttributes.SCOPE_REQUEST));\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/filter/JwtAuthenticationTokenFilter.java",
    "content": "package ginyi.framework.security.filter;\n\nimport ginyi.common.utils.StringUtils;\nimport ginyi.framework.security.utils.SecurityUtils;\nimport ginyi.system.domain.LoginUser;\nimport ginyi.system.service.ITokenService;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.annotation.Resource;\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * token过滤器 验证token有效性\n *\n * @author ruoyi\n */\n@Component\npublic class JwtAuthenticationTokenFilter extends OncePerRequestFilter {\n    @Resource\n    private ITokenService tokenService;\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n            throws ServletException, IOException {\n        LoginUser loginUser = tokenService.getLoginUser(request);\n        if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) {\n            tokenService.verifyToken(loginUser);\n            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());\n            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));\n            SecurityContextHolder.getContext().setAuthentication(authenticationToken);\n        }\n        chain.doFilter(request, response);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/filter/PropertyPreExcludeFilter.java",
    "content": "package ginyi.framework.security.filter;\n\nimport com.alibaba.fastjson2.filter.SimplePropertyPreFilter;\n\n/**\n * 排除JSON敏感属性\n *\n * @author ruoyi\n */\npublic class PropertyPreExcludeFilter extends SimplePropertyPreFilter {\n    public PropertyPreExcludeFilter() {\n    }\n\n    public PropertyPreExcludeFilter addExcludes(String... filters) {\n        for (int i = 0; i < filters.length; i++) {\n            this.getExcludes().add(filters[i]);\n        }\n        return this;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/filter/RepeatedlyRequestWrapper.java",
    "content": "package ginyi.framework.security.filter;\n\nimport ginyi.common.utils.Constants;\nimport ginyi.common.utils.http.HttpHelper;\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 = HttpHelper.getBodyString(request).getBytes(Constants.UTF8);\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": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/handle/AuthenticationEntryPointImpl.java",
    "content": "package ginyi.framework.security.handle;\n\nimport com.alibaba.fastjson2.JSON;\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.result.CommonResult;\nimport ginyi.common.result.StateCode;\nimport ginyi.common.utils.ServletUtils;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.io.Serializable;\n\n/**\n * 认证失败处理类 返回未授权\n *\n * @author ruoyi\n */\n@Component\npublic class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable {\n    private static final long serialVersionUID = -8970718410437077606L;\n\n    @Override\n    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)\n            throws IOException {\n        ServletUtils.renderString(response, JSON.toJSONString(CommonResult.error(StateCode.ERROR_AUTHENTICATION_VALID, CommonMessageConstants.SYS_AUTHENTICATION_VALID)));\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/handle/LogoutSuccessHandlerImpl.java",
    "content": "package ginyi.framework.security.handle;\n\nimport com.alibaba.fastjson2.JSON;\nimport ginyi.common.result.CommonResult;\nimport ginyi.common.result.StateCode;\nimport ginyi.common.utils.Constants;\nimport ginyi.common.utils.ServletUtils;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.framework.security.manager.AsyncManager;\nimport ginyi.framework.security.manager.factory.AsyncFactory;\nimport ginyi.system.domain.LoginUser;\nimport ginyi.system.service.ITokenService;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessHandler;\n\nimport javax.annotation.Resource;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * 自定义退出处理类 返回成功\n *\n * @author ruoyi\n */\n@Configuration\npublic class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {\n    @Resource\n    private ITokenService tokenService;\n\n    /**\n     * 退出处理\n     *\n     * @return\n     */\n    @Override\n    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)\n            throws IOException, ServletException {\n        LoginUser loginUser = tokenService.getLoginUser(request);\n        // 判断token是否存在\n        if (StringUtils.isNull(loginUser)) {\n            ServletUtils.renderString(response, JSON.toJSONString(CommonResult.error(StateCode.ERROR_TIMEOUT_TOKEN)));\n        }\n\n        String userName = loginUser.getUsername();\n        // 删除用户缓存记录\n        tokenService.delLoginUser(loginUser.getToken());\n        // 记录用户退出日志\n        AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, \"退出成功\"));\n        ServletUtils.renderString(response, JSON.toJSONString(CommonResult.success()));\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/interceptor/PreviewEnvInterceptor.java",
    "content": "package ginyi.framework.security.interceptor;\n\nimport cn.hutool.crypto.SecureUtil;\nimport cn.hutool.extra.servlet.ServletUtil;\nimport ginyi.common.constant.CacheConstants;\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.exception.CommonException;\nimport ginyi.common.redis.cache.RedisCache;\nimport ginyi.common.result.StateCode;\nimport ginyi.common.utils.StringUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.servlet.HandlerInterceptor;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.concurrent.TimeUnit;\n\n@Slf4j\n@Component\npublic class PreviewEnvInterceptor implements HandlerInterceptor {\n\n    @Resource\n    private RedisCache redisCache;\n    @Value(\"${ginyi.project-author}\")\n    private String author;\n\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {\n        // 不允许增删改数据\n//        boolean flag;\n//        List<String> list = Arrays.asList(\"add\", \"update\", \"edit\", \"delete\", \"remove\", \"register\");\n//        for (String url : list) {\n//            flag = request.getRequestURI().contains(url);\n//            if (flag) {\n//                throw new CommonException(StateCode.ERROR_LIMITED, CommonMessageConstants.SYS_PREVIEW_ENV);\n//            }\n//        }\n\n        // 记录ip请求，防止被恶意请求接口\n        String clientIP = ServletUtil.getClientIP(request);\n        String userAgent = ServletUtil.getHeader(request, \"User-Agent\", \"utf-8\");\n        String key = CacheConstants.API_REQUEST + SecureUtil.md5(clientIP + userAgent) + \":\" + clientIP + \":\" + request.getRequestURI();\n        Integer count = redisCache.getCacheObject(key, Integer.class);\n        long expire = redisCache.getExpire(key);\n\n        if (StringUtils.isNull(count)) {\n            redisCache.setCacheObject(key, 1, 300, TimeUnit.SECONDS);\n        } else {\n            // 请求大于100次\n            if (count >= 100) {\n                redisCache.setCacheObject(key, count + 1, 300, TimeUnit.SECONDS);\n                throw new CommonException(StateCode.ERROR_LIMITED, CommonMessageConstants.SYS_BED_REQUEST);\n            } else {\n                redisCache.setCacheObject(key, count + 1, (int) expire, TimeUnit.SECONDS);\n            }\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/interceptor/RepeatSubmitInterceptor.java",
    "content": "package ginyi.framework.security.interceptor;\n\nimport com.alibaba.fastjson2.JSON;\nimport ginyi.common.annotation.RepeatSubmit;\nimport ginyi.common.result.CommonResult;\nimport ginyi.common.result.StateCode;\nimport ginyi.common.utils.ServletUtils;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.method.HandlerMethod;\nimport org.springframework.web.servlet.HandlerInterceptor;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.lang.reflect.Method;\n\n/**\n * 防止重复提交拦截器\n *\n * @author ruoyi\n */\n@Component\npublic abstract class RepeatSubmitInterceptor implements HandlerInterceptor {\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {\n        if (handler instanceof HandlerMethod) {\n            HandlerMethod handlerMethod = (HandlerMethod) handler;\n            Method method = handlerMethod.getMethod();\n            RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);\n            if (annotation != null) {\n                if (this.isRepeatSubmit(request, annotation)) {\n                    CommonResult commonResult = CommonResult.error(StateCode.ERROR_PARAMS_SERVICE, annotation.message());\n                    ServletUtils.renderString(response, JSON.toJSONString(commonResult));\n                    return false;\n                }\n            }\n            return true;\n        } else {\n            return true;\n        }\n    }\n\n    /**\n     * 验证是否重复提交由子类实现具体的防重复提交的规则\n     *\n     * @param request\n     * @return\n     * @throws Exception\n     */\n    public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/interceptor/impl/SameUrlDataInterceptor.java",
    "content": "package ginyi.framework.security.interceptor.impl;\n\nimport com.alibaba.fastjson2.JSON;\nimport ginyi.common.annotation.RepeatSubmit;\nimport ginyi.common.constant.CacheConstants;\nimport ginyi.common.redis.cache.RedisCache;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.common.utils.http.HttpHelper;\nimport ginyi.framework.security.filter.RepeatedlyRequestWrapper;\nimport ginyi.framework.security.interceptor.RepeatSubmitInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 判断请求url和数据是否和上一次相同，\n * 如果和上次相同，则是重复提交表单。 有效时间为10秒内。\n *\n * @author ruoyi\n */\n@Component\npublic class SameUrlDataInterceptor extends RepeatSubmitInterceptor {\n    public final String REPEAT_PARAMS = \"repeatParams\";\n\n    public final String REPEAT_TIME = \"repeatTime\";\n\n    // 令牌自定义标识\n    @Value(\"${token.header}\")\n    private String header;\n\n    @Autowired\n    private RedisCache redisCache;\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) {\n        String nowParams = \"\";\n        if (request instanceof RepeatedlyRequestWrapper) {\n            RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;\n            nowParams = HttpHelper.getBodyString(repeatedlyRequest);\n        }\n\n        // body参数为空，获取Parameter的数据\n        if (StringUtils.isEmpty(nowParams)) {\n            nowParams = JSON.toJSONString(request.getParameterMap());\n        }\n        Map<String, Object> nowDataMap = new HashMap<String, Object>();\n        nowDataMap.put(REPEAT_PARAMS, nowParams);\n        nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());\n\n        // 请求地址（作为存放cache的key值）\n        String url = request.getRequestURI();\n\n        // 唯一值（没有消息头则使用请求地址）\n        String submitKey = StringUtils.trimToEmpty(request.getHeader(header));\n\n        // 唯一标识（指定key + url + 消息头）\n        String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey;\n\n        Object sessionObj = redisCache.getCacheObject(cacheRepeatKey, Object.class);\n        if (sessionObj != null) {\n            Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;\n            if (sessionMap.containsKey(url)) {\n                Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);\n                if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) {\n                    return true;\n                }\n            }\n        }\n        Map<String, Object> cacheMap = new HashMap<String, Object>();\n        cacheMap.put(url, nowDataMap);\n        redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS);\n        return false;\n    }\n\n    /**\n     * 判断参数是否相同\n     */\n    private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap) {\n        String nowParams = (String) nowMap.get(REPEAT_PARAMS);\n        String preParams = (String) preMap.get(REPEAT_PARAMS);\n        return nowParams.equals(preParams);\n    }\n\n    /**\n     * 判断两次间隔时间\n     */\n    private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval) {\n        long time1 = (Long) nowMap.get(REPEAT_TIME);\n        long time2 = (Long) preMap.get(REPEAT_TIME);\n        if ((time1 - time2) < interval) {\n            return true;\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/manager/AsyncManager.java",
    "content": "package ginyi.framework.security.manager;\n\nimport ginyi.common.utils.Threads;\nimport ginyi.common.utils.spring.SpringUtils;\n\nimport java.util.TimerTask;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 异步任务管理器\n *\n * @author ruoyi\n */\npublic class AsyncManager {\n    /**\n     * 操作延迟10毫秒\n     */\n    private final int OPERATE_DELAY_TIME = 10;\n\n    /**\n     * 异步操作任务调度线程池\n     */\n    private ScheduledExecutorService executor = SpringUtils.getBean(\"scheduledExecutorService\");\n\n    /**\n     * 单例模式\n     */\n    private AsyncManager() {\n    }\n\n    private static AsyncManager me = new AsyncManager();\n\n    public static AsyncManager me() {\n        return me;\n    }\n\n    /**\n     * 执行任务\n     *\n     * @param task 任务\n     */\n    public void execute(TimerTask task) {\n        executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);\n    }\n\n    /**\n     * 停止任务线程池\n     */\n    public void shutdown() {\n        Threads.shutdownAndAwaitTermination(executor);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/manager/factory/AsyncFactory.java",
    "content": "package ginyi.framework.security.manager.factory;\n\nimport eu.bitwalker.useragentutils.UserAgent;\nimport ginyi.common.utils.LogUtils;\nimport ginyi.common.utils.ServletUtils;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.common.utils.Constants;\nimport ginyi.common.utils.ip.AddressUtils;\nimport ginyi.common.utils.ip.IpUtils;\nimport ginyi.common.utils.spring.SpringUtils;\nimport ginyi.system.domain.SysLogLogin;\nimport ginyi.system.domain.SysLogOperation;\nimport lombok.extern.slf4j.Slf4j;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.data.mongodb.core.MongoTemplate;\n\nimport java.util.Date;\nimport java.util.TimerTask;\n\n/**\n * 异步工厂（产生任务用）\n *\n * @author ruoyi\n */\n@Slf4j\npublic class AsyncFactory {\n    private static final Logger sys_user_logger = LoggerFactory.getLogger(\"sys-user\");\n\n    /**\n     * 记录登录信息\n     *\n     * @param username 用户名\n     * @param status   状态\n     * @param message  消息\n     * @param args     列表\n     * @return 任务task\n     */\n    public static TimerTask recordLogininfor(final String username, final String status, final String message, final Object... args) {\n        final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader(\"User-Agent\"));\n        final String ip = IpUtils.getIpAddr(ServletUtils.getRequest());\n        return new TimerTask() {\n            @Override\n            public void run() {\n                String address = AddressUtils.getRealAddressByIP(ip);\n                StringBuilder s = new StringBuilder();\n                s.append(LogUtils.getBlock(ip));\n                s.append(address);\n                s.append(LogUtils.getBlock(username));\n                s.append(LogUtils.getBlock(status));\n                s.append(LogUtils.getBlock(message));\n                // 打印信息到日志\n                sys_user_logger.info(s.toString(), args);\n                // 获取客户端操作系统\n                String os = userAgent.getOperatingSystem().getName();\n                // 获取客户端浏览器\n                String browser = userAgent.getBrowser().getName();\n                // 封装对象\n                SysLogLogin logininfor = new SysLogLogin();\n                logininfor.setUserName(username);\n                logininfor.setIpaddr(ip);\n                logininfor.setLoginLocation(address);\n                logininfor.setBrowser(browser);\n                logininfor.setOs(os);\n                logininfor.setMsg(message);\n                logininfor.setCreateTime(new Date());\n                // 日志状态\n                if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) {\n                    logininfor.setStatus(Constants.SUCCESS);\n                } else if (Constants.LOGIN_FAIL.equals(status)) {\n                    logininfor.setStatus(Constants.FAIL);\n                }\n                // 保存到mongo\n                SpringUtils.getBean(MongoTemplate.class).save(logininfor);\n            }\n        };\n    }\n\n    /**\n     * 操作日志记录\n     *\n     * @param operLog 操作日志信息\n     * @return 任务task\n     */\n    public static TimerTask recordOper(final SysLogOperation operLog) {\n        return new TimerTask() {\n            @Override\n            public void run() {\n                // 远程查询操作地点\n                operLog.setOperationLocation(AddressUtils.getRealAddressByIP(operLog.getOperationIp()));\n                SpringUtils.getBean(MongoTemplate.class).save(operLog);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/properties/PermitAllUrlProperties.java",
    "content": "package ginyi.framework.security.properties;\n\nimport ginyi.common.annotation.Anonymous;\nimport org.apache.commons.lang3.RegExUtils;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.annotation.AnnotationUtils;\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.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.regex.Pattern;\n\n/**\n * 设置Anonymous注解允许匿名访问的url\n *\n * @author ruoyi\n */\n@Configuration\npublic class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware {\n\n    private static final Pattern PATTERN = Pattern.compile(\"\\\\{(.*?)\\\\}\");\n\n    private ApplicationContext applicationContext;\n\n    private List<String> urls = new ArrayList<>();\n\n    public String ASTERISK = \"*\";\n\n    @Override\n    public void afterPropertiesSet() {\n        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);\n        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();\n\n        map.keySet().forEach(info -> {\n            HandlerMethod handlerMethod = map.get(info);\n\n            // 获取方法上边的注解 替代path variable 为 *\n            Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);\n            Optional.ofNullable(method).ifPresent(anonymous -> info.getPatternsCondition().getPatterns()\n                    .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));\n\n            // 获取类上边的注解, 替代path variable 为 *\n            Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);\n            Optional.ofNullable(controller).ifPresent(anonymous -> info.getPatternsCondition().getPatterns()\n                    .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));\n        });\n    }\n\n    @Override\n    public void setApplicationContext(ApplicationContext context) throws BeansException {\n        this.applicationContext = context;\n    }\n\n    public List<String> getUrls() {\n        return urls;\n    }\n\n    public void setUrls(List<String> urls) {\n        this.urls = urls;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/ISysNoticeServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport ginyi.common.constant.CacheConstants;\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.exception.CommonException;\nimport ginyi.common.mysql.MyPage;\nimport ginyi.common.redis.cache.RedisCache;\nimport ginyi.common.result.StateCode;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.system.domain.LoginUser;\nimport ginyi.system.domain.SysNotice;\nimport ginyi.system.domain.SysUser;\nimport ginyi.system.domain.model.dto.NoticeDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.NoticeVo;\nimport ginyi.system.mapper.SysNoticeMapper;\nimport ginyi.system.mapper.SysUserMapper;\nimport ginyi.system.service.ISysNoticeService;\nimport ginyi.system.service.ITokenService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Slf4j\n@Service\npublic class ISysNoticeServiceImpl implements ISysNoticeService {\n\n    @Resource\n    private SysNoticeMapper noticeMapper;\n    @Resource\n    private SysUserMapper userMapper;\n    @Resource\n    private ITokenService tokenService;\n    @Resource\n    private HttpServletRequest request;\n    @Resource\n    private RedisCache redisCache;\n\n    /**\n     * 获取通知公告列表\n     */\n    @Override\n    public BaseVo<SysNotice> list(NoticeDto noticeDto, Long page, Long pageSize) {\n        IPage<SysNotice> list = noticeMapper.list(noticeDto, new MyPage(page, pageSize).getPage());\n        for (SysNotice notice : list.getRecords()) {\n            // 如果 userReadIds 是 null，则把它设置为[]\n            if (StringUtils.isNull(notice.getUserReadIds())) {\n                notice.setUserReadIds(new ArrayList<>());\n            }\n        }\n\n        BaseVo<SysNotice> baseVo = new BaseVo<>();\n        baseVo.setList(list.getRecords());\n        baseVo.setCount((int) list.getTotal());\n        return baseVo;\n    }\n\n    /**\n     * 获取用户通知公告列表\n     */\n    @Override\n    public BaseVo<NoticeVo> getUserNoticeList(Long page, Long pageSize) {\n        LoginUser loginUser = tokenService.getLoginUser(request);\n        Long userId = loginUser.getUserId();\n        IPage<SysNotice> list = noticeMapper.getUserNoticeList(userId, new MyPage(page, pageSize).getPage());\n\n        ArrayList<NoticeVo> noticeList = new ArrayList<>();\n        for (SysNotice notice : list.getRecords()) {\n            List<Long> userReadIds = notice.getUserReadIds();\n            NoticeVo vo = new NoticeVo();\n            BeanUtils.copyProperties(notice, vo);\n            // 判断是否已读\n            if (StringUtils.isNotNull(userReadIds)) {\n                vo.setHaveRead(userReadIds.contains(userId));\n            }\n            noticeList.add(vo);\n        }\n\n        BaseVo<NoticeVo> baseVo = new BaseVo<>();\n        baseVo.setList(noticeList);\n        baseVo.setCount((int) list.getTotal());\n        return baseVo;\n    }\n\n\n    /**\n     * 发布通知公告\n     */\n    @Override\n    public void add(NoticeDto noticeDto) {\n        List<SysUser> userList = userMapper.selectList(null);\n        if (noticeDto.getUserIds().size() > 0) {\n            for (Long userId : noticeDto.getUserIds()) {\n                boolean isExist = false;\n                for (SysUser user : userList) {\n                    // 判断 userId 是否合法\n                    if (userId.equals(user.getUserId())) {\n                        isExist = true;\n                        break;\n                    }\n                }\n                if (!isExist) {\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, userId + CommonMessageConstants.USER_NOT_EXIST);\n                }\n            }\n        } else {\n            throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.USER_IDS_ILLEGAL);\n        }\n\n        noticeMapper.addNotice(noticeDto);\n    }\n\n    /**\n     * 更新通知公告\n     *\n     * @param noticeDto\n     */\n    @Override\n    public void updateNotice(NoticeDto noticeDto) {\n        // 检查缓存中是否有标记空id\n        if (redisCache.hasKey(CacheConstants.NOTICE_NOT_EXIST_KEY + noticeDto.getNoticeId())) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, noticeDto.getNoticeId() + CommonMessageConstants.NOTICE_NOT_EXIST);\n        }\n        SysNotice notice = noticeMapper.selectOne(noticeDto.getNoticeId());\n        if (StringUtils.isNull(notice)) {\n            redisCache.setCacheObject(CacheConstants.NOTICE_NOT_EXIST_KEY + noticeDto.getNoticeId(), noticeDto.getNoticeId());\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, noticeDto.getNoticeId() + CommonMessageConstants.NOTICE_NOT_EXIST);\n        }\n\n        // 如果有人确认收到该通知公告，则不允许修改\n        if (StringUtils.isNotNull(notice.getUserReadIds())) {\n            throw new CommonException(StateCode.ERROR_PARAMS_SERVICE, CommonMessageConstants.NOTICE_NOT_ALLOW);\n        }\n        noticeMapper.updateNotice(noticeDto);\n\n    }\n\n    /**\n     * 确认收到通知公告\n     *\n     * @param noticeId\n     */\n    @Override\n    public void haveRead(Long noticeId) {\n        // 检查缓存中是否有标记空id\n        if (redisCache.hasKey(CacheConstants.NOTICE_NOT_EXIST_KEY + noticeId)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, noticeId + CommonMessageConstants.NOTICE_NOT_EXIST);\n        }\n\n        LambdaQueryWrapper<SysNotice> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysNotice::getNoticeId, noticeId);\n        SysNotice notice = noticeMapper.selectOne(noticeId);\n        if (StringUtils.isNull(notice)) {\n            redisCache.setCacheObject(CacheConstants.NOTICE_NOT_EXIST_KEY + noticeId, noticeId);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, noticeId + CommonMessageConstants.NOTICE_NOT_EXIST);\n        }\n\n        LoginUser loginUser = tokenService.getLoginUser(request);\n        Long userId = loginUser.getUserId();\n        List<Long> userReadIds = notice.getUserReadIds();\n        // 如果为 null 则初始化为 []\n        if (StringUtils.isNull(userReadIds)) {\n            userReadIds = new ArrayList<>();\n        }\n        // 判断该通知公告的用户列表中是否存在该用户\n        if (!notice.getUserIds().contains(userId)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.NOTICE_CURRENT_USER_NOT_EXIST);\n        }\n        // 判断是否已经确认收到该通知公告了\n        if (userReadIds.contains(userId)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.NOTICE_HAVE_READ);\n        }\n        userReadIds.add(userId);\n        notice.setUserReadIds(userReadIds);\n        noticeMapper.haveRead(notice);\n    }\n\n    /**\n     * 删除通知公告\n     *\n     * @param noticeId\n     */\n    @Override\n    public void remove(Long noticeId) {\n        // 检查缓存中是否有标记空id\n        if (redisCache.hasKey(CacheConstants.NOTICE_NOT_EXIST_KEY + noticeId)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, noticeId + CommonMessageConstants.NOTICE_NOT_EXIST);\n        }\n        SysNotice notice = noticeMapper.selectOne(noticeId);\n        if (StringUtils.isNull(notice)) {\n            redisCache.setCacheObject(CacheConstants.NOTICE_NOT_EXIST_KEY + noticeId, noticeId);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, noticeId + CommonMessageConstants.NOTICE_NOT_EXIST);\n        }\n        // 检查数据库中是否存在该通知公告\n        LambdaQueryWrapper<SysNotice> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysNotice::getNoticeId, noticeId);\n        noticeMapper.delete(queryWrapper);\n    }\n\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/MonitorServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport ginyi.common.constant.CacheConstants;\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.exception.CommonException;\nimport ginyi.common.mysql.MyPage;\nimport ginyi.common.redis.cache.RedisCache;\nimport ginyi.common.result.StateCode;\nimport ginyi.system.domain.LoginUser;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.SessionUserVo;\nimport ginyi.system.service.ISysOnlineService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Set;\n\n@Slf4j\n@Service\npublic class MonitorServiceImpl implements ISysOnlineService {\n\n    @Resource\n    private RedisCache redisCache;\n\n    @Override\n    public BaseVo<SessionUserVo> getOnlineUserList(Long page, Long pageSize) {\n        MyPage IPage = new MyPage(page, pageSize);\n        long skipCount = IPage.getPageSize() * (IPage.getPageNum() - 1);\n\n        BaseVo<SessionUserVo> baseVo = new BaseVo<>();\n        List<SessionUserVo> list = new LinkedList<>();\n        Set<String> keys = redisCache.getKeys(CacheConstants.LOGIN_TOKEN_KEY + \"*\");\n        LoginUser loginUser;\n        for (String key : keys) {\n            SessionUserVo sessionUserVo = new SessionUserVo();\n            loginUser = redisCache.getCacheObject(key, LoginUser.class);\n            BeanUtils.copyProperties(loginUser, sessionUserVo);\n            list.add(sessionUserVo);\n        }\n\n        long toIndex = Math.min(skipCount + IPage.getPageSize(), list.size());\n\n        if(skipCount > list.size()){\n            list = new LinkedList<>();\n        }else {\n            list = list.subList((int) skipCount, (int) toIndex);\n        }\n\n        baseVo.setList(list);\n        baseVo.setCount(keys.size());\n        return baseVo;\n    }\n\n    @Override\n    public void removeUser(String sessionId) {\n        if(!redisCache.hasKey(CacheConstants.LOGIN_TOKEN_KEY + sessionId)){\n            throw  new CommonException(StateCode.ERROR_NOT_EXIST, sessionId + CommonMessageConstants.USER_NOT_EXIST);\n        }\n        redisCache.removeCacheObject(CacheConstants.LOGIN_TOKEN_KEY + sessionId);\n    }\n\n    @Override\n    public void removeUser(Set<String> ids) {\n        for (String sessionId : ids) {\n            if(!redisCache.hasKey(CacheConstants.LOGIN_TOKEN_KEY + sessionId)){\n                throw  new CommonException(StateCode.ERROR_NOT_EXIST, sessionId + CommonMessageConstants.USER_NOT_EXIST);\n            }\n        }\n        // 全部没问题再执行\n        for (String sessionId : ids) {\n            redisCache.removeCacheObject(CacheConstants.LOGIN_TOKEN_KEY + sessionId);\n        }\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/PermissionService.java",
    "content": "package ginyi.framework.security.service;\n\nimport ginyi.common.exception.UnPermissionException;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.framework.security.context.PermissionContextHolder;\nimport ginyi.framework.security.utils.SecurityUtils;\nimport ginyi.system.domain.LoginUser;\nimport ginyi.system.domain.SysRole;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.Set;\n\n/**\n * RuoYi首创 自定义权限实现，ss取自SpringSecurity首字母\n *\n * @author ruoyi\n */\n@Service(\"ss\")\npublic class PermissionService {\n    /**\n     * 所有权限标识\n     */\n    private static final String ALL_PERMISSION = \"*:*:*\";\n\n    /**\n     * 管理员角色权限标识\n     */\n    private static final String SUPER_ADMIN = \"admin\";\n\n    private static final String ROLE_DELIMETER = \",\";\n\n    private static final String PERMISSION_DELIMETER = \",\";\n\n    /**\n     * 验证用户是否具备某权限\n     *\n     * @param permission 权限字符串\n     * @return 用户是否具备某权限\n     */\n    public boolean hasPermission(String permission) {\n        if (StringUtils.isEmpty(permission)) {\n            return false;\n        }\n        LoginUser loginUser = SecurityUtils.getLoginUser();\n        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {\n            return false;\n        }\n        PermissionContextHolder.setContext(permission);\n        return hasPermissions(loginUser.getPermissions(), permission);\n    }\n\n    /**\n     * 验证用户是否不具备某权限，与 hasPermi逻辑相反\n     *\n     * @param permission 权限字符串\n     * @return 用户是否不具备某权限\n     */\n    public boolean lacksPermi(String permission) {\n        return hasPermission(permission) != true;\n    }\n\n    /**\n     * 验证用户是否具有以下任意一个权限\n     *\n     * @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表\n     * @return 用户是否具有以下任意一个权限\n     */\n    public boolean hasAnyPermi(String permissions) {\n        if (StringUtils.isEmpty(permissions)) {\n            return false;\n        }\n        LoginUser loginUser = SecurityUtils.getLoginUser();\n        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {\n            return false;\n        }\n        PermissionContextHolder.setContext(permissions);\n        Set<String> authorities = loginUser.getPermissions();\n        for (String permission : permissions.split(PERMISSION_DELIMETER)) {\n            if (permission != null && hasPermissions(authorities, permission)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 判断用户是否拥有某个角色\n     *\n     * @param role 角色字符串\n     * @return 用户是否具备某角色\n     */\n    public boolean hasRole(String role) {\n        if (StringUtils.isEmpty(role)) {\n            return false;\n        }\n        LoginUser loginUser = SecurityUtils.getLoginUser();\n        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) {\n            return false;\n        }\n        for (SysRole sysRole : loginUser.getUser().getRoles()) {\n            String roleKey = sysRole.getRoleKey();\n            if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 验证用户是否不具备某角色，与 isRole逻辑相反。\n     *\n     * @param role 角色名称\n     * @return 用户是否不具备某角色\n     */\n    public boolean lacksRole(String role) {\n        return hasRole(role) != true;\n    }\n\n    /**\n     * 验证用户是否具有以下任意一个角色\n     *\n     * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表\n     * @return 用户是否具有以下任意一个角色\n     */\n    public boolean hasAnyRoles(String roles) {\n        if (StringUtils.isEmpty(roles)) {\n            return false;\n        }\n        LoginUser loginUser = SecurityUtils.getLoginUser();\n        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) {\n            return false;\n        }\n        for (String role : roles.split(ROLE_DELIMETER)) {\n            if (hasRole(role)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 判断是否包含权限\n     *\n     * @param permissions 权限列表\n     * @param permission  权限字符串\n     * @return 用户是否具备某权限\n     */\n    private boolean hasPermissions(Set<String> permissions, String permission) {\n        boolean isHasPermission = permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));\n        if (isHasPermission) {\n            return true;\n        } else {\n            throw new UnPermissionException();\n        }\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysConfigServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport ginyi.common.constant.CacheConstants;\nimport ginyi.common.redis.cache.RedisCache;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.common.utils.text.Convert;\nimport ginyi.system.domain.SysConfig;\nimport ginyi.system.mapper.SysConfigMapper;\nimport ginyi.system.service.ISysConfigService;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\n\n@Service\npublic class SysConfigServiceImpl implements ISysConfigService {\n\n    @Resource\n    private RedisCache redisCache;\n    @Resource\n    private SysConfigMapper configMapper;\n\n    /**\n     * 获取验证码开关\n     *\n     * @return true开启，false关闭\n     */\n    @Override\n    public boolean selectCaptchaEnabled() {\n        String captchaEnabled = 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 configKey 参数key\n     * @return 参数键值\n     */\n    @Override\n    public String selectConfigByKey(String configKey) {\n        String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey), String.class));\n        if (StringUtils.isNotEmpty(configValue)) {\n            return configValue;\n        }\n        LambdaQueryWrapper<SysConfig> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysConfig::getConfigKey, configKey);\n        SysConfig sysConfig = configMapper.selectOne(queryWrapper);\n        if (StringUtils.isNotNull(sysConfig)) {\n            redisCache.setCacheObject(getCacheKey(configKey), sysConfig.getConfigValue());\n            return sysConfig.getConfigValue();\n        }\n        return StringUtils.EMPTY;\n    }\n\n    /**\n     * 设置cache key\n     *\n     * @param configKey 参数键\n     * @return 缓存键key\n     */\n    private String getCacheKey(String configKey) {\n        return CacheConstants.SYS_CONFIG_KEY + configKey;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysDeptServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport ginyi.common.constant.CacheConstants;\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.exception.CommonException;\nimport ginyi.common.mysql.MyPage;\nimport ginyi.common.redis.cache.RedisCache;\nimport ginyi.common.result.StateCode;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.system.domain.SysDept;\nimport ginyi.system.domain.SysUser;\nimport ginyi.system.domain.model.dto.DeptDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.DeptVo;\nimport ginyi.system.mapper.SysDeptMapper;\nimport ginyi.system.service.ISysDeptService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n@Service\n@Slf4j\npublic class SysDeptServiceImpl implements ISysDeptService {\n\n    @Resource\n    private SysDeptMapper deptMapper;\n    @Resource\n    private RedisCache redisCache;\n\n    /**\n     * 获取部门列表\n     *\n     * @param deptDto\n     * @param page\n     * @param pageSize\n     */\n    @Override\n    public BaseVo<SysDept> list(DeptDto deptDto, Long page, Long pageSize) {\n        IPage<SysDept> list = deptMapper.list(deptDto, new MyPage(page, pageSize).getPage());\n        // 转成树\n        List<SysDept> deptList = list.getRecords().stream()\n                .filter(dept -> dept.getParentId().equals(0L))\n                .map(dept -> convertToDeptTree(dept, list.getRecords())).collect(Collectors.toList());\n        BaseVo<SysDept> baseVo = new BaseVo<>();\n        baseVo.setList(deptList);\n        baseVo.setCount(deptList.size());\n        return baseVo;\n    }\n\n    /**\n     * 根据部门id获取部门详情\n     *\n     * @param deptId\n     * @return\n     */\n    @Override\n    public DeptVo getDeptByDeptId(Long deptId) {\n        // 检查缓存中是否存在空id\n        if (redisCache.hasKey(CacheConstants.DEPT_NOT_EXIST_KEY + deptId)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, deptId + CommonMessageConstants.DEPT_NOT_EXIST);\n        }\n        DeptVo deptVo = new DeptVo();\n        // 检查缓存中是否存在\n        SysDept dept = redisCache.getCacheObject(CacheConstants.DEPT_DETAILS_BY_DEPTID_KEY + deptId, SysDept.class);\n        if (StringUtils.isNotNull(dept)) {\n            BeanUtils.copyProperties(dept, deptVo);\n            return deptVo;\n        }\n        LambdaQueryWrapper<SysDept> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysDept::getDeptId, deptId);\n        dept = deptMapper.selectOne(queryWrapper);\n        if (StringUtils.isNull(dept)) {\n            redisCache.setCacheObject(CacheConstants.DEPT_NOT_EXIST_KEY + deptId, null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, deptId + CommonMessageConstants.DEPT_NOT_EXIST);\n        }\n        redisCache.setCacheObject(CacheConstants.DEPT_DETAILS_BY_DEPTID_KEY + deptId, dept);\n        BeanUtils.copyProperties(dept, deptVo);\n        return deptVo;\n    }\n\n    /**\n     * 新增部门\n     *\n     * @param deptDto\n     */\n    @Override\n    public void addDept(DeptDto deptDto) {\n        Long parentId = StringUtils.isNull(deptDto.getParentId()) ? 0L : deptDto.getParentId();\n        // 检查缓存中是否有标记同个分支下名称有被使用\n        if (redisCache.hasKey(CacheConstants.DEPT_NAME_USED_KEY + parentId + deptDto.getDeptName())) {\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.DEPT_NAME_USED);\n        }\n        LambdaQueryWrapper<SysDept> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysDept::getParentId, parentId)\n                .eq(SysDept::getDeptName, deptDto.getDeptName());\n        SysDept dept = deptMapper.selectOne(queryWrapper);\n        // 检查同个分支下名称是否被使用\n        if (StringUtils.isNotNull(dept)) {\n            redisCache.setCacheObject(CacheConstants.DEPT_NAME_USED_KEY + parentId + deptDto.getDeptName(), null);\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.DEPT_NAME_USED);\n        }\n        deptMapper.insertDept(deptDto);\n        redisCache.removeCacheObject(CacheConstants.DEPT_KEY_PREFIX);\n    }\n\n    /**\n     * 更新部门\n     *\n     * @param deptDto\n     */\n    @Override\n    public void updateDept(DeptDto deptDto) {\n        // 检查缓存中是否有标记空id\n        if (redisCache.hasKey(CacheConstants.DEPT_NOT_EXIST_KEY + deptDto.getDeptId())) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, deptDto.getDeptId() + CommonMessageConstants.DEPT_NOT_EXIST);\n        }\n\n        LambdaQueryWrapper<SysDept> queryWrapper = new LambdaQueryWrapper<>();\n        Long parentId = StringUtils.isNull(deptDto.getParentId()) ? 0L : deptDto.getParentId();\n        queryWrapper.eq(parentId != 0L, SysDept::getDeptId, parentId);\n        List<SysDept> deptList = deptMapper.selectList(queryWrapper);\n        // 检查上级部门是否存在\n        if (deptList.size() == 0) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.DEPT_PARENT_NOT_EXIST);\n        }\n\n        queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysDept::getParentId, parentId)\n                .eq(SysDept::getDeptName, deptDto.getDeptName());\n        SysDept dept = deptMapper.selectOne(queryWrapper);\n        // 检查同个分支下名称是否被使用\n        if (StringUtils.isNotNull(dept)) {\n            redisCache.setCacheObject(CacheConstants.DEPT_NAME_USED_KEY + parentId + deptDto.getDeptName(), null);\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.DEPT_NAME_USED);\n        }\n\n        queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysDept::getDeptId, deptDto.getDeptId());\n        dept = deptMapper.selectOne(queryWrapper);\n        // 检查是否存在\n        if (StringUtils.isNull(dept)) {\n            redisCache.setCacheObject(CacheConstants.DEPT_NOT_EXIST_KEY + deptDto.getDeptId(), null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, deptDto.getDeptId() + CommonMessageConstants.DEPT_NOT_EXIST);\n        }\n        deptMapper.updateDept(deptDto);\n        redisCache.removeCacheObject(CacheConstants.DEPT_KEY_PREFIX);\n    }\n\n    /**\n     * 删除部门\n     *\n     * @param deptId\n     */\n    @Override\n    public void removeDeptById(Long deptId) {\n        // 检查缓存中是否标记着空id\n        if (redisCache.hasKey(CacheConstants.DEPT_NOT_EXIST_KEY + deptId)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, deptId + CommonMessageConstants.DEPT_NOT_EXIST);\n        }\n        LambdaQueryWrapper<SysDept> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysDept::getDeptId, deptId);\n        SysDept dept = deptMapper.selectOne(queryWrapper);\n        if (StringUtils.isNull(dept)) {\n            redisCache.setCacheObject(CacheConstants.DEPT_NOT_EXIST_KEY + deptId, null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, deptId + CommonMessageConstants.DEPT_NOT_EXIST);\n        }\n        deptMapper.deleteById(deptId);\n        redisCache.removeCacheObject(CacheConstants.DEPT_KEY_PREFIX);\n    }\n\n\n    /**\n     * 批量删除部门\n     *\n     * @param ids\n     */\n    @Override\n    public void removeDeptByIds(Set<Long> ids) {\n        if (ids.size() > 0) {\n            List<SysDept> deptList;\n            deptList = redisCache.getCacheList(CacheConstants.DEPT_LIST_KEY, SysDept.class);\n            if (StringUtils.isNull(deptList) || deptList.size() == 0) {\n                LambdaQueryWrapper<SysDept> queryWrapper = new LambdaQueryWrapper<>();\n                deptList = deptMapper.selectList(queryWrapper);\n                redisCache.setCacheList(CacheConstants.DEPT_LIST_KEY, deptList);\n            }\n            for (Long deptId : ids) {\n                // 检查缓存中是否标记着空id\n                if (redisCache.hasKey(CacheConstants.DEPT_NOT_EXIST_KEY + deptId)) {\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, deptId + CommonMessageConstants.DEPT_NOT_EXIST);\n                }\n                boolean exist = deptList.stream().anyMatch(dept -> deptId.equals(dept.getDeptId()));\n                if (!exist) {\n                    redisCache.setCacheObject(CacheConstants.DEPT_NOT_EXIST_KEY + deptId, null);\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, deptId + CommonMessageConstants.DEPT_NOT_EXIST);\n                }\n            }\n            deptMapper.deleteBatchIds(ids);\n            redisCache.removeCacheObject(CacheConstants.DEPT_KEY_PREFIX);\n        } else {\n            throw new CommonException(StateCode.ERROR_REQUEST_PARAMS, CommonMessageConstants.SYS_REQUEST_ILLEGAL);\n        }\n    }\n\n    /**\n     * 更新状态\n     * @param deptDto\n     */\n    @Override\n    public void updateStatus(DeptDto deptDto) {\n        if (StringUtils.isNull(deptDto.getDeptId())) {\n            throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.DEPT_ID_NOT_FOUND);\n        }\n        // 状态参数是否合法\n        if (!(\"0\".equals(deptDto.getStatus()) || \"1\".equals(deptDto.getStatus()))) {\n            throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.DEPT_STATUS_ILLEGAL);\n        }\n        // 检查缓存中是否标记着空id\n        if (redisCache.hasKey(CacheConstants.DEPT_NOT_EXIST_KEY + deptDto.getDeptId())) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, deptDto.getDeptId() + CommonMessageConstants.DEPT_NOT_EXIST);\n        }\n        LambdaQueryWrapper<SysDept> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysDept::getDeptId, deptDto.getDeptId());\n        SysDept dept = deptMapper.selectOne(queryWrapper);\n        if (StringUtils.isNull(dept)) {\n            redisCache.setCacheObject(CacheConstants.DEPT_NOT_EXIST_KEY + deptDto.getDeptId(), null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, deptDto.getDeptId() + CommonMessageConstants.DEPT_NOT_EXIST);\n        }\n        deptMapper.updateDeptStatus(deptDto);\n    }\n\n    /**\n     * 部门列表转换成部门树的结构\n     *\n     * @param dept\n     * @param list\n     * @return\n     */\n    public SysDept convertToDeptTree(SysDept dept, List<SysDept> list) {\n        SysDept sysDept = new SysDept();\n        BeanUtils.copyProperties(dept, sysDept);\n        List<SysDept> children = list.stream()\n                .filter(subDept -> dept.getDeptId().equals(subDept.getParentId()))\n                .map(subDept -> convertToDeptTree(subDept, list)).collect(Collectors.toList());\n        sysDept.setChildren(children);\n        return sysDept;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysLogServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport ginyi.common.mysql.MyPage;\nimport ginyi.system.domain.SysLogLogin;\nimport ginyi.system.domain.SysLogOperation;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.service.ISysLogService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.mongodb.core.MongoTemplate;\nimport org.springframework.data.mongodb.core.query.Query;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\n@Slf4j\n@Service\npublic class SysLogServiceImpl implements ISysLogService {\n\n    @Resource\n    private MongoTemplate mongoTemplate;\n\n\n    @Override\n    public BaseVo<SysLogLogin> getLoginLogList(Long page, Long pageSize) {\n        MyPage IPage = new MyPage(page, pageSize);\n        BaseVo<SysLogLogin> baseVo = new BaseVo<>();\n        Query query = new Query();\n\n        long count = mongoTemplate.count(query, SysLogLogin.class);\n\n        query.skip((int) (IPage.getPageSize() * (IPage.getPageNum() - 1)));\n        query.limit(IPage.getPageSize().intValue());\n        query.with(Sort.by(Sort.Order.desc(\"createTime\")));\n        List<SysLogLogin> list = mongoTemplate.find(query, SysLogLogin.class);\n\n        baseVo.setList(list);\n        baseVo.setCount((int) count);\n        return baseVo;\n    }\n\n    @Override\n    public BaseVo<SysLogOperation> getOperationLogList(Long page, Long pageSize) {\n        MyPage IPage = new MyPage(page, pageSize);\n        BaseVo<SysLogOperation> baseVo = new BaseVo<>();\n        Query query = new Query();\n\n        long count = mongoTemplate.count(query, SysLogOperation.class);\n\n        int skip = (int) (IPage.getPageSize() * (IPage.getPageNum() - 1));\n        query.skip(skip).limit(IPage.getPageSize().intValue());\n        query.with(Sort.by(Sort.Order.desc(\"createTime\")));\n        List<SysLogOperation> list = mongoTemplate.find(query, SysLogOperation.class);\n\n        baseVo.setList(list);\n        baseVo.setCount((int) count);\n        return baseVo;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysLoginLogServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport ginyi.system.domain.SysLogLogin;\nimport ginyi.system.service.ISysLogininforService;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n/**\n * 系统访问日志情况信息 服务层处理\n *\n * @author ruoyi\n */\n@Service\npublic class SysLoginLogServiceImpl implements ISysLogininforService {\n\n    /**\n     * 新增系统登录日志\n     *\n     * @param logininfor 访问日志对象\n     */\n    @Override\n    public void insertLogininfor(SysLogLogin logininfor) {\n        // todo 待写~\n    }\n\n    /**\n     * 查询系统登录日志集合\n     *\n     * @param logininfor 访问日志对象\n     * @return 登录记录集合\n     */\n    @Override\n    public List<SysLogLogin> selectLogininforList(SysLogLogin logininfor) {\n        // todo 待写~\n        return null;\n    }\n\n    /**\n     * 批量删除系统登录日志\n     *\n     * @param infoIds 需要删除的登录日志ID\n     * @return 结果\n     */\n    @Override\n    public int deleteLogininforByIds(Long[] infoIds) {\n        // todo 待写~\n        return 0;\n    }\n\n    /**\n     * 清空系统登录日志\n     */\n    @Override\n    public void cleanLogininfor() {\n        // todo 待写~\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysLoginServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.constant.UserConstants;\nimport ginyi.common.exception.CommonException;\nimport ginyi.common.exception.UserPasswordNotMatchException;\nimport ginyi.common.exception.UserPasswordRetryLimitExceedException;\nimport ginyi.common.result.StateCode;\nimport ginyi.common.utils.Constants;\nimport ginyi.common.utils.DateUtils;\nimport ginyi.common.utils.ServletUtils;\nimport ginyi.common.utils.ip.IpUtils;\nimport ginyi.framework.security.context.AuthenticationContextHolder;\nimport ginyi.framework.security.manager.AsyncManager;\nimport ginyi.framework.security.manager.factory.AsyncFactory;\nimport ginyi.framework.security.utils.SecurityUtils;\nimport ginyi.system.domain.LoginUser;\nimport ginyi.system.domain.SysUser;\nimport ginyi.system.domain.model.dto.LoginDto;\nimport ginyi.system.domain.model.dto.RegisterDto;\nimport ginyi.system.domain.model.dto.UserDto;\nimport ginyi.system.domain.model.vo.LoginVo;\nimport ginyi.system.service.*;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\n\n@Service\npublic class SysLoginServiceImpl implements ISysLoginService {\n\n    @Value(\"${token.prefix}\")\n    private String tokenPrefix;\n    @Value(\"${token.header}\")\n    private String tokenHeader;\n    @Resource\n    private ISysConfigService configService;\n    @Resource\n    private AuthenticationManager authenticationManager;\n    @Resource\n    private ISysUserService userService;\n    @Resource\n    private ITokenService tokenService;\n    @Resource\n    private IVerifyService verifyService;\n\n    /**\n     * 登录验证\n     *\n     * @param loginDto\n     * @return\n     */\n    @Override\n    public LoginVo login(LoginDto loginDto) {\n\n        String username = loginDto.getUsername();\n        String password = loginDto.getPassword();\n        String code = loginDto.getCode();\n\n        boolean captchaEnabled = configService.selectCaptchaEnabled();\n        // 图片验证码开关\n        if (captchaEnabled) {\n            verifyService.checkImgCode(code);\n        }\n        // 用户验证\n        Authentication authentication = null;\n        try {\n            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);\n            AuthenticationContextHolder.setContext(authenticationToken);\n            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername\n            authentication = authenticationManager.authenticate(authenticationToken);\n\n            // 登录成功\n            LoginUser loginUser = (LoginUser) authentication.getPrincipal();\n            recordLoginInfo(loginUser.getUserId());\n            String token = tokenService.createToken(loginUser);\n\n            // 生成token\n            LoginVo loginVo = new LoginVo();\n            loginVo.setToken(tokenPrefix + \" \" +token);\n            loginVo.setTokenHeader(tokenHeader);\n\n            // 记录日志\n            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, CommonMessageConstants.LOGIN_SUCCESS));\n            return loginVo;\n        } catch (Exception e) {\n            if (e instanceof AuthenticationException) {\n                // 账户被锁定\n                if (e.getCause() instanceof UserPasswordRetryLimitExceedException) {\n                    UserPasswordRetryLimitExceedException userPasswordRetryLimitExceedException = (UserPasswordRetryLimitExceedException) e.getCause();\n                    AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, CommonMessageConstants.USER_IS_LOCKED));\n                    throw new UserPasswordRetryLimitExceedException(\n                            userPasswordRetryLimitExceedException.getState(),\n                            userPasswordRetryLimitExceedException.getData()\n                    );\n                }\n                // 密码不匹配\n                if (e.getCause() instanceof UserPasswordNotMatchException) {\n                    AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, CommonMessageConstants.USER_PASSWORD_NOT_MATCH));\n                    throw new UserPasswordNotMatchException();\n                }\n                // 其他异常（被禁用、被删除、被停用）\n                if(e.getCause() instanceof CommonException){\n                    CommonException commonException = (CommonException) e.getCause();\n                    AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, String.valueOf(commonException.getData())));\n                    throw new CommonException(commonException.getState(), commonException.getData());\n                }\n            } else {\n                // 其他的未知异常\n                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));\n                throw new CommonException(StateCode.ERROR_SYSTEM);\n            }\n        } finally {\n            AuthenticationContextHolder.clearContext();\n        }\n        // 其他的未知异常\n        return null;\n    }\n\n\n    /**\n     * 用户注册\n     *\n     * @param registerDto\n     */\n    @Override\n    public void register(RegisterDto registerDto) {\n        // 图片验证码开关\n        boolean captchaEnabled = configService.selectCaptchaEnabled();\n        if (captchaEnabled) {\n            verifyService.checkImgCode(registerDto.getCode());\n        }\n\n        SysUser sysUser = new SysUser();\n        sysUser.setUserName(registerDto.getUsername());\n\n        // 当前注册的用户是否存在\n        if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(sysUser))) {\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.USER_EXIST);\n        } else {\n            sysUser.setNickName(registerDto.getUsername());\n            sysUser.setPassword(SecurityUtils.encryptPassword(registerDto.getPassword()));\n            boolean regFlag = userService.registerUser(sysUser);\n            if (!regFlag) {\n                throw new CommonException(StateCode.ERROR_SYSTEM);\n            } else {\n                AsyncManager.me().execute(AsyncFactory.recordLogininfor(registerDto.getUsername(), Constants.REGISTER, CommonMessageConstants.REGISTER_SUCCESS));\n            }\n        }\n    }\n\n\n    /**\n     * 记录登录信息\n     *\n     * @param userId 用户ID\n     */\n    public void recordLoginInfo(Long userId) {\n        UserDto userDto = new UserDto();\n        userDto.setUserId(userId);\n        userDto.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));\n        userDto.setLoginDate(DateUtils.getNowDate());\n        userService.updateUser(userDto);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysMenuServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport ginyi.common.constant.CacheConstants;\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.exception.CommonException;\nimport ginyi.common.redis.cache.RedisCache;\nimport ginyi.common.result.StateCode;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.framework.security.utils.SecurityUtils;\nimport ginyi.system.domain.LoginUser;\nimport ginyi.system.domain.SysMenu;\nimport ginyi.system.domain.SysUser;\nimport ginyi.system.domain.model.dto.MenuDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.mapper.SysMenuMapper;\nimport ginyi.system.service.ISysMenuService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport javax.annotation.Resource;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n/**\n * 菜单 业务层处理\n *\n * @author ruoyi\n */\n@Slf4j\n@Service\npublic class SysMenuServiceImpl implements ISysMenuService {\n\n    @Resource\n    private SysMenuMapper menuMapper;\n    @Resource\n    private RedisCache redisCache;\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 = menuMapper.selectMenuPermsByRoleId(roleId);\n        Set<String> permsSet = new HashSet<>();\n        for (String perm : perms) {\n            if (StringUtils.isNotEmpty(perm)) {\n                permsSet.addAll(Arrays.asList(perm.trim().split(\",\")));\n            }\n        }\n        return permsSet;\n    }\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 = menuMapper.selectMenuPermsByUserId(userId);\n        Set<String> permsSet = new HashSet<>();\n        for (String perm : perms) {\n            if (StringUtils.isNotEmpty(perm)) {\n                permsSet.addAll(Arrays.asList(perm.trim().split(\",\")));\n            }\n        }\n        return permsSet;\n    }\n\n    /**\n     * 根据用户查询系统菜单列表\n     *\n     * @return 菜单列表\n     */\n    @Override\n    public BaseVo<SysMenu> selectMenuList() {\n        List<SysMenu> menuList;\n        BaseVo<SysMenu> baseVo = new BaseVo<>();\n        LoginUser user = SecurityUtils.getLoginUser();\n        // 判断缓存是否有数据\n        menuList = redisCache.getCacheList(CacheConstants.MENU_USER_LIST_KEY + user.getUsername(), SysMenu.class);\n        if (menuList.size() > 0) {\n            baseVo.setList(menuList);\n            baseVo.setCount(menuList.size());\n            return baseVo;\n        }\n        boolean isAdmin = SysUser.isAdmin(user.getUserId());\n        // 管理员返回全部，普通用户则对应的菜单\n        LambdaQueryWrapper<SysMenu> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysMenu::getMenuType, \"M\").or().eq(SysMenu::getMenuType, \"C\");\n        List<SysMenu> list = isAdmin ? menuMapper.selectList(queryWrapper) : menuMapper.selectMenuListByUserId(user.getUserId());\n        // 转成树\n        menuList = list.stream()\n                .filter(menu -> menu.getParentId().equals(0L))\n                .map(menu -> convertToMenuTree(menu, list)).collect(Collectors.toList());\n        redisCache.setCacheList(CacheConstants.MENU_USER_LIST_KEY + user.getUsername(), menuList);\n\n        baseVo.setList(menuList);\n        baseVo.setCount(menuList.size());\n        return baseVo;\n    }\n\n    /**\n     * 管理员查询（管理）菜单列表\n     *\n     * @param menuDto\n     * @return\n     */\n    @Override\n    public BaseVo<SysMenu> list(MenuDto menuDto) {\n        List<SysMenu> list = menuMapper.selectMenuListByAdmin(menuDto);\n        List<SysMenu> menuList = list.stream()\n                .filter(menu -> menu.getParentId().equals(0L))\n                .map(menu -> convertToMenuTree(menu, list)).collect(Collectors.toList());\n        BaseVo<SysMenu> baseVo = new BaseVo<>();\n        baseVo.setList(menuList);\n        baseVo.setCount(menuList.size());\n        return baseVo;\n    }\n\n    /**\n     * 菜单列表转成树状\n     *\n     * @param menu\n     * @param list\n     * @return\n     */\n    public SysMenu convertToMenuTree(SysMenu menu, List<SysMenu> list) {\n        SysMenu sysMenu = new SysMenu();\n        BeanUtils.copyProperties(menu, sysMenu);\n        List<SysMenu> children = list.stream()\n                .filter(subMenu -> menu.getMenuId().equals(subMenu.getParentId()))\n                .map(subMenu -> convertToMenuTree(subMenu, list)).collect(Collectors.toList());\n        sysMenu.setChildren(children);\n        return sysMenu;\n    }\n\n    /**\n     * 添加菜单\n     *\n     * @param menuDto\n     */\n    @Override\n    public void addMenu(MenuDto menuDto) {\n        // c 是菜单，其余的是目录或者按钮\n        if (\"C\".equalsIgnoreCase(menuDto.getMenuType())) {\n            if (menuDto.getComponent().isEmpty()) {\n                throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.MENU_COMPONENT_NOT_EXIST);\n            }\n            if (menuDto.getPath().isEmpty()) {\n                throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.MENU_PATH_NOT_EXIST);\n            }\n        }\n\n        // 判断是否已存在\n        LambdaQueryWrapper<SysMenu> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysMenu::getMenuName, menuDto.getMenuName());\n        SysMenu result = menuMapper.selectOne(queryWrapper);\n        if (StringUtils.isNotNull(result)) {\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.MENU_NAME_USED);\n        }\n\n        menuMapper.insertMenu(menuDto);\n        // 清除menu的相关缓存\n        redisCache.removeCacheObject(CacheConstants.MENU_KEY_PREFIX);\n    }\n\n    /**\n     * 根据id获取菜单详情\n     *\n     * @param menuId\n     * @return\n     */\n    @Override\n    public SysMenu getMenuById(Long menuId) {\n        SysMenu menu;\n\n        // 判断是否是无效id\n        if (redisCache.hasKey(CacheConstants.MENU_NOT_EXIST_KEY + menuId)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.MENU_NOT_EXIST);\n        }\n        // 查看缓存中是否有\n        menu = redisCache.getCacheObject(CacheConstants.MENU_DETAILS_BY_ID_KEY + menuId, SysMenu.class);\n        if (StringUtils.isNotNull(menu)) {\n            return menu;\n        }\n\n        LambdaQueryWrapper<SysMenu> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysMenu::getMenuId, menuId);\n        menu = menuMapper.selectOne(queryWrapper);\n\n        if (StringUtils.isNull(menu)) {\n            // 存储不存在的key\n            redisCache.setCacheObject(CacheConstants.MENU_NOT_EXIST_KEY + menuId, null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.MENU_NOT_EXIST);\n        }\n        // 存入缓存\n        redisCache.setCacheObject(CacheConstants.MENU_DETAILS_BY_ID_KEY + menuId, menu);\n        return menu;\n    }\n\n    /**\n     * 更新菜单\n     *\n     * @param menuDto\n     */\n    @Override\n    public void updateMenu(MenuDto menuDto) {\n        // c 是菜单，其余的是目录或者按钮\n        if (\"C\".equalsIgnoreCase(menuDto.getMenuType())) {\n            if (StringUtils.isNull(menuDto.getComponent()) || menuDto.getComponent().isEmpty()) {\n                throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.MENU_COMPONENT_NOT_EXIST);\n            }\n            if (StringUtils.isNull(menuDto.getPath()) || menuDto.getPath().isEmpty()) {\n                throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.MENU_PATH_NOT_EXIST);\n            }\n        }\n\n        // 判断menuId是否存在\n        LambdaQueryWrapper<SysMenu> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysMenu::getMenuId, menuDto.getMenuId());\n        SysMenu result = menuMapper.selectOne(queryWrapper);\n        if (StringUtils.isNull(result)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.MENU_NOT_EXIST);\n        }\n\n        // 判断上级菜单是否存在\n        Long parentId = StringUtils.isNull(menuDto.getParentId()) ? 0L : menuDto.getParentId();\n        queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(parentId != 0L, SysMenu::getMenuId, parentId);\n        List<SysMenu> menuList = menuMapper.selectList(queryWrapper);\n        if (menuList.size() == 0) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.MENU_PARENT_NOT_EXIST);\n        }\n\n        menuMapper.updateMenu(menuDto);\n        // 清除menu的相关缓存\n        redisCache.removeCacheObject(CacheConstants.MENU_KEY_PREFIX);\n    }\n\n    /**\n     * 根据菜单id删除\n     *\n     * @param menuId\n     */\n    @Override\n    public void removeMenuById(Long menuId) {\n        // 缓存中是否标记空id\n        if (redisCache.hasKey(CacheConstants.MENU_NOT_EXIST_KEY + menuId)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, menuId + CommonMessageConstants.MENU_NOT_EXIST);\n        }\n\n        LambdaQueryWrapper<SysMenu> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysMenu::getMenuId, menuId);\n        SysMenu menu = menuMapper.selectOne(queryWrapper);\n\n        // 数据不存在\n        if (StringUtils.isNull(menu)) {\n            redisCache.setCacheObject(CacheConstants.MENU_NOT_EXIST_KEY + menuId, null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, menuId + CommonMessageConstants.MENU_NOT_EXIST);\n        }\n\n        menuMapper.deleteById(menuId);\n        redisCache.removeCacheObject(CacheConstants.MENU_KEY_PREFIX);\n    }\n\n    /**\n     * 根据ids批量删除菜单\n     *\n     * @param ids\n     */\n    @Override\n    @Transactional\n    public void removeMenuByIds(Set<Long> ids) {\n        if (ids.size() > 0) {\n            List<SysMenu> menuList;\n            menuList = redisCache.getCacheList(CacheConstants.MENU_LIST_KEY, SysMenu.class);\n            if (StringUtils.isNull(menuList) || menuList.size() == 0) {\n                LambdaQueryWrapper<SysMenu> queryWrapper = new LambdaQueryWrapper<>();\n                menuList = menuMapper.selectList(queryWrapper);\n            }\n            for (Long menuId : ids) {\n                // 缓存中是否标记空id\n                if (redisCache.hasKey(CacheConstants.MENU_NOT_EXIST_KEY + menuId)) {\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, menuId + CommonMessageConstants.MENU_NOT_EXIST);\n                }\n                boolean exist = menuList.stream().anyMatch(menu -> menuId.equals(menu.getMenuId()));\n                if (!exist) {\n                    redisCache.setCacheObject(CacheConstants.MENU_NOT_EXIST_KEY + menuId, null);\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, menuId + CommonMessageConstants.MENU_NOT_EXIST);\n                }\n            }\n            menuMapper.deleteBatchIds(ids);\n            redisCache.removeCacheObject(CacheConstants.MENU_KEY_PREFIX);\n        } else {\n            throw new CommonException(StateCode.ERROR_REQUEST_PARAMS, CommonMessageConstants.SYS_REQUEST_ILLEGAL);\n        }\n    }\n\n    /**\n     * 更新状态\n     * @param menuDto\n     */\n    @Override\n    public void updateStatus(MenuDto menuDto) {\n        if (StringUtils.isNull(menuDto.getMenuId())) {\n            throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.MENU_ID_NOT_FOUND);\n        }\n        // 状态参数是否合法\n        if (!(\"0\".equals(menuDto.getStatus()) || \"1\".equals(menuDto.getStatus()))) {\n            throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.MENU_STATUS_ILLEGAL);\n        }\n        // 检查缓存中是否标记着空id\n        if (redisCache.hasKey(CacheConstants.MENU_NOT_EXIST_KEY + menuDto.getMenuId())) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, menuDto.getMenuId() + CommonMessageConstants.MENU_NOT_EXIST);\n        }\n        LambdaQueryWrapper<SysMenu> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysMenu::getMenuId, menuDto.getMenuId());\n        SysMenu menu = menuMapper.selectOne(queryWrapper);\n        if (StringUtils.isNull(menu)) {\n            redisCache.setCacheObject(CacheConstants.MENU_NOT_EXIST_KEY + menuDto.getMenuId(), null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, menuDto.getMenuId() + CommonMessageConstants.MENU_NOT_EXIST);\n        }\n        menuMapper.updateMenuStatus(menuDto);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysMonitorServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.exception.CommonException;\nimport ginyi.common.redis.cache.RedisCache;\nimport ginyi.common.result.StateCode;\nimport ginyi.common.utils.DateUtils;\nimport ginyi.common.utils.NumberUtils;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.common.utils.ip.IpUtils;\nimport ginyi.system.domain.*;\nimport ginyi.system.domain.model.dto.CacheDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.CacheKeyVo;\nimport ginyi.system.domain.model.vo.CacheVo;\nimport ginyi.system.service.ISysMonitorService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.data.redis.connection.DataType;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.stereotype.Service;\nimport oshi.SystemInfo;\nimport oshi.hardware.CentralProcessor;\nimport oshi.hardware.HardwareAbstractionLayer;\nimport oshi.software.os.FileSystem;\nimport oshi.software.os.OSFileStore;\n\nimport javax.annotation.Resource;\nimport java.lang.management.ManagementFactory;\nimport java.util.*;\n\n@Slf4j\n@Service\npublic class SysMonitorServiceImpl implements ISysMonitorService {\n\n    @Resource\n    private RedisCache redisCache;\n    @Resource\n    private RedisTemplate redisTemplate;\n\n    /**\n     * 获取系统服务信息\n     */\n    @Override\n    public SysServer getServerInfo() throws InterruptedException {\n        SysServer server = new SysServer();\n\n        SystemInfo systemInfo = new SystemInfo();\n        HardwareAbstractionLayer hardware = systemInfo.getHardware();\n\n        // CPU信息\n        long[] prevTicks = hardware.getProcessor().getSystemCpuLoadTicks();\n        Thread.sleep(1000);\n        long[] ticks = hardware.getProcessor().getSystemCpuLoadTicks();\n        long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];\n        long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];\n        long softIrq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];\n        long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];\n        long SysRate = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];\n        long userRate = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];\n        long waitRate = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];\n        long IdleRate = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];\n        long totalCpu = userRate + nice + SysRate + IdleRate + waitRate + irq + softIrq + steal;\n        SysCpu sysCpu = new SysCpu();\n        sysCpu.setCpuNum(hardware.getProcessor().getLogicalProcessorCount());\n        sysCpu.setTotal(totalCpu);\n        sysCpu.setSys(NumberUtils.round(((double) SysRate / totalCpu) * 100, 2));\n        sysCpu.setUsed(NumberUtils.round(((double) userRate / totalCpu) * 100, 2));\n        sysCpu.setWait(NumberUtils.round(((double) waitRate / totalCpu) * 100, 2));\n        sysCpu.setFree(NumberUtils.round(((double) IdleRate / totalCpu) * 100, 2));\n\n\n        // 内存\n        SysMemory sysMemory = new SysMemory();\n        sysMemory.setTotal(NumberUtils.round((double) hardware.getMemory().getTotal() / (1024 * 1024 * 1024), 2));\n        sysMemory.setUsed(NumberUtils.round((double) (hardware.getMemory().getTotal() - hardware.getMemory().getAvailable()) / (1024 * 1024 * 1024), 2));\n        sysMemory.setFree(NumberUtils.round((double) hardware.getMemory().getAvailable() / (1024 * 1024 * 1024), 2));\n        sysMemory.setUsage(NumberUtils.round((sysMemory.getUsed() / sysMemory.getTotal()) * 100, 2));\n\n\n        // Java虚拟机\n        Properties properties = System.getProperties();\n        SysJvm sysJvm = new SysJvm();\n        sysJvm.setTotal(NumberUtils.round((double) Runtime.getRuntime().totalMemory() / (1024 * 1024), 2));\n        sysJvm.setMax(NumberUtils.round((double) Runtime.getRuntime().maxMemory() / (1024 * 1024), 2));\n        sysJvm.setFree(NumberUtils.round((double) Runtime.getRuntime().freeMemory() / (1024 * 1024), 2));\n        sysJvm.setUsed(NumberUtils.round(sysJvm.getTotal() - sysJvm.getFree(), 2));\n        sysJvm.setUsage(NumberUtils.round(((sysJvm.getTotal() - sysJvm.getFree()) / sysJvm.getTotal()) * 100, 2));\n        sysJvm.setInputArgs(ManagementFactory.getRuntimeMXBean().getInputArguments().toString());\n        sysJvm.setName(ManagementFactory.getRuntimeMXBean().getVmName());\n        sysJvm.setRunTime(DateUtils.getDatePoor(DateUtils.getNowDate(), DateUtils.getServerStartDate()));\n        sysJvm.setStartTime(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate()));\n        sysJvm.setVersion(properties.getProperty(\"java.version\"));\n        sysJvm.setHome(properties.getProperty(\"java.home\"));\n\n\n        // 服务器信息\n        Sys sys = new Sys();\n        sys.setComputerName(IpUtils.getHostName());\n        sys.setComputerIp(IpUtils.getHostIp());\n        sys.setOsName(properties.getProperty(\"os.name\"));\n        sys.setOsArch(properties.getProperty(\"os.arch\"));\n        sys.setUserDir(properties.getProperty(\"user.dir\"));\n\n\n        // 磁盘\n        FileSystem fileSystem = systemInfo.getOperatingSystem().getFileSystem();\n        List<OSFileStore> fsArray = fileSystem.getFileStores();\n        List<SysFile> sysFiles = new LinkedList<>();\n        for (OSFileStore fs : fsArray) {\n            long free = fs.getUsableSpace();\n            long total = fs.getTotalSpace();\n            long used = total - free;\n            SysFile sysFile = new SysFile();\n            sysFile.setDirName(fs.getMount());\n            sysFile.setTypeName(fs.getName());\n            sysFile.setTotal(NumberUtils.round((double) total / (1024 * 1024 * 1024), 2));\n            sysFile.setFree(NumberUtils.round((double) free / (1024 * 1024 * 1024), 2));\n            sysFile.setUsed(NumberUtils.round((double) used / (1024 * 1024 * 1024), 2));\n            sysFile.setUsage(NumberUtils.round(((double) used / total) * 100, 2));\n            sysFiles.add(sysFile);\n        }\n\n        server.setCpu(sysCpu);\n        server.setMemory(sysMemory);\n        server.setJvm(sysJvm);\n        server.setSys(sys);\n        server.setFile(sysFiles);\n        return server;\n    }\n\n    /**\n     * 获取缓存列表\n     */\n    @Override\n    public BaseVo<CacheKeyVo> getCacheList() {\n        BaseVo<CacheKeyVo> baseVo = new BaseVo<>();\n        // 获取所有key\n        Set<String> keys = redisCache.getKeys(\"*\");\n        ArrayList<CacheKeyVo> list = new ArrayList<>();\n\n        for (String key : keys) {\n            CacheKeyVo keyVo = new CacheKeyVo();\n            DataType type = redisCache.getType(key);\n            keyVo.setKey(key);\n            keyVo.setType(type.code());\n            list.add(keyVo);\n        }\n\n        // 排序\n        list.sort(Comparator.comparing(CacheKeyVo::getKey));\n        baseVo.setList(list);\n        baseVo.setCount(keys.size());\n        return baseVo;\n    }\n\n    /**\n     * 获取缓存详情\n     */\n    @Override\n    public CacheVo getCacheDetails(CacheDto cacheDto) {\n\n        if (!redisCache.hasKey(cacheDto.getKey())) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.REDIS_KEY_NOT_EXIST);\n        }\n\n        Object object = null;\n        try {\n            if (\"string\".equalsIgnoreCase(cacheDto.getType())) {\n                object = redisCache.getCacheObject(cacheDto.getKey(), Object.class);\n            }\n            if (\"list\".equalsIgnoreCase(cacheDto.getType())) {\n                object = redisCache.getCacheList(cacheDto.getKey(), Object.class);\n            }\n        } catch (Exception e) {\n            throw new CommonException(StateCode.ERROR_BUSINESS, CommonMessageConstants.REDIS_VALUE_TYPE_NOT_MATCH);\n        }\n\n        if (StringUtils.isNull(object)) {\n            throw new CommonException(StateCode.ERROR_BUSINESS, CommonMessageConstants.REDIS_VALUE_TYPE_NOT_MATCH);\n        }\n\n        CacheVo cacheVo = new CacheVo();\n        cacheVo.setKey(cacheDto.getKey());\n        cacheVo.setExpire(redisCache.getExpire(cacheDto.getKey()));\n        cacheVo.setValue(object);\n        return cacheVo;\n    }\n\n    /**\n     * 删除缓存\n     */\n    @Override\n    public void removeCache(String key) {\n        if (!redisCache.hasKey(key)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.REDIS_KEY_NOT_EXIST);\n        }\n        redisCache.removeCacheObject(key);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysOperationLogServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport ginyi.system.domain.SysLogOperation;\nimport ginyi.system.service.ISysOperLogService;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n/**\n * 操作日志 服务层处理\n *\n * @author ruoyi\n */\n@Service\npublic class SysOperationLogServiceImpl implements ISysOperLogService {\n\n    /**\n     * 新增操作日志\n     *\n     * @param operLog 操作日志对象\n     */\n    @Override\n    public void insertOperlog(SysLogOperation operLog) {\n        // todo 待写~\n    }\n\n    /**\n     * 查询系统操作日志集合\n     *\n     * @param operLog 操作日志对象\n     * @return 操作日志集合\n     */\n    @Override\n    public List<SysLogOperation> selectOperLogList(SysLogOperation operLog) {\n        // todo 待写~\n        return null;\n    }\n\n    /**\n     * 批量删除系统操作日志\n     *\n     * @param operIds 需要删除的操作日志ID\n     * @return 结果\n     */\n    @Override\n    public int deleteOperLogByIds(Long[] operIds) {\n        // todo 待写~\n        return 0;\n    }\n\n    /**\n     * 查询操作日志详细\n     *\n     * @param operId 操作ID\n     * @return 操作日志对象\n     */\n    @Override\n    public SysLogOperation selectOperLogById(Long operId) {\n        // todo 待写~\n        return null;\n    }\n\n    /**\n     * 清空操作日志\n     */\n    @Override\n    public void cleanOperLog() {\n        // todo 待写~\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysPasswordServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport ginyi.common.constant.CacheConstants;\nimport ginyi.common.exception.UserPasswordNotMatchException;\nimport ginyi.common.exception.UserPasswordRetryLimitExceedException;\nimport ginyi.common.redis.cache.RedisCache;\nimport ginyi.common.result.StateCode;\nimport ginyi.framework.security.context.AuthenticationContextHolder;\nimport ginyi.framework.security.utils.SecurityUtils;\nimport ginyi.system.domain.SysUser;\nimport ginyi.system.service.ISysPasswordService;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.util.concurrent.TimeUnit;\n\n@Service\npublic class SysPasswordServiceImpl implements ISysPasswordService {\n\n    @Resource\n    private RedisCache redisCache;\n    @Value(value = \"${user.password.maxRetryCount}\")\n    private int maxRetryCount;\n    @Value(value = \"${user.password.lockTime}\")\n    private int lockTime;\n\n\n    public void validate(SysUser user) {\n        Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext();\n        String username = usernamePasswordAuthenticationToken.getName();\n        String password = usernamePasswordAuthenticationToken.getCredentials().toString();\n\n        Integer retryCount = redisCache.getCacheObject(getCacheKey(username), Integer.class);\n\n        if (retryCount == null) {\n            retryCount = 0;\n        }\n\n        // 错误输入次数大于上限\n        if (retryCount >= maxRetryCount) {\n            StringBuilder errorMessage = new StringBuilder();\n            errorMessage.append(\"密码输入错误\").append(maxRetryCount).append(\"次，帐号被系统锁定\").append(lockTime).append(\"分钟，请稍后再试！\");\n            throw new UserPasswordRetryLimitExceedException(StateCode.ERROR_UNAUTHENTICATION, errorMessage);\n        }\n        // 密码不匹配\n        if (!matches(user, password)) {\n            retryCount = retryCount + 1;\n            redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES);\n            throw new UserPasswordNotMatchException();\n        } else {\n            clearLoginRecordCache(username);\n        }\n    }\n\n    public boolean matches(SysUser user, String rawPassword) {\n        return SecurityUtils.matchesPassword(rawPassword, user.getPassword());\n    }\n\n    /**\n     * 登录账户密码错误次数缓存键名\n     *\n     * @param username 用户名\n     * @return 缓存键key\n     */\n    private String getCacheKey(String username) {\n        return CacheConstants.PWD_ERR_CNT_KEY + username;\n    }\n\n    public void clearLoginRecordCache(String loginName) {\n        if (redisCache.hasKey(getCacheKey(loginName))) {\n            redisCache.removeCacheObject(getCacheKey(loginName));\n        }\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysPermissionServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport ginyi.system.domain.SysRole;\nimport ginyi.system.domain.SysUser;\nimport ginyi.system.service.ISysMenuService;\nimport ginyi.system.service.ISysPermissionService;\nimport ginyi.system.service.ISysRoleService;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\n@Service\npublic class SysPermissionServiceImpl implements ISysPermissionService {\n\n    @Resource\n    private ISysRoleService roleService;\n    @Resource\n    private ISysMenuService menuService;\n\n    /**\n     * 获取角色数据权限\n     *\n     * @param user 用户信息\n     * @return 角色权限信息\n     */\n    @Override\n    public Set<String> getRolePermission(SysUser user) {\n        Set<String> roles = new HashSet<String>();\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    @Override\n    public Set<String> getMenuPermission(SysUser user) {\n        Set<String> perms = new HashSet<String>();\n        // 管理员拥有所有权限\n        if (user.isAdmin()) {\n            perms.add(\"*:*:*\");\n        } else {\n            List<SysRole> roles = user.getRoles();\n            if (!roles.isEmpty() && roles.size() > 1) {\n                // 多角色设置permissions属性，以便数据权限匹配权限\n                for (SysRole role : roles) {\n                    Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId());\n                    role.setPermissions(rolePerms);\n                    perms.addAll(rolePerms);\n                }\n            } else {\n                perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId()));\n            }\n        }\n        return perms;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysPostServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport ginyi.common.constant.CacheConstants;\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.exception.CommonException;\nimport ginyi.common.mysql.MyPage;\nimport ginyi.common.redis.cache.RedisCache;\nimport ginyi.common.result.StateCode;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.system.domain.SysMenu;\nimport ginyi.system.domain.SysPost;\nimport ginyi.system.domain.model.dto.PostDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.PostVo;\nimport ginyi.system.mapper.SysPostMapper;\nimport ginyi.system.service.ISysPostService;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.util.List;\nimport java.util.Set;\n\n@Service\npublic class SysPostServiceImpl implements ISysPostService {\n\n    @Resource\n    private SysPostMapper postMapper;\n    @Resource\n    private RedisCache redisCache;\n\n    /**\n     * 查询岗位列表\n     *\n     * @param postDto\n     * @param page\n     * @param pageSize\n     * @return\n     */\n    @Override\n    public BaseVo<PostVo> list(PostDto postDto, Long page, Long pageSize) {\n        IPage<PostVo> list = postMapper.list(postDto, new MyPage(page, pageSize).getPage());\n        BaseVo<PostVo> baseVo = new BaseVo<>();\n        baseVo.setList(list.getRecords());\n        baseVo.setCount((int) list.getTotal());\n        return baseVo;\n    }\n\n    /**\n     * 获取岗位详情\n     *\n     * @param postId\n     * @return\n     */\n    @Override\n    public PostVo getPostByPostId(Long postId) {\n        // 检查缓存中是否存在空id\n        if (redisCache.hasKey(CacheConstants.POST_NOT_EXIST_KEY + postId)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST);\n        }\n        PostVo postVo = new PostVo();\n        // 检查缓存中是否存在\n        SysPost post = redisCache.getCacheObject(CacheConstants.POST_DETAILS_BY_POSTID_KEY + postId, SysPost.class);\n        if (StringUtils.isNotNull(post)) {\n            BeanUtils.copyProperties(post, postVo);\n            return postVo;\n        }\n        LambdaQueryWrapper<SysPost> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysPost::getPostId, postId);\n        post = postMapper.selectOne(queryWrapper);\n        if (StringUtils.isNull(post)) {\n            redisCache.setCacheObject(CacheConstants.POST_NOT_EXIST_KEY + postId, null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST);\n        }\n        redisCache.setCacheObject(CacheConstants.POST_DETAILS_BY_POSTID_KEY + postId, post);\n        BeanUtils.copyProperties(post, postVo);\n        return postVo;\n    }\n\n    /**\n     * 添加岗位\n     *\n     * @param postDto\n     */\n    @Override\n    public void addPost(PostDto postDto) {\n        if (redisCache.hasKey(CacheConstants.ROLE_NAME_USED_KEY + postDto.getPostName())) {\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.POST_NAME_USED);\n        }\n        if (redisCache.hasKey(CacheConstants.ROLE_CODE_USED_KEY + postDto.getPostCode())) {\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.POST_CODE_USED);\n        }\n        // 检查名称是否被使用\n        LambdaQueryWrapper<SysPost> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysPost::getPostName, postDto.getPostName());\n        SysPost post = postMapper.selectOne(queryWrapper);\n        if (StringUtils.isNotNull(post)) {\n            redisCache.setCacheObject(CacheConstants.ROLE_NAME_USED_KEY + postDto.getPostName(), null);\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.POST_NAME_USED);\n        }\n        // 检查编码是否被使用\n        queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysPost::getPostCode, postDto.getPostCode());\n        post = postMapper.selectOne(queryWrapper);\n        if (StringUtils.isNotNull(post)) {\n            redisCache.setCacheObject(CacheConstants.ROLE_CODE_USED_KEY + postDto.getPostCode(), null);\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.POST_CODE_USED);\n        }\n        postMapper.insertPost(postDto);\n        redisCache.removeCacheObject(CacheConstants.POST_KEY_PREFIX);\n        redisCache.removeCacheObject(CacheConstants.ROLE_KEY_PREFIX);\n    }\n\n    /**\n     * 更新岗位\n     *\n     * @param postDto\n     */\n    @Override\n    public void updatePost(PostDto postDto) {\n        // 检查缓存中是否存在空id\n        if (redisCache.hasKey(CacheConstants.POST_NOT_EXIST_KEY + postDto.getPostId())) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, postDto.getPostId() + CommonMessageConstants.POST_NOT_EXIST);\n        }\n        LambdaQueryWrapper<SysPost> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysPost::getPostId, postDto.getPostId());\n        SysPost post = postMapper.selectOne(queryWrapper);\n        if (StringUtils.isNull(post)) {\n            redisCache.setCacheObject(CacheConstants.POST_NOT_EXIST_KEY + postDto.getPostId(), null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, postDto.getPostId() + CommonMessageConstants.POST_NOT_EXIST);\n        }\n        // 检查名称是否被使用\n        queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysPost::getPostName, postDto.getPostName());\n        post = postMapper.selectOne(queryWrapper);\n        if (StringUtils.isNotNull(post) && !post.getPostId().equals(postDto.getPostId())) {\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.POST_NAME_USED);\n        }\n        // 检查编码是否被使用\n        queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysPost::getPostCode, postDto.getPostCode());\n        post = postMapper.selectOne(queryWrapper);\n        if (StringUtils.isNotNull(post) && !post.getPostId().equals(postDto.getPostId())) {\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.POST_CODE_USED);\n        }\n        postMapper.updatePost(postDto);\n        redisCache.removeCacheObject(CacheConstants.POST_KEY_PREFIX);\n    }\n\n    /**\n     * 删除岗位\n     *\n     * @param postId\n     */\n    @Override\n    public void removePostById(Long postId) {\n        // 查看缓存中是否存在空id\n        if (redisCache.hasKey(CacheConstants.POST_NOT_EXIST_KEY + postId)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST);\n        }\n        LambdaQueryWrapper<SysPost> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysPost::getPostId, postId);\n        SysPost post = postMapper.selectOne(queryWrapper);\n        if (StringUtils.isNull(post)) {\n            redisCache.setCacheObject(CacheConstants.POST_NOT_EXIST_KEY + postId, null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST);\n        }\n        postMapper.deleteById(postId);\n        redisCache.removeCacheObject(CacheConstants.POST_KEY_PREFIX);\n    }\n\n    /**\n     * 批量删除岗位\n     *\n     * @param ids\n     */\n    @Override\n    public void removeDeptByIds(Set<Long> ids) {\n        if (ids.size() > 0) {\n            List<SysPost> postList;\n            postList = redisCache.getCacheList(CacheConstants.POST_LIST_KEY, SysPost.class);\n            if (StringUtils.isNull(postList) || postList.size() == 0) {\n                LambdaQueryWrapper<SysPost> queryWrapper = new LambdaQueryWrapper<>();\n                postList = postMapper.selectList(queryWrapper);\n                redisCache.setCacheList(CacheConstants.POST_LIST_KEY, postList);\n            }\n            for (Long postId : ids) {\n                // 检查缓存中是否有标记着空id\n                if (redisCache.hasKey(CacheConstants.POST_NOT_EXIST_KEY + postId)) {\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST);\n                }\n                boolean exist = postList.stream().anyMatch(post -> postId.equals(post.getPostId()));\n                if (!exist) {\n                    redisCache.setCacheObject(CacheConstants.POST_NOT_EXIST_KEY + postId, null);\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST);\n                }\n            }\n            postMapper.deleteBatchIds(ids);\n            redisCache.removeCacheObject(CacheConstants.POST_KEY_PREFIX);\n        } else {\n            throw new CommonException(StateCode.ERROR_REQUEST_PARAMS, CommonMessageConstants.SYS_REQUEST_ILLEGAL);\n        }\n    }\n\n    /**\n     * 更新状态\n     * @param postDto\n     */\n    @Override\n    public void updateStatus(PostDto postDto) {\n        if (StringUtils.isNull(postDto.getPostId())) {\n            throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.POST_ID_NOT_FOUND);\n        }\n        // 状态参数是否合法\n        if (!(\"0\".equals(postDto.getStatus()) || \"1\".equals(postDto.getStatus()))) {\n            throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.POST_STATUS_ILLEGAL);\n        }\n        // 检查缓存中是否标记着空id\n        if (redisCache.hasKey(CacheConstants.USER_NOT_EXIST_KEY + postDto.getPostId())) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, postDto.getPostId() + CommonMessageConstants.POST_NOT_EXIST);\n        }\n        LambdaQueryWrapper<SysPost> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysPost::getPostId, postDto.getPostId());\n        SysPost post = postMapper.selectOne(queryWrapper);\n        if (StringUtils.isNull(post)) {\n            redisCache.setCacheObject(CacheConstants.POST_NOT_EXIST_KEY + postDto.getPostId(), null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, postDto.getPostId() + CommonMessageConstants.POST_NOT_EXIST);\n        }\n        postMapper.updatePostStatus(postDto);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysRoleServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport ginyi.common.constant.CacheConstants;\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.exception.CommonException;\nimport ginyi.common.mysql.MyPage;\nimport ginyi.common.redis.cache.RedisCache;\nimport ginyi.common.result.StateCode;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.system.domain.SysMenu;\nimport ginyi.system.domain.SysRole;\nimport ginyi.system.domain.model.dto.RoleDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.RoleVo;\nimport ginyi.system.mapper.SysMenuMapper;\nimport ginyi.system.mapper.SysRoleMapper;\nimport ginyi.system.service.ISysRoleService;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport javax.annotation.Resource;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\n@Service\npublic class SysRoleServiceImpl implements ISysRoleService {\n\n    @Resource\n    private SysRoleMapper roleMapper;\n    @Resource\n    private SysMenuMapper menuMapper;\n    @Resource\n    private RedisCache redisCache;\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 = roleMapper.selectRolePermissionByUserId(userId);\n        Set<String> permsSet = new HashSet<>();\n        for (SysRole perm : perms) {\n            if (StringUtils.isNotNull(perm)) {\n                permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(\",\")));\n            }\n        }\n        return permsSet;\n    }\n\n    /**\n     * 角色列表\n     *\n     * @param roleDto\n     * @param page\n     * @param pageSize\n     * @return\n     */\n    @Override\n    public BaseVo<RoleVo> list(RoleDto roleDto, Long page, Long pageSize) {\n        IPage<RoleVo> list = roleMapper.list(roleDto, new MyPage(page, pageSize).getPage());\n        BaseVo<RoleVo> baseVo = new BaseVo<>();\n        baseVo.setList(list.getRecords());\n        baseVo.setCount((int) list.getTotal());\n        return baseVo;\n    }\n\n    /**\n     * 根据角色id获取角色\n     *\n     * @param roleId\n     * @return\n     */\n    @Override\n    public RoleVo getRoleByRoleId(Long roleId) {\n        // 检查缓存中是否标记着空id\n        if (redisCache.hasKey(CacheConstants.ROLE_NOT_EXIST_KEY + roleId)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST);\n        }\n        // 检查缓存中是否存在\n        RoleVo role = redisCache.getCacheObject(CacheConstants.ROLE_DETAILS_BY_ROLEID_KEY + roleId, RoleVo.class);\n        if (StringUtils.isNotNull(role)) {\n            return role;\n        }\n        role = roleMapper.selectRoleByRoleId(roleId);\n        if (StringUtils.isNull(role)) {\n            redisCache.setCacheObject(CacheConstants.ROLE_NOT_EXIST_KEY + roleId, null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST);\n        }\n        redisCache.setCacheObject(CacheConstants.ROLE_DETAILS_BY_ROLEID_KEY + roleId, role);\n        return role;\n    }\n\n    /**\n     * 新增角色\n     *\n     * @param roleDto\n     */\n    @Override\n    @Transactional\n    public void addRole(RoleDto roleDto) {\n        // 角色名称是否被使用\n        if (redisCache.hasKey(CacheConstants.ROLE_NAME_USED_KEY + roleDto.getRoleName())) {\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_NAME_USED);\n        }\n        // 角色权限字符是否被使用\n        if (redisCache.hasKey(CacheConstants.ROLE_CODE_USED_KEY + roleDto.getRoleName())) {\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_PERMISSION_CODE_USED);\n        }\n        LambdaQueryWrapper<SysRole> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysRole::getRoleName, roleDto.getRoleName());\n        SysRole role = roleMapper.selectOne(queryWrapper);\n        if (StringUtils.isNotNull(role)) {\n            redisCache.setCacheObject(CacheConstants.ROLE_NAME_USED_KEY + roleDto.getRoleName(), null);\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_NAME_USED);\n        }\n        queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysRole::getRoleKey, roleDto.getRoleKey());\n        role = roleMapper.selectOne(queryWrapper);\n        if (StringUtils.isNotNull(role)) {\n            redisCache.setCacheObject(CacheConstants.ROLE_NAME_USED_KEY + roleDto.getRoleKey(), null);\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_PERMISSION_CODE_USED);\n        }\n        roleMapper.insertRole(roleDto);\n        roleMapper.insertRoleMenu(roleDto);\n        redisCache.removeCacheObject(CacheConstants.ROLE_KEY_PREFIX);\n    }\n\n    /**\n     * 更新角色\n     *\n     * @param roleDto\n     */\n    @Override\n    @Transactional\n    public void updateRole(RoleDto roleDto) {\n        // 检查缓存中是否存在空id\n        if (redisCache.hasKey(CacheConstants.ROLE_NOT_EXIST_KEY + roleDto.getRoleId())) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, roleDto.getRoleId() + CommonMessageConstants.ROLE_NOT_EXIST);\n        }\n        // 检查缓存中标记着角色权限字符已被使用\n        if (redisCache.hasKey(CacheConstants.ROLE_CODE_USED_KEY + roleDto.getRoleKey())) {\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_PERMISSION_CODE_USED);\n        }\n        // 检查缓存中标记着角色名称已被使用\n        if (redisCache.hasKey(CacheConstants.ROLE_NAME_USED_KEY + roleDto.getRoleName())) {\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_NAME_USED);\n        }\n        LambdaQueryWrapper<SysRole> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysRole::getRoleId, roleDto.getRoleId());\n        SysRole role = roleMapper.selectOne(queryWrapper);\n        // 是否存在该角色\n        if (StringUtils.isNull(role)) {\n            redisCache.setCacheObject(CacheConstants.ROLE_NOT_EXIST_KEY + roleDto.getRoleId(), null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, roleDto.getRoleId() + CommonMessageConstants.ROLE_NOT_EXIST);\n        }\n        // 角色名称是否被使用\n        queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysRole::getRoleName, roleDto.getRoleName());\n        role = roleMapper.selectOne(queryWrapper);\n        if (StringUtils.isNotNull(role) && !roleDto.getRoleId().equals(role.getRoleId())) {\n            redisCache.setCacheObject(CacheConstants.ROLE_NAME_USED_KEY + roleDto.getRoleName(), null);\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_NAME_USED);\n        }\n        // 角色权限字符是否被使用\n        queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysRole::getRoleKey, roleDto.getRoleKey());\n        role = roleMapper.selectOne(queryWrapper);\n        if (StringUtils.isNotNull(role) && !roleDto.getRoleId().equals(role.getRoleId())) {\n            redisCache.setCacheObject(CacheConstants.ROLE_CODE_USED_KEY + roleDto.getRoleKey(), null);\n            throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_PERMISSION_CODE_USED);\n        }\n        // 菜单id是否存在\n        if (roleDto.getPermissions().size() > 0) {\n            List<SysMenu> menuList = redisCache.getCacheList(CacheConstants.MENU_LIST_KEY, SysMenu.class);\n            if (StringUtils.isNull(menuList) || menuList.size() == 0) {\n                LambdaQueryWrapper<SysMenu> menuQueryWrapper = new LambdaQueryWrapper<>();\n                menuList = menuMapper.selectList(menuQueryWrapper);\n                redisCache.setCacheList(CacheConstants.MENU_LIST_KEY, menuList);\n            }\n            for (Long menuId : roleDto.getPermissions()) {\n                boolean exist = menuList.stream().anyMatch(menu -> menuId.equals(menu.getMenuId()));\n                if (!exist) {\n                    // 菜单id不存在\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, menuId + CommonMessageConstants.ROLE_MENU_NOT_EXIST);\n                }\n            }\n        }\n        roleMapper.updateRole(roleDto);\n        if (roleDto.getPermissions().size() > 0) {\n            roleMapper.updateRoleMenu(roleDto);\n        }\n        redisCache.removeCacheObject(CacheConstants.ROLE_KEY_PREFIX);\n    }\n\n    /**\n     * 删除角色\n     *\n     * @param roleId\n     */\n    @Override\n    public void removeByRoleId(Long roleId) {\n        // 检查缓存中是否存在空id\n        if (redisCache.hasKey(CacheConstants.ROLE_NOT_EXIST_KEY + roleId)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST);\n        }\n        LambdaQueryWrapper<SysRole> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysRole::getRoleId, roleId);\n        SysRole role = roleMapper.selectOne(queryWrapper);\n        if (StringUtils.isNull(role)) {\n            redisCache.setCacheObject(CacheConstants.ROLE_NOT_EXIST_KEY + roleId, null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST);\n        }\n        roleMapper.deleteById(roleId);\n        redisCache.removeCacheObject(CacheConstants.ROLE_KEY_PREFIX);\n    }\n\n    /**\n     * 批量删除角色\n     *\n     * @param ids\n     */\n    @Override\n    public void removeByRoleIds(Set<Long> ids) {\n        if (ids.size() > 0) {\n            List<SysRole> roleList;\n            roleList = redisCache.getCacheList(CacheConstants.ROLE_LIST_KEY, SysRole.class);\n            if (StringUtils.isNull(roleList) || roleList.size() == 0) {\n                LambdaQueryWrapper<SysRole> queryWrapper = new LambdaQueryWrapper<>();\n                roleList = roleMapper.selectList(queryWrapper);\n                redisCache.setCacheList(CacheConstants.ROLE_LIST_KEY, roleList);\n            }\n            for (Long roleId : ids) {\n                // 检查缓存中是否存在空id\n                if (redisCache.hasKey(CacheConstants.ROLE_NOT_EXIST_KEY + roleId)) {\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST);\n                }\n                boolean exist = roleList.stream().anyMatch(role -> roleId.equals(role.getRoleId()));\n                if (!exist) {\n                    redisCache.setCacheObject(CacheConstants.ROLE_NOT_EXIST_KEY + roleId, null);\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST);\n                }\n            }\n            roleMapper.deleteBatchIds(ids);\n            redisCache.removeCacheObject(CacheConstants.ROLE_KEY_PREFIX);\n        } else {\n            throw new CommonException(StateCode.ERROR_REQUEST_PARAMS, CommonMessageConstants.SYS_REQUEST_ILLEGAL);\n        }\n    }\n\n    /**\n     * 更新状态\n     * @param roleDto\n     */\n    @Override\n    public void updateStatus(RoleDto roleDto) {\n        if (StringUtils.isNull(roleDto.getRoleId())) {\n            throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.ROLE_ID_NOT_FOUND);\n        }\n        // 状态参数是否合法\n        if (!(\"0\".equals(roleDto.getStatus()) || \"1\".equals(roleDto.getStatus()))) {\n            throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.ROLE_STATUS_ILLEGAL);\n        }\n        // 检查缓存中是否标记着空id\n        if (redisCache.hasKey(CacheConstants.ROLE_NOT_EXIST_KEY + roleDto.getRoleId())) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, roleDto.getRoleId() + CommonMessageConstants.ROLE_NOT_EXIST);\n        }\n        LambdaQueryWrapper<SysRole> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysRole::getRoleId, roleDto.getRoleId());\n        SysRole menu = roleMapper.selectOne(queryWrapper);\n        if (StringUtils.isNull(menu)) {\n            redisCache.setCacheObject(CacheConstants.ROLE_NOT_EXIST_KEY + roleDto.getRoleId(), null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, roleDto.getRoleId() + CommonMessageConstants.ROLE_NOT_EXIST);\n        }\n        roleMapper.updateRoleStatus(roleDto);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysUserServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport ginyi.common.constant.CacheConstants;\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.constant.UserConstants;\nimport ginyi.common.exception.CommonException;\nimport ginyi.common.mysql.MyPage;\nimport ginyi.common.redis.cache.RedisCache;\nimport ginyi.common.result.StateCode;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.system.domain.SysDept;\nimport ginyi.system.domain.SysPost;\nimport ginyi.system.domain.SysRole;\nimport ginyi.system.domain.SysUser;\nimport ginyi.system.domain.model.dto.DeptDto;\nimport ginyi.system.domain.model.dto.PostDto;\nimport ginyi.system.domain.model.dto.RoleDto;\nimport ginyi.system.domain.model.dto.UserDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.UserVo;\nimport ginyi.system.mapper.SysDeptMapper;\nimport ginyi.system.mapper.SysPostMapper;\nimport ginyi.system.mapper.SysRoleMapper;\nimport ginyi.system.mapper.SysUserMapper;\nimport ginyi.system.service.ISysUserService;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport javax.annotation.Resource;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n@Service\npublic class SysUserServiceImpl implements ISysUserService {\n\n    @Resource\n    private SysUserMapper userMapper;\n    @Resource\n    private SysPostMapper postMapper;\n    @Resource\n    private SysRoleMapper roleMapper;\n    @Resource\n    private SysDeptMapper deptMapper;\n    @Resource\n    private RedisCache redisCache;\n\n    /**\n     * 修改用户基本信息\n     *\n     * @param userDto 用户信息\n     * @return 结果\n     */\n    @Override\n    @Transactional\n    public void updateUser(UserDto userDto) {\n        if (checkLogic(userDto)) {\n            userMapper.updateUser(userDto);\n            if (StringUtils.isNotNull(userDto.getPostIds()) && userDto.getPostIds().size() > 0) {\n                userMapper.updateUserPostIds(userDto);\n            }\n            if (StringUtils.isNotNull(userDto.getRoleIds()) && userDto.getRoleIds().size() > 0) {\n                userMapper.updateUserRoleIds(userDto);\n            }\n            // 清除缓存\n            redisCache.removeCacheObject(CacheConstants.USER_KEY_PREFIX);\n        }\n    }\n\n\n    /**\n     * 新增用户\n     *\n     * @param userDto\n     */\n    @Override\n    @Transactional\n    public void addUser(UserDto userDto) {\n        if (checkLogic(userDto)) {\n            userMapper.insertUser(userDto);\n            if (StringUtils.isNotNull(userDto.getPostIds()) && userDto.getPostIds().size() > 0) {\n                userMapper.insertUserPostIds(userDto);\n            }\n            if (StringUtils.isNotNull(userDto.getRoleIds()) && userDto.getRoleIds().size() > 0) {\n                userMapper.insertUserRoleIds(userDto);\n            }\n            // 清除缓存\n            redisCache.removeCacheObject(CacheConstants.USER_KEY_PREFIX);\n            redisCache.removeCacheObject(CacheConstants.POST_KEY_PREFIX);\n            redisCache.removeCacheObject(CacheConstants.DEPT_KEY_PREFIX);\n        }\n    }\n\n    /**\n     * 根据用户id查询用户\n     *\n     * @param userId\n     */\n    @Override\n    public UserVo getUserByUserId(String userId) {\n        // 检查缓存中是否记录着空的userId\n        if (redisCache.hasKey(CacheConstants.USER_NOT_EXIST_KEY + userId)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.USER_NOT_EXIST);\n        }\n        UserVo user;\n        // 检查缓存中是否有该user\n        user = redisCache.getCacheObject(CacheConstants.USER_DETAILS_BY_USERID_KEY + userId, UserVo.class);\n        if (StringUtils.isNotNull(user)) {\n            return user;\n        }\n        user = userMapper.selectUserByUserId(userId);\n        if (StringUtils.isNull(user)) {\n            redisCache.setCacheObject(CacheConstants.USER_NOT_EXIST_KEY + userId, null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.USER_NOT_EXIST);\n        }\n        // 存入缓存\n        redisCache.setCacheObject(CacheConstants.USER_DETAILS_BY_USERID_KEY + userId, user);\n        return user;\n    }\n\n\n    /**\n     * 获取用户列表(不含admin)\n     *\n     * @param userDto\n     * @param page\n     * @param pageSize\n     */\n    @Override\n    public BaseVo<UserVo> list(UserDto userDto, Long page, Long pageSize) {\n        IPage<UserVo> list = userMapper.list(userDto, new MyPage(page, pageSize).getPage());\n        BaseVo<UserVo> baseVo = new BaseVo<>();\n        baseVo.setList(list.getRecords());\n        baseVo.setCount((int) list.getTotal());\n        return baseVo;\n    }\n\n\n    /**\n     * 根据用户id删除用户\n     *\n     * @param userId\n     */\n    @Override\n    public void removeById(Long userId) {\n        // 检查缓存中是否标记着空id\n        if (redisCache.hasKey(CacheConstants.USER_NOT_EXIST_KEY + userId)) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, userId + CommonMessageConstants.USER_NOT_EXIST);\n        }\n        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysUser::getUserId, userId);\n        SysUser user = userMapper.selectOne(queryWrapper);\n        if (StringUtils.isNull(user)) {\n            redisCache.setCacheObject(CacheConstants.USER_NOT_EXIST_KEY + userId, null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, userId + CommonMessageConstants.USER_NOT_EXIST);\n        }\n        userMapper.deleteById(userId);\n    }\n\n    /**\n     * 批量删除用户\n     *\n     * @param ids\n     */\n    @Override\n    @Transactional\n    public void removeUserByIds(Set<Long> ids) {\n        if (ids.size() > 0) {\n            List<SysUser> userList;\n            userList = redisCache.getCacheList(CacheConstants.USER_LIST_KEY, SysUser.class);\n            if (StringUtils.isNull(userList) || userList.size() == 0) {\n                LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();\n                userList = userMapper.selectList(queryWrapper);\n                redisCache.setCacheList(CacheConstants.USER_LIST_KEY, userList);\n            }\n            for (Long userId : ids) {\n                // 检查缓存中是否标记着空id\n                if (redisCache.hasKey(CacheConstants.USER_NOT_EXIST_KEY + userId)) {\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, userId + CommonMessageConstants.USER_NOT_EXIST);\n                }\n                boolean exist = userList.stream().anyMatch(user -> userId.equals(user.getUserId()));\n                if (!exist) {\n                    redisCache.setCacheObject(CacheConstants.USER_NOT_EXIST_KEY + userId, null);\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, userId + CommonMessageConstants.USER_NOT_EXIST);\n                }\n            }\n            userMapper.deleteBatchIds(ids);\n            redisCache.removeCacheObject(CacheConstants.USER_KEY_PREFIX);\n        } else {\n            throw new CommonException(StateCode.ERROR_REQUEST_PARAMS, CommonMessageConstants.SYS_REQUEST_ILLEGAL);\n        }\n    }\n\n    /**\n     * 更改用户状态（0正常 1停用）\n     *\n     * @param userDto\n     */\n    @Override\n    public void updateStatus(UserDto userDto) {\n        if (StringUtils.isNull(userDto.getUserId())) {\n            throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.USER_ID_NOT_FOUND);\n        }\n        // 状态参数是否合法\n        if (!(\"0\".equals(userDto.getStatus()) || \"1\".equals(userDto.getStatus()))) {\n            throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.USER_STATUS_ILLEGAL);\n        }\n        // 检查缓存中是否标记着空id\n        if (redisCache.hasKey(CacheConstants.USER_NOT_EXIST_KEY + userDto.getUserId())) {\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, userDto.getUserId() + CommonMessageConstants.USER_NOT_EXIST);\n        }\n        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(SysUser::getUserId, userDto.getUserId());\n        SysUser user = userMapper.selectOne(queryWrapper);\n        if (StringUtils.isNull(user)) {\n            redisCache.setCacheObject(CacheConstants.USER_NOT_EXIST_KEY + userDto.getUserId(), null);\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, userDto.getUserId() + CommonMessageConstants.USER_NOT_EXIST);\n        }\n        userMapper.updateUserStatus(userDto);\n    }\n\n    /**\n     * 根据部门 id 获取用户列表\n     *\n     * @param deptDto\n     * @return\n     */\n    @Override\n    public BaseVo<HashMap<String, Object>> getUserListByDeptIds(DeptDto deptDto) {\n        List<Long> deptIds = deptDto.getDeptIds();\n        if (deptIds.size() == 0) {\n            throw new CommonException(StateCode.ERROR_PARAMS_SERVICE, CommonMessageConstants.SYS_REQUEST_ILLEGAL);\n        }\n        List<SysDept> deptList = deptMapper.selectList(null);\n        // 判断部门id是否合法\n        for (Long deptId : deptIds) {\n            boolean isExist = false;\n            for (SysDept dept : deptList) {\n                if (deptId.equals(dept.getDeptId())) {\n                    isExist = true;\n                    break;\n                }\n            }\n            if (!isExist) {\n                throw new CommonException(StateCode.ERROR_NOT_EXIST, deptId + CommonMessageConstants.DEPT_NOT_EXIST);\n            }\n        }\n        List<SysUser> userList = userMapper.selectUserByDeptIds(deptIds);\n\n        ArrayList<HashMap<String, Object>> arrayList = new ArrayList<>();\n        for (SysUser user : userList) {\n            HashMap<String, Object> map = new HashMap<>();\n            map.put(\"userId\", user.getUserId());\n            map.put(\"nickName\", user.getNickName());\n            arrayList.add(map);\n        }\n\n        BaseVo<HashMap<String, Object>> baseVo = new BaseVo<>();\n        baseVo.setList(arrayList);\n        baseVo.setCount(arrayList.size());\n        return baseVo;\n    }\n\n    /**\n     * 根据岗位id获取用户列表\n     * @param postDto\n     * @return\n     */\n    @Override\n    public BaseVo<HashMap<String, Object>> getUserListByPostIds(PostDto postDto) {\n        List<Long> postIds = postDto.getPostIds();\n        if (postIds.size() == 0) {\n            throw new CommonException(StateCode.ERROR_PARAMS_SERVICE, CommonMessageConstants.SYS_REQUEST_ILLEGAL);\n        }\n        List<SysPost> postList = postMapper.selectList(null);\n        // 判断岗位id是否合法\n        for (Long postId : postIds) {\n            boolean isExist = false;\n            for (SysPost post : postList) {\n                if (postId.equals(post.getPostId())) {\n                    isExist = true;\n                    break;\n                }\n            }\n            if (!isExist) {\n                throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST);\n            }\n        }\n        // 查出所有用户\n        IPage<UserVo> userList = userMapper.list(new UserDto(), new MyPage(null, null).getPage());\n\n        ArrayList<HashMap<String, Object>> arrayList = new ArrayList<>();\n        for (Long postId : postDto.getPostIds()) {\n            for (UserVo user : userList.getRecords()) {\n                HashMap<String, Object> map = new HashMap<>();\n                if (user.getPostIds().contains(postId)) {\n                    map.put(\"userId\", user.getUserId());\n                    map.put(\"nickName\", user.getNickName());\n                    arrayList.add(map);\n                }\n            }\n        }\n        // 去重\n        List<HashMap<String, Object>> resultList = arrayList.stream().distinct().collect(Collectors.toList());\n\n        BaseVo<HashMap<String, Object>> baseVo = new BaseVo<>();\n        baseVo.setList(resultList);\n        baseVo.setCount(resultList.size());\n        return baseVo;\n    }\n\n    /**\n     * 根据角色 id 获取用户列表\n     * @param roleDto\n     * @return\n     */\n    @Override\n    public BaseVo<HashMap<String, Object>> getUserListByRoleIds(RoleDto roleDto) {\n        List<Long> roleIds = roleDto.getRoleIds();\n        if (roleIds.size() == 0) {\n            throw new CommonException(StateCode.ERROR_PARAMS_SERVICE, CommonMessageConstants.SYS_REQUEST_ILLEGAL);\n        }\n        List<SysRole> roleList = roleMapper.selectList(null);\n        // 判断岗位id是否合法\n        for (Long roleId : roleIds) {\n            boolean isExist = false;\n            for (SysRole role : roleList) {\n                if (roleId.equals(role.getRoleId())) {\n                    isExist = true;\n                    break;\n                }\n            }\n            if (!isExist) {\n                throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST);\n            }\n        }\n        // 查出所有用户\n        IPage<UserVo> userList = userMapper.list(new UserDto(), new MyPage(null, null).getPage());\n\n        ArrayList<HashMap<String, Object>> arrayList = new ArrayList<>();\n        for (Long roleId : roleDto.getRoleIds()) {\n            for (UserVo user : userList.getRecords()) {\n                HashMap<String, Object> map = new HashMap<>();\n                if (user.getRoleIds().contains(roleId)) {\n                    map.put(\"userId\", user.getUserId());\n                    map.put(\"nickName\", user.getNickName());\n                    arrayList.add(map);\n                }\n            }\n        }\n        // 去重\n        List<HashMap<String, Object>> resultList = arrayList.stream().distinct().collect(Collectors.toList());\n\n        BaseVo<HashMap<String, Object>> baseVo = new BaseVo<>();\n        baseVo.setList(resultList);\n        baseVo.setCount(resultList.size());\n        return baseVo;\n    }\n\n    /**\n     * 校验逻辑\n     *\n     * @param userDto\n     */\n    public boolean checkLogic(UserDto userDto) {\n        // 判断部门是否存在\n        if (StringUtils.isNotNull(userDto.getDeptId())) {\n            // 检查缓存中是否标记着空id\n            if (redisCache.hasKey(CacheConstants.DEPT_NOT_EXIST_KEY + userDto.getDeptId())) {\n                throw new CommonException(StateCode.ERROR_NOT_EXIST, userDto.getDeptId() + CommonMessageConstants.DEPT_NOT_EXIST);\n            }\n            SysDept sysDept;\n            LambdaQueryWrapper<SysDept> deptQueryWrapper = new LambdaQueryWrapper<>();\n            deptQueryWrapper.eq(SysDept::getDeptId, userDto.getDeptId());\n            sysDept = deptMapper.selectOne(deptQueryWrapper);\n            if (sysDept == null) {\n                redisCache.setCacheObject(CacheConstants.DEPT_NOT_EXIST_KEY + userDto.getDeptId(), null);\n                throw new CommonException(StateCode.ERROR_NOT_EXIST, userDto.getDeptId() + CommonMessageConstants.DEPT_NOT_EXIST);\n            }\n        }\n\n        // 用户名是否被占用（插入时）\n        if (StringUtils.isNotBlank(userDto.getUserName()) && StringUtils.isNull(userDto.getUserId())) {\n            LambdaQueryWrapper<SysUser> userQueryWrapper = new LambdaQueryWrapper<>();\n            userQueryWrapper.eq(SysUser::getUserName, userDto.getUserName());\n            SysUser user = userMapper.selectOne(userQueryWrapper);\n            if (user != null) {\n                throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.USER_NAME_USED);\n            }\n        }\n\n        // 判断插入的岗位id是否存在\n        if (StringUtils.isNotNull(userDto.getPostIds()) && userDto.getPostIds().size() > 0) {\n            SysPost sysPost;\n            for (Long postId : userDto.getPostIds()) {\n                if (redisCache.hasKey(CacheConstants.POST_NOT_EXIST_KEY + postId)) {\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST);\n                }\n                LambdaQueryWrapper<SysPost> postQueryWrapper = new LambdaQueryWrapper<>();\n                postQueryWrapper.eq(SysPost::getPostId, postId);\n                sysPost = postMapper.selectOne(postQueryWrapper);\n                if (sysPost == null) {\n                    redisCache.setCacheObject(CacheConstants.POST_NOT_EXIST_KEY + postId, null);\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST);\n                }\n            }\n        }\n\n        // 判断插入的角色id是否存在\n        if (StringUtils.isNotNull(userDto.getRoleIds()) && userDto.getRoleIds().size() > 0) {\n            SysRole sysRole;\n            for (Long roleId : userDto.getRoleIds()) {\n                if (redisCache.hasKey(CacheConstants.ROLE_NOT_EXIST_KEY + roleId)) {\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST);\n                }\n                LambdaQueryWrapper<SysRole> roleQueryWrapper = new LambdaQueryWrapper<>();\n                roleQueryWrapper.eq(SysRole::getRoleId, roleId);\n                sysRole = roleMapper.selectOne(roleQueryWrapper);\n                if (sysRole == null) {\n                    redisCache.setCacheObject(CacheConstants.ROLE_NOT_EXIST_KEY + roleId, null);\n                    throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST);\n                }\n            }\n        }\n        return true;\n    }\n\n    /**\n     * 注册用户信息\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    @Override\n    public boolean registerUser(SysUser user) {\n        UserDto userDto = new UserDto();\n        BeanUtils.copyProperties(user, userDto);\n        return userMapper.insertUser(userDto) > 0;\n    }\n\n    @Override\n    public SysUser selectUserByUserName(String userName) {\n        return userMapper.selectUserByUserName(userName);\n    }\n\n    /**\n     * 校验用户名称是否唯一\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    @Override\n    public String checkUserNameUnique(SysUser user) {\n        Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();\n        SysUser info = userMapper.checkUserNameUnique(user.getUserName());\n        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) {\n            return UserConstants.NOT_UNIQUE;\n        }\n        return UserConstants.UNIQUE;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysVerifyServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport cn.hutool.captcha.CaptchaUtil;\nimport cn.hutool.captcha.CircleCaptcha;\nimport cn.hutool.crypto.SecureUtil;\nimport cn.hutool.extra.servlet.ServletUtil;\nimport ginyi.common.constant.CacheConstants;\nimport ginyi.common.exception.CommonException;\nimport ginyi.common.redis.cache.RedisCache;\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.result.StateCode;\nimport ginyi.common.utils.Constants;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.system.service.ISysConfigService;\nimport ginyi.system.service.IVerifyService;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.concurrent.TimeUnit;\n\n@Service\npublic class SysVerifyServiceImpl implements IVerifyService {\n\n\n    @Resource\n    private HttpServletRequest request;\n    @Resource\n    private RedisCache redisCache;\n    @Resource\n    private ISysConfigService configService;\n\n    /**\n     * 图片验证码\n     */\n    @Override\n    public String captcha() {\n\n        boolean captchaEnabled = configService.selectCaptchaEnabled();\n        if (!captchaEnabled) {\n            return null;\n        }\n\n        CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(200, 100, 4, 15);\n        String img = captcha.getImageBase64();\n        String code = captcha.getCode();\n\n        String clientIP = ServletUtil.getClientIP(request);\n        String userAgent = ServletUtil.getHeader(request, \"User-Agent\", \"utf-8\");\n\n        // 缓存key\n        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + SecureUtil.md5(clientIP + userAgent);\n        redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);\n\n        return img;\n    }\n\n    /**\n     * 校验图片验证码\n     *\n     * @param code\n     */\n    @Override\n    public void checkImgCode(String code) {\n        String clientIP = ServletUtil.getClientIP(request);\n        String userAgent = ServletUtil.getHeader(request, \"User-Agent\", \"utf-8\");\n\n        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + SecureUtil.md5(clientIP + userAgent);\n        String captcha = redisCache.getCacheObject(verifyKey, String.class);\n        if(StringUtils.isNull(code)){\n            throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.SYS_CAPTCHA_NOT_EXIST);\n        }\n        // 验证码失效\n        if (captcha == null) {\n            throw new CommonException(StateCode.ERROR_PARAMS_SERVICE, CommonMessageConstants.VERIFY_EXPIRE);\n        }\n        if (!captcha.equalsIgnoreCase(code)) {\n            throw new CommonException(StateCode.ERROR_PARAMS_SERVICE, CommonMessageConstants.VERiFY_INCORRECT);\n        } else {\n            // 输入正确，删除该验证码\n            redisCache.removeCacheObject(verifyKey);\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/TokenServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport eu.bitwalker.useragentutils.UserAgent;\nimport ginyi.common.constant.CacheConstants;\nimport ginyi.common.constant.UserConstants;\nimport ginyi.common.redis.cache.RedisCache;\nimport ginyi.common.utils.ServletUtils;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.common.utils.Constants;\nimport ginyi.common.utils.ip.AddressUtils;\nimport ginyi.common.utils.ip.IpUtils;\nimport ginyi.common.utils.uuid.IdUtils;\nimport ginyi.system.domain.LoginUser;\nimport ginyi.system.service.ITokenService;\nimport io.jsonwebtoken.Claims;\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.SignatureAlgorithm;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\n@Slf4j\n@Service\npublic class TokenServiceImpl implements ITokenService {\n\n    @Resource\n    private RedisCache redisCache;\n    @Resource\n    private HttpServletRequest request;\n    /**\n     * 令牌自定义标识\n     */\n    @Value(\"${token.header}\")\n    private String header;\n    /**\n     * 令牌秘钥\n     */\n    @Value(\"${token.secret}\")\n    private String secret;\n    /**\n     * 令牌有效期（默认30分钟）\n     */\n    @Value(\"${token.expireTime}\")\n    private int expireTime;\n\n    protected static final long MILLIS_SECOND = 1000;\n\n    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;\n\n    private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;\n\n\n    /**\n     * 从数据声明生成令牌\n     *\n     * @param claims 数据声明\n     * @return 令牌\n     */\n    private String createToken(Map<String, Object> claims) {\n        String token = Jwts.builder()\n                .setClaims(claims)\n                .signWith(SignatureAlgorithm.HS512, secret).compact();\n        return token;\n    }\n\n\n    /**\n     * 创建令牌\n     *\n     * @param loginUser 用户信息\n     * @return 令牌\n     */\n    @Override\n    public String createToken(LoginUser loginUser) {\n        String token = IdUtils.fastUUID();\n        loginUser.setToken(token);\n        setUserAgent(loginUser);\n        refreshToken(loginUser);\n\n        Map<String, Object> claims = new HashMap<>();\n        claims.put(Constants.LOGIN_USER_KEY, token);\n        return createToken(claims);\n    }\n\n    /**\n     * 获取用户身份信息\n     *\n     * @return 用户信息\n     */\n    @Override\n    public LoginUser getLoginUser(HttpServletRequest request) {\n        // 获取请求携带的令牌\n        String token = getToken(request);\n        if (StringUtils.isNotEmpty(token)) {\n            try {\n                Claims claims = parseToken(token);\n                // 解析对应的权限以及用户信息\n                String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);\n                String userKey = getTokenKey(uuid);\n                LoginUser user = redisCache.getCacheObject(userKey, LoginUser.class);\n                request.setAttribute(UserConstants.CURRENT_USER, user.getUsername());\n                return user;\n            } catch (Exception e) {\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 删除用户身份信息\n     */\n    @Override\n    public void delLoginUser(String token) {\n        if (StringUtils.isNotEmpty(token)) {\n            String userKey = getTokenKey(token);\n            redisCache.removeCacheObject(userKey);\n        }\n    }\n\n    /**\n     * 验证令牌有效期，相差不足20分钟，自动刷新缓存\n     *\n     * @param loginUser\n     * @return 令牌\n     */\n    @Override\n    public void verifyToken(LoginUser loginUser) {\n        long expireTime = loginUser.getExpireTime();\n        long currentTime = System.currentTimeMillis();\n        if (expireTime - currentTime <= MILLIS_MINUTE_TEN) {\n            refreshToken(loginUser);\n        }\n    }\n\n    /**\n     * 设置用户代理信息\n     *\n     * @param loginUser 登录信息\n     */\n    public void setUserAgent(LoginUser loginUser) {\n        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader(\"User-Agent\"));\n        String ip = IpUtils.getIpAddr(ServletUtils.getRequest());\n        loginUser.setIpaddr(ip);\n        loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));\n        loginUser.setBrowser(userAgent.getBrowser().getName());\n        loginUser.setOs(userAgent.getOperatingSystem().getName());\n    }\n\n    /**\n     * 刷新令牌有效期\n     *\n     * @param loginUser 登录信息\n     */\n    public void refreshToken(LoginUser loginUser) {\n        loginUser.setLoginTime(System.currentTimeMillis());\n        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);\n        // 根据uuid将loginUser缓存\n        String userKey = getTokenKey(loginUser.getToken());\n        redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);\n    }\n\n    private String getTokenKey(String uuid) {\n        return CacheConstants.LOGIN_TOKEN_KEY + uuid;\n    }\n\n    /**\n     * 获取请求token\n     *\n     * @param request\n     * @return token\n     */\n    private String getToken(HttpServletRequest request) {\n        String token = request.getHeader(header);\n        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {\n            token = token.replace(Constants.TOKEN_PREFIX, \"\");\n        }\n        return token;\n    }\n\n    /**\n     * 从令牌中获取数据声明\n     *\n     * @param token 令牌\n     * @return 数据声明\n     */\n    private Claims parseToken(String token) {\n        return Jwts.parser()\n                .setSigningKey(secret)\n                .parseClaimsJws(token)\n                .getBody();\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/UserDetailsServiceImpl.java",
    "content": "package ginyi.framework.security.service;\n\nimport ginyi.common.exception.CommonException;\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.result.StateCode;\nimport ginyi.common.utils.StringUtils;\nimport ginyi.common.utils.enums.UserStatus;\nimport ginyi.system.domain.LoginUser;\nimport ginyi.system.domain.SysUser;\nimport ginyi.system.service.ISysPasswordService;\nimport ginyi.system.service.ISysPermissionService;\nimport ginyi.system.service.ISysUserService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\n\n@Service\npublic class UserDetailsServiceImpl implements UserDetailsService {\n\n    private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);\n\n    @Resource\n    private ISysUserService userService;\n    @Resource\n    private ISysPasswordService passwordService;\n    @Resource\n    private ISysPermissionService permissionService;\n\n    @Override\n    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n        SysUser user = userService.selectUserByUserName(username);\n        if (StringUtils.isNull(user)) {\n            // 不存在\n            throw new CommonException(StateCode.ERROR_UNAUTHENTICATION, CommonMessageConstants.USER_NOT_EXIST);\n        } else if (UserStatus.DELETED.getCode().equals(user.getDeleted())) {\n            // 被删除\n            throw new CommonException(StateCode.ERROR_UNAUTHENTICATION, CommonMessageConstants.USER_IS_DELETED);\n        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {\n            // 被停用\n            throw new CommonException(StateCode.ERROR_UNAUTHENTICATION, CommonMessageConstants.USER_IS_FORBIDDEN);\n        }\n\n        passwordService.validate(user);\n        return createLoginUser(user);\n    }\n\n    public UserDetails createLoginUser(SysUser user) {\n        return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/utils/SecurityUtils.java",
    "content": "package ginyi.framework.security.utils;\n\nimport ginyi.common.constant.CommonMessageConstants;\nimport ginyi.common.exception.CommonException;\nimport ginyi.common.result.StateCode;\nimport ginyi.system.domain.LoginUser;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\n\n/**\n * 安全服务工具类\n *\n * @author ruoyi\n */\npublic class SecurityUtils {\n    /**\n     * 获取Authentication\n     */\n    public static Authentication getAuthentication() {\n        return SecurityContextHolder.getContext().getAuthentication();\n    }\n\n    /**\n     * 获取用户\n     **/\n    public static LoginUser getLoginUser() {\n        try {\n            return (LoginUser) getAuthentication().getPrincipal();\n        } catch (Exception e) {\n            throw new CommonException(StateCode.ERROR_SYSTEM, CommonMessageConstants.USER_NOT_FOUND);\n        }\n    }\n\n    /**\n     * 判断密码是否相同\n     *\n     * @param rawPassword     真实密码\n     * @param encodedPassword 加密后字符\n     * @return 结果\n     */\n    public static boolean matchesPassword(String rawPassword, String encodedPassword) {\n        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();\n        return passwordEncoder.matches(rawPassword, encodedPassword);\n    }\n\n    /**\n     * 生成BCryptPasswordEncoder密码\n     *\n     * @param password 密码\n     * @return 加密字符串\n     */\n    public static String encryptPassword(String password) {\n        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();\n        return passwordEncoder.encode(password);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-websocket/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>ginyi-framework</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-framework-websocket</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-websocket</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-websocket/src/main/java/ginyi/framework/websocket/WebSocket.java",
    "content": "package ginyi.framework.websocket;\n\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.websocket.*;\nimport javax.websocket.server.PathParam;\nimport javax.websocket.server.ServerEndpoint;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArraySet;\n\n@Slf4j\n@Component\n@ServerEndpoint(value = \"/websocket/{userId}\")  // 接口路径 ws://主机ip:端口/webSocket;\npublic class WebSocket {\n\n    /**\n     * 与某个客户端的连接会话，需要通过它来给客户端发送数据\n     */\n    private Session session;\n    /**\n     * 用户ID\n     */\n    private String userId;\n\n    /**\n     * concurrent包的线程安全 Set，用来存放每个客户端对应的MyWebSocket对象。\n     * 虽然 @Component 默认是单例模式的，但 springboot 还是会为每个 websocket 连接初始化一个 bean，所以可以用一个静态 set 保存起来\n     * 注：底下WebSocket是当前类名\n     */\n    private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();\n    /**\n     * 用来存在线连接用户信息\n     */\n    private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();\n\n    /**\n     * 链接成功调用的方法\n     */\n    @OnOpen\n    public void onOpen(Session session, @PathParam(value = \"userId\") String userId) {\n        try {\n            this.session = session;\n            this.userId = userId;\n            webSockets.add(this);\n            sessionPool.put(userId, session);\n            log.info(\"【websocket消息】新的连接，新建连接的用户是：{}, 当前总连接数为：{}\", userId, webSockets.size());\n        } catch (Exception e) {\n        }\n    }\n\n    /**\n     * 链接关闭调用的方法\n     */\n    @OnClose\n    public void onClose() {\n        try {\n            webSockets.remove(this);\n            sessionPool.remove(this.userId);\n            log.info(\"【websocket消息】连接断开，断开连接的用户是：{}，当前总连接数为：{}\", userId, webSockets.size());\n        } catch (Exception e) {\n        }\n    }\n\n    /**\n     * 收到客户端消息后调用的方法\n     *\n     * @param message\n     */\n    @OnMessage\n    public void onMessage(String message, @PathParam(value = \"userId\") String userId) {\n        log.info(\"【websocket消息】收到客户端消息，发送者是：{}，消息是：{}\", userId, message);\n    }\n\n    /**\n     * 发送错误时的处理\n     *\n     * @param session\n     * @param error\n     */\n    @OnError\n    public void onError(Session session, Throwable error) {\n\n        log.error(\"用户错误，原因:{}\", error.getMessage());\n        error.printStackTrace();\n    }\n\n\n    /**\n     * 此为广播消息\n     *\n     * @param message\n     */\n    public void sendAllMessage(String message) {\n        log.info(\"【websocket消息】广播消息:\" + message);\n        for (WebSocket webSocket : webSockets) {\n            try {\n                if (webSocket.session.isOpen()) {\n                    webSocket.session.getAsyncRemote().sendText(message);\n                }\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    /**\n     * 此为单点消息 (单人)\n     *\n     * @param userId\n     * @param message\n     */\n    public void sendOneMessage(String userId, String message) {\n        Session session = sessionPool.get(userId);\n        if (session != null && session.isOpen()) {\n            try {\n                log.info(\"【websocket消息】 单点消息:\" + message);\n                session.getAsyncRemote().sendText(message);\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    /**\n     * 此为单点消息 (多人)\n     *\n     * @param userIds\n     * @param message\n     */\n    public void sendMoreMessage(String[] userIds, String message) {\n        for (String userId : userIds) {\n            Session session = sessionPool.get(userId);\n            if (session != null && session.isOpen()) {\n                try {\n                    log.info(\"【websocket消息】 单点消息:\" + message);\n                    session.getAsyncRemote().sendText(message);\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n\n}\n\n"
  },
  {
    "path": "ginyi-springboot/ginyi-framework/ginyi-framework-websocket/src/main/java/ginyi/framework/websocket/config/WebSocketConfig.java",
    "content": "package ginyi.framework.websocket.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.socket.server.standard.ServerEndpointExporter;\n\n@Configuration\npublic class WebSocketConfig {\n    /**\n     * 注入ServerEndpointExporter，\n     * 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint\n     */\n    @Bean\n    public ServerEndpointExporter serverEndpointExporter() {\n        return new ServerEndpointExporter();\n    }\n\n}\n\n"
  },
  {
    "path": "ginyi-springboot/ginyi-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>ginyi-springboot</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-framework</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>ginyi-framework-core</module>\n        <module>ginyi-framework-security</module>\n        <module>ginyi-framework-websocket</module>\n    </modules>\n\n    <dependencies>\n\n        <!-- 系统模块 -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-system</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n\n    </dependencies>\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/Dockerfile",
    "content": "FROM java:8\nCOPY *.jar /app.jar\nEXPOSE 8066\nENTRYPOINT [\"java\",\"-jar\",\"/app.jar\"]"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-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>ginyi-server</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-server-admin</artifactId>\n\n    <dependencies>\n\n        <!-- swagger2接口文档 -->\n        <dependency>\n            <groupId>io.springfox</groupId>\n            <artifactId>springfox-swagger2</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.springfox</groupId>\n            <artifactId>springfox-swagger-ui</artifactId>\n        </dependency>\n\n        <!-- Knife4j -->\n        <dependency>\n            <groupId>com.github.xiaoymin</groupId>\n            <artifactId>knife4j-spring-boot-starter</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/resources</directory>\n                <includes>\n                    <include>**/*.*</include>\n                </includes>\n                <filtering>false</filtering>\n            </resource>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <include>**/*.xml</include>\n                </includes>\n                <filtering>false</filtering>\n            </resource>\n        </resources>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring-boot.version}</version>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/AdminApplication.java",
    "content": "package ginyi.server.admin;\n\n\nimport ginyi.framework.core.config.AppConfig;\nimport lombok.extern.slf4j.Slf4j;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ConfigurableApplicationContext;\n\nimport java.net.UnknownHostException;\n\n// 扫描系统服务的包以及自身所在模块的包\n@SpringBootApplication(scanBasePackages = {\n        \"ginyi.system\",\n        \"ginyi.common\",\n        \"ginyi.framework.security\",\n        \"ginyi.server.admin\"\n})\n@MapperScan({\n        \"ginyi.system.**.mapper\",\n        \"ginyi.server.admin.**.mapper\"\n})\n@Slf4j\npublic class AdminApplication {\n    public static void main(String[] args) throws UnknownHostException {\n        ConfigurableApplicationContext context = SpringApplication.run(AdminApplication.class, args);\n        AppConfig.printAppInfo(context);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysDeptController.java",
    "content": "package ginyi.server.admin.controller;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;\nimport ginyi.common.annotation.Log;\nimport ginyi.common.enums.BusinessType;\nimport ginyi.common.result.CommonResult;\nimport ginyi.common.swagger.AddGroup;\nimport ginyi.common.swagger.UpdateGroup;\nimport ginyi.system.domain.SysDept;\nimport ginyi.system.domain.model.dto.DeptDto;\nimport ginyi.system.domain.model.dto.UserDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.DeptVo;\nimport ginyi.system.service.ISysDeptService;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport java.util.Set;\n\n@Api(tags = \"部门模块\")\n@RestController\n@RequestMapping(\"/api/dept\")\npublic class SysDeptController {\n\n    @Resource\n    private ISysDeptService deptService;\n\n    @ApiOperation(\"删除部门\")\n    @PostMapping(\"/delete/{deptId}\")\n    @PreAuthorize(\"@ss.hasPermission('system:dept:remove')\")\n    @Log(title = \"部门模块\", businessType = BusinessType.DELETE)\n    public CommonResult delete(@PathVariable(\"deptId\") Long deptId) {\n        deptService.removeDeptById(deptId);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"批量删除部门\")\n    @PostMapping(\"/delete\")\n    @PreAuthorize(\"@ss.hasPermission('system:dept:remove')\")\n    @Log(title = \"部门模块\", businessType = BusinessType.DELETE)\n    public CommonResult delete(@RequestBody Set<Long> ids) {\n        deptService.removeDeptByIds(ids);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"部门详情\")\n    @GetMapping(\"/getDeptByDeptId/{deptId}\")\n    @PreAuthorize(\"@ss.hasPermission('system:dept:edit')\")\n    public CommonResult<DeptVo> getDeptByDeptId(@PathVariable(\"deptId\") Long deptId) {\n        DeptVo deptVo = deptService.getDeptByDeptId(deptId);\n        return CommonResult.success(deptVo);\n    }\n\n\n    @ApiOperation(\"部门列表\")\n    @PostMapping(\"/list\")\n    @PreAuthorize(\"@ss.hasPermission('system:dept:list')\")\n    @ApiOperationSupport(ignoreParameters = {\n            \"deptDto.updateBy\",\n            \"deptDto.updateTime\",\n            \"deptDto.createTime\",\n            \"deptDto.params\",\n            \"deptDto.ancestors\",\n            \"deptDto.deptId\",\n            \"deptDto.sort\",\n    })\n    public CommonResult<BaseVo<SysDept>> list(@RequestBody DeptDto deptDto,\n                                              @RequestParam(value = \"page\", required = false) Long page,\n                                              @RequestParam(value = \"pageSize\", required = false) Long pageSize) {\n        BaseVo<SysDept> list = deptService.list(deptDto, page, pageSize);\n        return CommonResult.success(list);\n    }\n\n    @ApiOperation(\"新增部门\")\n    @PostMapping(\"/add\")\n    @PreAuthorize(\"@ss.hasPermission('system:dept:add')\")\n    @Log(title = \"部门模块\", businessType = BusinessType.INSERT)\n    @ApiOperationSupport(ignoreParameters = {\n            \"deptDto.updateBy\",\n            \"deptDto.updateTime\",\n            \"deptDto.createTime\",\n            \"deptDto.createBy\",\n            \"deptDto.params\",\n            \"deptDto.ancestors\",\n            \"deptDto.deptId\",\n            \"deptDto.beginTime\",\n            \"deptDto.endTime\",\n    })\n    public CommonResult addDept(@RequestBody @Validated(AddGroup.class) DeptDto deptDto) {\n        deptService.addDept(deptDto);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"更新部门\")\n    @PostMapping(\"/update\")\n    @PreAuthorize(\"@ss.hasPermission('system:dept:edit')\")\n    @Log(title = \"部门模块\", businessType = BusinessType.UPDATE)\n    @ApiOperationSupport(ignoreParameters = {\n            \"deptDto.updateBy\",\n            \"deptDto.updateTime\",\n            \"deptDto.createTime\",\n            \"deptDto.createBy\",\n            \"deptDto.params\",\n            \"deptDto.ancestors\",\n            \"deptDto.beginTime\",\n            \"deptDto.endTime\",\n    })\n    public CommonResult update(@RequestBody @Validated(UpdateGroup.class) DeptDto deptDto) {\n        deptService.updateDept(deptDto);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"更新部门状态\")\n    @PostMapping(\"/updateStatus\")\n    @PreAuthorize(\"@ss.hasPermission('system:dept:edit')\")\n    @Log(title = \"部门模块\", businessType = BusinessType.UPDATE)\n    @ApiOperationSupport(includeParameters = {\n            \"deptDto.deptId\",\n            \"deptDto.status\"\n    })\n    public CommonResult updateStatus(@RequestBody DeptDto deptDto){\n        deptService.updateStatus(deptDto);\n        return CommonResult.success();\n    }\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysLogController.java",
    "content": "package ginyi.server.admin.controller;\n\nimport ginyi.common.result.CommonResult;\nimport ginyi.system.domain.SysLogLogin;\nimport ginyi.system.domain.SysLogOperation;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.service.ISysLogService;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\n\n@Api(tags = \"日志模块\")\n@RestController\n@RequestMapping(\"/api/log\")\npublic class SysLogController {\n\n    @Resource\n    private ISysLogService logService;\n\n    @ApiOperation(\"登录日志\")\n    @GetMapping(\"/getLoginLogList\")\n    @PreAuthorize(\"@ss.hasPermission('monitor:operlog:list')\")\n    public CommonResult<BaseVo<SysLogLogin>> loginLogList(@RequestParam(value = \"page\", required = false) Long page,\n                                                          @RequestParam(value = \"pageSize\", required = false) Long pageSize) {\n        BaseVo<SysLogLogin> list = logService.getLoginLogList(page, pageSize);\n        return CommonResult.success(list);\n    }\n\n    @ApiOperation(\"操作日志\")\n    @GetMapping(\"/getOperationLogList\")\n    @PreAuthorize(\"@ss.hasPermission('monitor:loginlog:list')\")\n    public CommonResult<BaseVo<SysLogOperation>> operationLogList(@RequestParam(value = \"page\", required = false) Long page,\n                                                                  @RequestParam(value = \"pageSize\", required = false) Long pageSize) {\n        BaseVo<SysLogOperation> list = logService.getOperationLogList(page, pageSize);\n        return CommonResult.success(list);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysLoginController.java",
    "content": "package ginyi.server.admin.controller;\n\nimport ginyi.common.result.CommonResult;\nimport ginyi.system.domain.model.dto.LoginDto;\nimport ginyi.system.domain.model.dto.RegisterDto;\nimport ginyi.system.domain.model.vo.LoginVo;\nimport ginyi.system.service.ISysLoginService;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\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.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/api/user\")\n@Api(tags = \"登录模块\")\n@Slf4j\npublic class SysLoginController {\n\n    @Autowired\n    private ISysLoginService loginService;\n\n    @ApiOperation(\"用户登录\")\n    @PostMapping(\"/login\")\n    public CommonResult<LoginVo> login(@RequestBody @Validated LoginDto loginDto) {\n        LoginVo loginVo = loginService.login(loginDto);\n        return CommonResult.success(loginVo);\n    }\n\n    @ApiOperation(\"用户注册\")\n    @PostMapping(\"/register\")\n    public CommonResult register(@RequestBody @Validated RegisterDto registerDto) {\n        loginService.register(registerDto);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"退出登录\")\n    @PostMapping(\"/logout\")\n    public CommonResult logout() {\n        return CommonResult.success();\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysMenuController.java",
    "content": "package ginyi.server.admin.controller;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;\nimport ginyi.common.annotation.Log;\nimport ginyi.common.enums.BusinessType;\nimport ginyi.common.result.CommonResult;\nimport ginyi.common.swagger.AddGroup;\nimport ginyi.common.swagger.UpdateGroup;\nimport ginyi.system.domain.SysMenu;\nimport ginyi.system.domain.model.dto.MenuDto;\nimport ginyi.system.domain.model.dto.UserDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.service.ISysMenuService;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport java.util.Set;\n\n@Api(tags = \"菜单模块\")\n@RestController\n@RequestMapping(\"/api/menu\")\npublic class SysMenuController {\n\n    @Resource\n    private ISysMenuService menuService;\n\n    @ApiOperation(\"删除菜单\")\n    @PostMapping(\"/delete/{menuId}\")\n    @PreAuthorize(\"@ss.hasPermission('system:menu:remove')\")\n    @Log(title = \"菜单模块\", businessType = BusinessType.DELETE)\n    public CommonResult delete(@PathVariable(\"menuId\") Long menuId) {\n        menuService.removeMenuById(menuId);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"批量删除菜单\")\n    @PostMapping(\"/delete\")\n    @PreAuthorize(\"@ss.hasPermission('system:menu:remove')\")\n    @Log(title = \"菜单模块\", businessType = BusinessType.DELETE)\n    public CommonResult delete(@RequestBody Set<Long> ids) {\n        menuService.removeMenuByIds(ids);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"菜单详情\")\n    @GetMapping(\"/getMenuById/{menuId}\")\n    @PreAuthorize(\"@ss.hasPermission('system:menu:edit')\")\n    public CommonResult<SysMenu> getMenuById(@PathVariable(\"menuId\") Long menuId) {\n        SysMenu menu = menuService.getMenuById(menuId);\n        return CommonResult.success(menu);\n    }\n\n    @ApiOperation(\"路由菜单列表\")\n    @GetMapping(\"/getRouterList\")\n    public CommonResult<BaseVo<SysMenu>> list() {\n        BaseVo<SysMenu> baseVo = menuService.selectMenuList();\n        return CommonResult.success(baseVo);\n    }\n\n    @ApiOperation(\"菜单列表\")\n    @PostMapping(\"/list\")\n    @PreAuthorize(\"@ss.hasPermission('system:menu:list')\")\n    @ApiOperationSupport(ignoreParameters = {\n            \"menuDto.updateBy\",\n            \"menuDto.updateTime\",\n            \"menuDto.createTime\",\n            \"menuDto.params\",\n    })\n    public CommonResult<BaseVo<SysMenu>> list(@RequestBody @Validated MenuDto menuDto) {\n        BaseVo<SysMenu> baseVo = menuService.list(menuDto);\n        return CommonResult.success(baseVo);\n    }\n\n    @ApiOperation(\"新增菜单\")\n    @PostMapping(\"/add\")\n    @Log(title = \"菜单模块\", businessType = BusinessType.INSERT)\n    @PreAuthorize(\"@ss.hasPermission('system:menu:add')\")\n    @ApiOperationSupport(ignoreParameters = {\n            \"menuDto.updateBy\",\n            \"menuDto.updateTime\",\n            \"menuDto.createTime\",\n            \"menuDto.createBy\",\n            \"menuDto.params\",\n            \"menuDto.menuId\",\n    })\n    public CommonResult add(@RequestBody @Validated({AddGroup.class}) MenuDto menuDto) {\n        menuService.addMenu(menuDto);\n        return CommonResult.success();\n    }\n\n\n    @ApiOperation(\"更新菜单\")\n    @PostMapping(\"/update\")\n    @PreAuthorize(\"@ss.hasPermission('system:menu:edit')\")\n    @Log(title = \"菜单模块\", businessType = BusinessType.UPDATE)\n    @ApiOperationSupport(ignoreParameters = {\n            \"menuDto.updateBy\",\n            \"menuDto.updateTime\",\n            \"menuDto.createTime\",\n            \"menuDto.createBy\",\n            \"menuDto.params\",\n    })\n    public CommonResult update(@RequestBody @Validated({UpdateGroup.class}) MenuDto menuDto) {\n        menuService.updateMenu(menuDto);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"更新菜单状态\")\n    @PostMapping(\"/updateStatus\")\n    @PreAuthorize(\"@ss.hasPermission('system:menu:edit')\")\n    @Log(title = \"菜单模块\", businessType = BusinessType.UPDATE)\n    @ApiOperationSupport(includeParameters = {\n            \"menuDto.menuId\",\n            \"menuDto.status\"\n    })\n    public CommonResult updateStatus(@RequestBody MenuDto menuDto){\n        menuService.updateStatus(menuDto);\n        return CommonResult.success();\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysMonitorController.java",
    "content": "package ginyi.server.admin.controller;\n\nimport ginyi.common.annotation.Log;\nimport ginyi.common.result.CommonResult;\nimport ginyi.system.domain.SysServer;\nimport ginyi.system.domain.model.dto.CacheDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.CacheKeyVo;\nimport ginyi.system.domain.model.vo.CacheVo;\nimport ginyi.system.service.ISysMonitorService;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\n\n@Api(tags = \"监控模块\")\n@RestController\n@RequestMapping(\"/api/monitor\")\npublic class SysMonitorController {\n\n    @Resource\n    private ISysMonitorService monitorService;\n\n    @ApiOperation(\"获取系统服务信息\")\n    @GetMapping(\"/getServerInfo\")\n    @PreAuthorize(\"@ss.hasPermission('monitor:server:list')\")\n    public CommonResult<SysServer> getServerInfo() throws InterruptedException {\n        SysServer server = monitorService.getServerInfo();\n        return CommonResult.success(server);\n    }\n\n    @ApiOperation(\"获取缓存列表\")\n    @GetMapping(\"/getCacheList\")\n    @PreAuthorize(\"@ss.hasPermission('monitor:cache:list')\")\n    public CommonResult<BaseVo<CacheKeyVo>> getCacheList(){\n        BaseVo<CacheKeyVo> baseVo = monitorService.getCacheList();\n        return CommonResult.success(baseVo);\n    }\n\n    @ApiOperation(\"获取缓存详情\")\n    @PostMapping(\"/getCacheValue\")\n    @PreAuthorize(\"@ss.hasPermission('monitor:cache:details')\")\n    public CommonResult<CacheVo> getCacheDetails(@RequestBody CacheDto cacheDto){\n        CacheVo cacheVo = monitorService.getCacheDetails(cacheDto);\n        return CommonResult.success(cacheVo);\n    }\n\n    @ApiOperation(\"删除缓存\")\n    @PostMapping(\"/removeCache/{key}\")\n    @Log(title = \"缓存模块\")\n    @PreAuthorize(\"@ss.hasPermission('monitor:cache:remove')\")\n    public CommonResult removeCache(@PathVariable String key){\n        monitorService.removeCache(key);\n        return CommonResult.success();\n    }\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysNoticeController.java",
    "content": "package ginyi.server.admin.controller;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;\nimport ginyi.common.annotation.Log;\nimport ginyi.common.enums.BusinessType;\nimport ginyi.common.result.CommonResult;\nimport ginyi.common.swagger.AddGroup;\nimport ginyi.common.swagger.UpdateGroup;\nimport ginyi.system.domain.SysNotice;\nimport ginyi.system.domain.model.dto.NoticeDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.NoticeVo;\nimport ginyi.system.service.ISysNoticeService;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\n\n@Api(tags = \"通知公告模块\")\n@RestController\n@RequestMapping(\"/api/notice\")\npublic class SysNoticeController {\n\n    @Resource\n    private ISysNoticeService noticeService;\n\n    @ApiOperation(\"通知公告列表（管理员）\")\n    @PostMapping(\"/list\")\n    @ApiOperationSupport(ignoreParameters = {\n            \"noticeDto.noticeId\",\n            \"noticeDto.remark\",\n            \"noticeDto.updateBy\",\n            \"noticeDto.updateTime\",\n            \"noticeDto.createBy\",\n            \"noticeDto.createTime\",\n            \"noticeDto.params\",\n            \"noticeDto.content\",\n            \"noticeDto.userIds\",\n            \"noticeDto.userReadIds\",\n    })\n    @PreAuthorize(\"@ss.hasPermission('system:notice:query:admin')\")\n    public CommonResult<BaseVo<SysNotice>> list(@RequestBody NoticeDto noticeDto,\n                                               @RequestParam(value = \"page\", required = false) Long page,\n                                               @RequestParam(value = \"pageSize\", required = false) Long pageSize) {\n        BaseVo<SysNotice> baseVo = noticeService.list(noticeDto, page, pageSize);\n        return CommonResult.success(baseVo);\n    }\n\n    @ApiOperation(\"获取用户通知公告列表\")\n    @GetMapping(\"/getUserNoticeList\")\n    @PreAuthorize(\"@ss.hasPermission('system:notice:query')\")\n    public CommonResult<BaseVo<NoticeVo>> getUserNoticeList(@RequestParam(value = \"page\", required = false) Long page,\n                                                            @RequestParam(value = \"pageSize\", required = false) Long pageSize){\n        BaseVo<NoticeVo> baseVo = noticeService.getUserNoticeList(page, pageSize);\n        return CommonResult.success(baseVo);\n    }\n\n    @ApiOperation(\"发布通知公告\")\n    @PostMapping(\"/add\")\n    @ApiOperationSupport(ignoreParameters = {\n            \"noticeDto.noticeId\",\n            \"noticeDto.remark\",\n            \"noticeDto.updateBy\",\n            \"noticeDto.updateTime\",\n            \"noticeDto.createBy\",\n            \"noticeDto.createTime\",\n            \"noticeDto.params\",\n            \"noticeDto.userReadIds\",\n    })\n    @PreAuthorize(\"@ss.hasPermission('system:notice:add')\")\n    @Log(title = \"通知公告模块\", businessType = BusinessType.INSERT)\n    public CommonResult add(@RequestBody @Validated(AddGroup.class) NoticeDto noticeDto){\n        noticeService.add(noticeDto);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"更新通知公告\")\n    @PostMapping(\"/update\")\n    @ApiOperationSupport(ignoreParameters = {\n            \"noticeDto.remark\",\n            \"noticeDto.updateBy\",\n            \"noticeDto.updateTime\",\n            \"noticeDto.createBy\",\n            \"noticeDto.createTime\",\n            \"noticeDto.params\",\n            \"noticeDto.userReadIds\",\n    })\n    @PreAuthorize(\"@ss.hasPermission('system:notice:edit')\")\n    @Log(title = \"通知公告模块\", businessType = BusinessType.UPDATE)\n    public CommonResult update(@RequestBody @Validated(UpdateGroup.class) NoticeDto noticeDto){\n        noticeService.updateNotice(noticeDto);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"确认收到通知公告\")\n    @PostMapping(\"/haveRead/{noticeId}\")\n    @PreAuthorize(\"@ss.hasPermission('system:notice:haveRead')\")\n    @Log(title = \"通知公告模块\", businessType = BusinessType.UPDATE)\n    public CommonResult haveRead(@PathVariable Long noticeId){\n        noticeService.haveRead(noticeId);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"删除通知公告\")\n    @PostMapping(\"/delete/{noticeId}\")\n    @PreAuthorize(\"@ss.hasPermission('system:notice:remove')\")\n    @Log(title = \"通知公告模块\", businessType = BusinessType.DELETE)\n    public CommonResult remove(@PathVariable Long noticeId){\n        noticeService.remove(noticeId);\n        return CommonResult.success();\n    }\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysOnlineController.java",
    "content": "package ginyi.server.admin.controller;\n\nimport ginyi.common.annotation.Log;\nimport ginyi.common.enums.BusinessType;\nimport ginyi.common.result.CommonResult;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.SessionUserVo;\nimport ginyi.system.service.ISysOnlineService;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport java.util.Set;\n\n@Api(tags = \"在线用户\")\n@RestController\n@RequestMapping(\"/api/online\")\npublic class SysOnlineController {\n\n    @Resource\n    private ISysOnlineService onlineService;\n\n    @ApiOperation(\"在线用户列表\")\n    @GetMapping(\"/getOnlineUserList\")\n    public CommonResult<BaseVo<SessionUserVo>> getOnlineUserList(@RequestParam(value = \"page\", required = false) Long page,\n                                                                 @RequestParam(value = \"pageSize\", required = false) Long pageSize) {\n        BaseVo<SessionUserVo> list = onlineService.getOnlineUserList(page, pageSize);\n        return CommonResult.success(list);\n    }\n\n    @ApiOperation(\"强制用户退出\")\n    @PostMapping(\"/removeUser/{token}\")\n    @Log(title = \"在线用户模块\", businessType = BusinessType.CLEAN)\n    @PreAuthorize(\"@ss.hasPermission('monitor:online:forceLogout')\")\n    public CommonResult removeUser(@PathVariable(\"token\") String sessionId) {\n        onlineService.removeUser(sessionId);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"批量强制用户退出\")\n    @PostMapping(\"/removeUser\")\n    @Log(title = \"在线用户模块\", businessType = BusinessType.CLEAN)\n    @PreAuthorize(\"@ss.hasPermission('monitor:online:batchLogout')\")\n    public CommonResult removeUser(@RequestBody Set<String> ids) {\n        onlineService.removeUser(ids);\n        return CommonResult.success();\n    }\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysPostController.java",
    "content": "package ginyi.server.admin.controller;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;\nimport ginyi.common.annotation.Log;\nimport ginyi.common.enums.BusinessType;\nimport ginyi.common.result.CommonResult;\nimport ginyi.common.swagger.AddGroup;\nimport ginyi.system.domain.model.dto.DeptDto;\nimport ginyi.system.domain.model.dto.PostDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.PostVo;\nimport ginyi.system.service.ISysPostService;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport java.util.Set;\n\n\n@Api(tags = \"岗位模块\")\n@RestController\n@RequestMapping(\"/api/post\")\npublic class SysPostController {\n\n    @Resource\n    private ISysPostService postService;\n\n    @ApiOperation(\"岗位详情\")\n    @GetMapping(\"/getPostById/{postId}\")\n    @PreAuthorize(\"@ss.hasPermission('system:post:edit')\")\n    public CommonResult<PostVo> getPostByDeptId(@PathVariable(\"postId\") Long postId) {\n        PostVo postVo = postService.getPostByPostId(postId);\n        return CommonResult.success(postVo);\n    }\n\n    @ApiOperation(\"删除岗位\")\n    @PostMapping(\"/delete/{postId}\")\n    @PreAuthorize(\"@ss.hasPermission('system:post:remove')\")\n    @Log(title = \"岗位模块\", businessType = BusinessType.DELETE)\n    public CommonResult delete(@PathVariable(\"postId\") Long postId) {\n        postService.removePostById(postId);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"批量删除岗位\")\n    @PostMapping(\"/delete\")\n    @PreAuthorize(\"@ss.hasPermission('system:post:remove')\")\n    @Log(title = \"岗位模块\", businessType = BusinessType.DELETE)\n    public CommonResult delete(@RequestBody Set<Long> ids) {\n        postService.removeDeptByIds(ids);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"岗位列表\")\n    @PostMapping(\"/list\")\n    @PreAuthorize(\"@ss.hasPermission('system:post:list')\")\n    @ApiOperationSupport(ignoreParameters = {\n            \"postDto.createTime\",\n            \"postDto.params\",\n            \"postDto.updateBy\",\n            \"postDto.updateTime\",\n            \"postDto.postId\",\n            \"postDto.sort\",\n    })\n    public CommonResult<BaseVo<PostVo>> list(@RequestBody PostDto postDto,\n                                             @RequestParam(value = \"page\", required = false) Long page,\n                                             @RequestParam(value = \"pageSize\", required = false) Long pageSize) {\n        BaseVo<PostVo> list = postService.list(postDto, page, pageSize);\n        return CommonResult.success(list);\n    }\n\n\n    @ApiOperation(\"新增岗位\")\n    @PostMapping(\"/add\")\n    @PreAuthorize(\"@ss.hasPermission('system:post:add')\")\n    @Log(title = \"岗位模块\", businessType = BusinessType.INSERT)\n    @ApiOperationSupport(ignoreParameters = {\n            \"postDto.createBy\",\n            \"postDto.createTime\",\n            \"postDto.beginTime\",\n            \"postDto.endTime\",\n            \"postDto.params\",\n            \"postDto.updateBy\",\n            \"postDto.updateTime\",\n            \"postDto.postId\",\n    })\n    public CommonResult addPost(@RequestBody @Validated(AddGroup.class) PostDto postDto) {\n        postService.addPost(postDto);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"更新岗位\")\n    @PostMapping(\"/update\")\n    @PreAuthorize(\"@ss.hasPermission('system:post:edit')\")\n    @Log(title = \"岗位模块\", businessType = BusinessType.UPDATE)\n    @ApiOperationSupport(ignoreParameters = {\n            \"postDto.createBy\",\n            \"postDto.createTime\",\n            \"postDto.beginTime\",\n            \"postDto.endTime\",\n            \"postDto.params\",\n            \"postDto.updateBy\",\n            \"postDto.updateTime\",\n    })\n    public CommonResult update(@RequestBody @Validated PostDto postDto){\n        postService.updatePost(postDto);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"更新岗位状态\")\n    @PostMapping(\"/updateStatus\")\n    @PreAuthorize(\"@ss.hasPermission('system:post:edit')\")\n    @Log(title = \"岗位模块\", businessType = BusinessType.UPDATE)\n    @ApiOperationSupport(includeParameters = {\n            \"postDto.postId\",\n            \"postDto.status\"\n    })\n    public CommonResult updateStatus(@RequestBody PostDto postDto){\n        postService.updateStatus(postDto);\n        return CommonResult.success();\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysRoleController.java",
    "content": "package ginyi.server.admin.controller;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;\nimport ginyi.common.annotation.Log;\nimport ginyi.common.enums.BusinessType;\nimport ginyi.common.result.CommonResult;\nimport ginyi.common.swagger.AddGroup;\nimport ginyi.common.swagger.UpdateGroup;\nimport ginyi.system.domain.model.dto.PostDto;\nimport ginyi.system.domain.model.dto.RoleDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.RoleVo;\nimport ginyi.system.service.ISysRoleService;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport java.util.Set;\n\n@Api(tags = \"角色模块\")\n@RestController\n@RequestMapping(\"/api/role\")\npublic class SysRoleController {\n\n    @Resource\n    private ISysRoleService roleService;\n\n    @ApiOperation(\"角色列表\")\n    @PostMapping(\"/list\")\n    @PreAuthorize(\"@ss.hasPermission('system:role:query')\")\n    @ApiOperationSupport(ignoreParameters = {\n            \"roleDto.updateBy\",\n            \"roleDto.updateTime\",\n            \"roleDto.createTime\",\n            \"roleDto.params\",\n            \"roleDto.sort\",\n            \"roleDto.permissions\",\n            \"roleDto.roleId\",\n    })\n    public CommonResult<BaseVo<RoleVo>> list(@RequestBody @Validated RoleDto roleDto,\n                                             @RequestParam(value = \"page\", required = false) Long page,\n                                             @RequestParam(value = \"pageSize\", required = false) Long pageSize) {\n        BaseVo<RoleVo> list = roleService.list(roleDto, page, pageSize);\n        return CommonResult.success(list);\n    }\n\n    @ApiOperation(\"角色详情\")\n    @GetMapping(\"/getRoleByRoleId/{roleId}\")\n    @PreAuthorize(\"@ss.hasPermission('system:role:edit')\")\n    public CommonResult getRoleByRoleId(@PathVariable(\"roleId\") Long roleId){\n        RoleVo role = roleService.getRoleByRoleId(roleId);\n        return CommonResult.success(role);\n    }\n\n    @ApiOperation(\"新增角色\")\n    @PostMapping(\"/add\")\n    @PreAuthorize(\"@ss.hasPermission('system:role:add')\")\n    @Log(title = \"角色模块\", businessType = BusinessType.INSERT)\n    @ApiOperationSupport(ignoreParameters = {\n            \"roleDto.updateBy\",\n            \"roleDto.updateTime\",\n            \"roleDto.createBy\",\n            \"roleDto.createTime\",\n            \"roleDto.beginTime\",\n            \"roleDto.endTime\",\n            \"roleDto.params\",\n            \"roleDto.roleId\",\n    })\n    public CommonResult addRole(@RequestBody @Validated(AddGroup.class) RoleDto roleDto){\n        roleService.addRole(roleDto);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"更新角色\")\n    @PostMapping(\"/update\")\n    @PreAuthorize(\"@ss.hasPermission('system:role:add')\")\n    @Log(title = \"角色模块\", businessType = BusinessType.UPDATE)\n    @ApiOperationSupport(ignoreParameters = {\n            \"roleDto.updateBy\",\n            \"roleDto.updateTime\",\n            \"roleDto.createBy\",\n            \"roleDto.createTime\",\n            \"roleDto.beginTime\",\n            \"roleDto.endTime\",\n            \"roleDto.params\",\n    })\n    public CommonResult update(@RequestBody @Validated(UpdateGroup.class) RoleDto roleDto){\n        roleService.updateRole(roleDto);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"删除角色\")\n    @PostMapping(\"/delete/{roleId}\")\n    @PreAuthorize(\"@ss.hasPermission('system:role:remove')\")\n    @Log(title = \"角色模块\", businessType = BusinessType.DELETE)\n    public CommonResult delete(@PathVariable(\"roleId\") Long roleId){\n        roleService.removeByRoleId(roleId);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"批量删除角色\")\n    @PreAuthorize(\"@ss.hasPermission('system:role:remove')\")\n    @PostMapping(\"/delete\")\n    @Log(title = \"角色模块\", businessType = BusinessType.DELETE)\n    public CommonResult delete(@RequestBody Set<Long> ids){\n        roleService.removeByRoleIds(ids);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"更新角色状态\")\n    @PostMapping(\"/updateStatus\")\n    @PreAuthorize(\"@ss.hasPermission('system:post:edit')\")\n    @Log(title = \"角色模块\", businessType = BusinessType.UPDATE)\n    @ApiOperationSupport(includeParameters = {\n            \"roleDto.roleId\",\n            \"roleDto.status\"\n    })\n    public CommonResult updateStatus(@RequestBody RoleDto roleDto){\n        roleService.updateStatus(roleDto);\n        return CommonResult.success();\n    }\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysUserController.java",
    "content": "package ginyi.server.admin.controller;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;\nimport ginyi.common.annotation.Log;\nimport ginyi.common.enums.BusinessType;\nimport ginyi.common.result.CommonResult;\nimport ginyi.common.swagger.AddGroup;\nimport ginyi.common.swagger.UpdateGroup;\nimport ginyi.system.domain.SysUser;\nimport ginyi.system.domain.model.dto.DeptDto;\nimport ginyi.system.domain.model.dto.PostDto;\nimport ginyi.system.domain.model.dto.RoleDto;\nimport ginyi.system.domain.model.dto.UserDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.UserVo;\nimport ginyi.system.service.ISysUserService;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport java.util.HashMap;\nimport java.util.Set;\n\n@Api(tags = \"用户模块\")\n@RestController\n@Slf4j\n@RequestMapping(\"/api/user\")\npublic class SysUserController {\n\n    @Resource\n    private ISysUserService userService;\n\n    @ApiOperation(\"删除用户\")\n    @PostMapping(\"/delete/{userId}\")\n    @PreAuthorize(\"@ss.hasPermission('system:user:remove')\")\n    @Log(title = \"用户模块\", businessType = BusinessType.DELETE)\n    public CommonResult delete(@PathVariable(\"userId\") Long userId){\n        userService.removeById(userId);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"用户详情\")\n    @GetMapping(\"/getUserByUserId/{userId}\")\n    @PreAuthorize(\"@ss.hasPermission('system:user:edit')\")\n    public CommonResult<UserVo> getUserByUserId(@PathVariable(\"userId\") String userId) {\n        UserVo user = userService.getUserByUserId(userId);\n        return CommonResult.success(user);\n    }\n\n    @ApiOperation(\"批量删除用户\")\n    @PostMapping(\"/delete\")\n    @PreAuthorize(\"@ss.hasPermission('system:user:remove')\")\n    @Log(title = \"用户模块\", businessType = BusinessType.DELETE)\n    public CommonResult delete(@RequestBody Set<Long> ids){\n        userService.removeUserByIds(ids);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"更新用户状态\")\n    @PostMapping(\"/updateStatus\")\n    @PreAuthorize(\"@ss.hasPermission('system:user:edit')\")\n    @Log(title = \"用户模块\", businessType = BusinessType.UPDATE)\n    @ApiOperationSupport(includeParameters = {\n            \"userDto.userId\",\n            \"userDto.status\"\n    })\n    public CommonResult updateStatus(@RequestBody UserDto userDto){\n        userService.updateStatus(userDto);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"添加用户\")\n    @PostMapping(\"/add\")\n    @PreAuthorize(\"@ss.hasPermission('system:user:add')\")\n    @Log(title = \"用户模块\", businessType = BusinessType.INSERT)\n    @ApiOperationSupport(ignoreParameters = {\n            \"userDto.beginTime\",\n            \"userDto.endTime\",\n            \"userDto.createBy\",\n            \"userDto.createTime\",\n            \"userDto.deleted\",\n            \"userDto.loginDate\",\n            \"userDto.loginIp\",\n            \"userDto.params\",\n            \"userDto.updateBy\",\n            \"userDto.updateTime\",\n            \"userDto.userId\",\n            \"userDto.password\",\n    })\n    public CommonResult add(@RequestBody @Validated(AddGroup.class) UserDto userDto) {\n        userService.addUser(userDto);\n        return CommonResult.success();\n    }\n\n    @ApiOperation(\"更新用户\")\n    @PostMapping(\"/update\")\n    @PreAuthorize(\"@ss.hasPermission('system:user:edit')\")\n    @Log(title = \"用户模块\", businessType = BusinessType.UPDATE)\n    @ApiOperationSupport(ignoreParameters = {\n            \"userDto.beginTime\",\n            \"userDto.endTime\",\n            \"userDto.createBy\",\n            \"userDto.createTime\",\n            \"userDto.deleted\",\n            \"userDto.loginDate\",\n            \"userDto.loginIp\",\n            \"userDto.params\",\n            \"userDto.updateBy\",\n            \"userDto.updateTime\",\n            \"userDto.password\",\n    })\n    public CommonResult update(@RequestBody @Validated(UpdateGroup.class) UserDto userDto) {\n        userService.updateUser(userDto);\n        return CommonResult.success();\n    }\n\n\n    @ApiOperation(\"用户列表\")\n    @PostMapping(\"/list\")\n    @PreAuthorize(\"@ss.hasPermission('system:user:list')\")\n    @ApiOperationSupport(ignoreParameters = {\n            \"userDto.deleted\",\n            \"userDto.loginDate\",\n            \"userDto.loginIp\",\n            \"userDto.params\",\n            \"userDto.userId\",\n            \"userDto.password\",\n            \"userDto.avatar\",\n            \"userDto.updateBy\",\n            \"userDto.updateTime\",\n            \"userDto.createTime\",\n            \"userDto.deptId\",\n            \"userDto.postIds\",\n            \"userDto.roleIds\",\n    })\n    public CommonResult<BaseVo<UserVo>> list(@RequestBody UserDto userDto,\n                                             @RequestParam(value = \"page\", required = false) Long page,\n                                             @RequestParam(value = \"pageSize\", required = false) Long pageSize) {\n        BaseVo<UserVo> baseVo = userService.list(userDto, page, pageSize);\n        return CommonResult.success(baseVo);\n    }\n\n    @ApiOperation(\"根据部门id获取用户\")\n    @PostMapping(\"getUserListByDeptIds\")\n    @ApiOperationSupport(ignoreParameters = {\n            \"deptDto.deptId\",\n            \"deptDto.parentId\",\n            \"deptDto.ancestors\",\n            \"deptDto.deptName\",\n            \"deptDto.sort\",\n            \"deptDto.leader\",\n            \"deptDto.phone\",\n            \"deptDto.email\",\n            \"deptDto.beginTime\",\n            \"deptDto.endTime\",\n            \"deptDto.updateBy\",\n            \"deptDto.updateTime\",\n            \"deptDto.createBy\",\n            \"deptDto.createTime\",\n            \"deptDto.remark\",\n            \"deptDto.params\",\n            \"deptDto.status\",\n    })\n    public CommonResult<BaseVo<HashMap<String, Object>>> getUserListByDeptIds(@RequestBody DeptDto deptDto){\n        BaseVo<HashMap<String, Object>> userList = userService.getUserListByDeptIds(deptDto);\n        return CommonResult.success(userList);\n    }\n\n    @ApiOperation(\"根据岗位id获取用户\")\n    @PostMapping(\"getUserListByPostIds\")\n    @ApiOperationSupport(ignoreParameters = {\n            \"postDto.postId\",\n            \"postDto.postName\",\n            \"postDto.sort\",\n            \"postDto.postCode\",\n            \"postDto.beginTime\",\n            \"postDto.endTime\",\n            \"postDto.updateBy\",\n            \"postDto.updateTime\",\n            \"postDto.createBy\",\n            \"postDto.createTime\",\n            \"postDto.remark\",\n            \"postDto.params\",\n            \"postDto.status\",\n    })\n    public CommonResult<BaseVo<HashMap<String, Object>>> getUserListByPostIds(@RequestBody PostDto postDto){\n        BaseVo<HashMap<String, Object>> userList = userService.getUserListByPostIds(postDto);\n        return CommonResult.success(userList);\n    }\n\n    @ApiOperation(\"根据角色id获取用户\")\n    @PostMapping(\"getUserListByRoleIds\")\n    @ApiOperationSupport(ignoreParameters = {\n            \"roleDto.roleId\",\n            \"roleDto.roleName\",\n            \"roleDto.sort\",\n            \"roleDto.roleKey\",\n            \"roleDto.beginTime\",\n            \"roleDto.endTime\",\n            \"roleDto.updateBy\",\n            \"roleDto.updateTime\",\n            \"roleDto.createBy\",\n            \"roleDto.createTime\",\n            \"roleDto.remark\",\n            \"roleDto.params\",\n            \"roleDto.status\",\n            \"roleDto.permissions\",\n    })\n    public CommonResult<BaseVo<HashMap<String, Object>>> getUserListByPostIds(@RequestBody RoleDto roleDto){\n        BaseVo<HashMap<String, Object>> userList = userService.getUserListByRoleIds(roleDto);\n        return CommonResult.success(userList);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysVerifyController.java",
    "content": "package ginyi.server.admin.controller;\n\nimport ginyi.common.result.CommonResult;\nimport ginyi.system.service.IVerifyService;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.HashMap;\n\n@RestController\n@Api(tags = \"验证码模块\")\n@RequestMapping(\"/api/verify\")\npublic class SysVerifyController {\n\n    @Resource\n    private IVerifyService verifyService;\n\n    @ApiOperation(\"图片验证码\")\n    @GetMapping(\"/captcha\")\n    public CommonResult captcha(){\n        String img = verifyService.captcha();\n        HashMap<String, String> map = new HashMap<>();\n        map.put(\"img\", img);\n        return CommonResult.success(map);\n    }\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/swagger/AdminSwagger2Config.java",
    "content": "package ginyi.server.admin.swagger;\n\nimport com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;\nimport ginyi.common.swagger.Swagger2Config;\nimport org.springframework.context.annotation.Configuration;\nimport springfox.documentation.swagger2.annotations.EnableSwagger2;\n\n\n@EnableSwagger2\n@EnableKnife4j\n@Configuration\npublic class AdminSwagger2Config extends Swagger2Config {\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/resources/config/application.yml",
    "content": "# 说明：\n#     1、这是 ginyi-server-admin 模块下特有的配置项\n#     2、更多全局通用的配置项在 ginyi-system 系统应用服务下\n#     3、如果需要对重写全局的配置项的话，可以将其对应的属性配置拷贝至本配置中，会自动覆盖！\n\n\n# 应用服务\nserver:\n  # 服务端口\n  port: 8066\n\n\n# 接口文档的配置\nswagger:\n  group-name: admin\n  # 接口文档url\n  swagger-url: 写点什么呢~~~\n  # 接口文档标题\n  swagger-title: Ginyi-admin api doc\n  # 接口文档版本号\n  swaggerversion: 1.0\n  # 接口文档描述\n  swagger-description: 这是swagger接口文档~"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/resources/static/i18n/messages.properties",
    "content": "#错误消息\nnot.null=* 必须填写\nuser.jcaptcha.error=验证码错误\nuser.jcaptcha.expire=验证码已失效\nuser.not.exists=用户不存在/密码错误\nuser.password.not.match=用户不存在/密码错误\nuser.password.retry.limit.count=密码输入错误{0}次\nuser.password.retry.limit.exceed=密码输入错误{0}次，帐户锁定{1}分钟\nuser.password.delete=对不起，您的账号已被删除\nuser.blocked=用户已封禁，请联系管理员\nrole.blocked=角色已封禁，请联系管理员\nuser.logout.success=退出成功\n\nlength.not.valid=长度必须在{min}到{max}个字符之间\n\nuser.username.not.valid=* 2到20个汉字、字母、数字或下划线组成，且必须以非数字开头\nuser.password.not.valid=* 5-50个字符\n \nuser.email.not.valid=邮箱格式错误\nuser.mobile.phone.number.not.valid=手机号格式错误\nuser.login.success=登录成功\nuser.register.success=注册成功\nuser.notfound=请重新登录\nuser.forcelogout=管理员强制退出，请重新登录\nuser.unknown.error=未知错误，请重新登录\n\n##文件上传消息\nupload.exceed.maxSize=上传的文件大小超出限制的文件大小！<br/>允许的文件最大大小是：{0}MB！\nupload.filename.exceed.length=上传的文件名最长{0}个字符\n\n##权限\nno.permission=您没有数据的权限，请联系管理员添加权限 [{0}]\nno.create.permission=您没有创建数据的权限，请联系管理员添加权限 [{0}]\nno.update.permission=您没有修改数据的权限，请联系管理员添加权限 [{0}]\nno.delete.permission=您没有删除数据的权限，请联系管理员添加权限 [{0}]\nno.export.permission=您没有导出数据的权限，请联系管理员添加权限 [{0}]\nno.view.permission=您没有查看数据的权限，请联系管理员添加权限 [{0}]\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-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>ginyi-server</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-server-common</artifactId>\n\n    <dependencies>\n\n    </dependencies>\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-web/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>ginyi-server</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-server-web</artifactId>\n\n    <dependencies>\n\n        <!-- swagger2接口文档 -->\n        <dependency>\n            <groupId>io.springfox</groupId>\n            <artifactId>springfox-swagger2</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.springfox</groupId>\n            <artifactId>springfox-swagger-ui</artifactId>\n        </dependency>\n\n        <!-- Knife4j -->\n        <dependency>\n            <groupId>com.github.xiaoymin</groupId>\n            <artifactId>knife4j-spring-boot-starter</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/resources</directory>\n                <includes>\n                    <include>**/*.*</include>\n                </includes>\n                <filtering>false</filtering>\n            </resource>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <include>**/*.xml</include>\n                </includes>\n                <filtering>false</filtering>\n            </resource>\n        </resources>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring-boot.version}</version>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-web/src/main/java/ginyi/server/web/WebApplication.java",
    "content": "package ginyi.server.web;\n\nimport ginyi.framework.core.config.AppConfig;\nimport lombok.extern.slf4j.Slf4j;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.data.redis.core.RedisTemplate;\n\nimport javax.sql.DataSource;\nimport java.net.UnknownHostException;\n\n@Slf4j\n// 扫描系统服务的包以及自身所在模块的包\n@SpringBootApplication(scanBasePackages = {\n        \"ginyi.system\",\n        \"ginyi.common\",\n        \"ginyi.framework.websocket\",\n        \"ginyi.framework.security\",\n        \"ginyi.server.web\"\n})\n@MapperScan({\n        \"ginyi.system.**.mapper\",\n        \"ginyi.server.admin.**.mapper\"\n})\npublic class WebApplication {\n\n    public static void main(String[] args) throws UnknownHostException {\n        ConfigurableApplicationContext context = SpringApplication.run(WebApplication.class, args);\n        AppConfig.printAppInfo(context);\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-web/src/main/java/ginyi/server/web/controller/Test.java",
    "content": "package ginyi.server.web.controller;\n\n\nimport ginyi.common.result.CommonResult;\nimport ginyi.system.domain.model.dto.LoginDto;\nimport ginyi.system.domain.model.vo.LoginVo;\nimport ginyi.system.service.ISysLoginService;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\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.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/api/user\")\n@Api(tags = \"登录模块\")\n@Slf4j\npublic class Test {\n\n    @Autowired\n    private ISysLoginService loginService;\n\n    @ApiOperation(\"测试用户登录\")\n    @PostMapping(\"/login\")\n    public CommonResult<LoginVo> login(@RequestBody @Validated LoginDto loginDto) {\n        LoginVo loginVo = loginService.login(loginDto);\n        return CommonResult.success(loginVo);\n    }\n\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-web/src/main/java/ginyi/server/web/swagger/WebSwagger2Config.java",
    "content": "package ginyi.server.web.swagger;\n\nimport ginyi.common.swagger.Swagger2Config;\nimport org.springframework.context.annotation.Configuration;\nimport springfox.documentation.swagger2.annotations.EnableSwagger2;\n\n@EnableSwagger2\n@Configuration\npublic class WebSwagger2Config extends Swagger2Config {\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/ginyi-server-web/src/main/resources/config/application.yml",
    "content": "# 说明：\n#     1、这是 ginyi-server-web 模块下特有的配置项\n#     2、更多全局通用的配置项在 ginyi-system 系统应用服务下\n#     3、如果需要对重写全局的配置项的话，可以将其对应的属性配置拷贝至本配置中，会自动覆盖！\n\n\n# 应用服务\nserver:\n  # 服务端口\n  port: 8055\n\n\n# 接口文档的配置\nswagger:\n  group-name: web\n  # 接口文档url\n  swagger-url: 写点什么呢~~~\n  # 接口文档标题\n  swagger-title: Ginyi-web api doc\n  # 接口文档版本号\n  swaggerversion: 1.0\n  # 接口文档描述\n  swagger-description: 这是swagger接口文档~\n\n"
  },
  {
    "path": "ginyi-springboot/ginyi-server/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>ginyi-springboot</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-server</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>ginyi-server-web</module>\n        <module>ginyi-server-admin</module>\n        <module>ginyi-server-common</module>\n    </modules>\n\n    <dependencies>\n\n        <!-- 核心框架 -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-framework-security</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n\n        <!-- 項目配置 -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-framework-core</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n\n        <!-- websocket -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-framework-websocket</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n\n    </dependencies>\n\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-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>ginyi-springboot</artifactId>\n        <groupId>com.ginyi</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>ginyi-system</artifactId>\n\n    <dependencies>\n\n        <!-- SpringBoot Web容器 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!--测试依赖-->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <!-- 公用redis -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-common-redis</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n\n        <!-- 公用mysql -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-common-mysql</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n\n        <!-- 公用swagger -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-common-swagger</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n\n        <!-- 公用异常处理 -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-common-exception</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n\n        <!-- 公用结果集 -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-common-result</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n\n        <!-- 公用工具类 -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-common-utils</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n\n        <!-- 通用静态常量 -->\n        <dependency>\n            <groupId>com.ginyi</groupId>\n            <artifactId>ginyi-common-constants</artifactId>\n            <version>${ginyi.version}</version>\n        </dependency>\n\n        <!-- 参数校验工具 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- MongoDB -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-mongodb</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/BaseEntity.java",
    "content": "package ginyi.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport ginyi.common.annotation.CreateBy;\nimport ginyi.common.annotation.CreateTime;\nimport ginyi.common.annotation.UpdateBy;\nimport ginyi.common.annotation.UpdateTime;\nimport io.swagger.annotations.ApiModelProperty;\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@Data\npublic class BaseEntity implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 搜索值\n     */\n    @JsonIgnore\n    @TableField(exist = false, select = false)\n    private String searchValue;\n\n    /**\n     * 创建者\n     */\n    @ApiModelProperty(\"创建者\")\n    @CreateBy\n    @TableField(fill = FieldFill.INSERT)\n    private String createBy;\n\n    /**\n     * 创建时间\n     */\n    @JsonFormat\n    @ApiModelProperty(\"创建时间\")\n    @CreateTime\n    @TableField(fill = FieldFill.INSERT)\n    private Date createTime;\n\n    /**\n     * 更新者\n     */\n    @ApiModelProperty(\"更新者\")\n    @UpdateBy\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private String updateBy;\n\n    /**\n     * 更新时间\n     */\n    @JsonFormat\n    @ApiModelProperty(\"更新时间\")\n    @UpdateTime\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private Date updateTime;\n\n    /**\n     * 备注\n     */\n    @ApiModelProperty(\"备注\")\n    private String remark;\n\n    /**\n     * 请求参数\n     */\n    @JsonInclude(JsonInclude.Include.NON_EMPTY)\n    @TableField(exist = false, select = false)\n    private Map<String, Object> params;\n\n\n    public Map<String, Object> getParams() {\n        if (params == null) {\n            params = new HashMap<>();\n        }\n        return params;\n    }\n\n    public void setParams(Map<String, Object> params) {\n        this.params = params;\n    }\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/LoginUser.java",
    "content": "package ginyi.system.domain;\n\nimport com.alibaba.fastjson2.annotation.JSONField;\nimport ginyi.system.domain.SysUser;\nimport lombok.Data;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\n\nimport java.util.Collection;\nimport java.util.Set;\n\n/**\n * 登录用户身份权限\n *\n * @author ruoyi\n */\n@Data\npublic class LoginUser implements UserDetails {\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 token;\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> permissions;\n\n    /**\n     * 用户信息\n     */\n    private SysUser user;\n\n    public LoginUser() {\n    }\n\n    public LoginUser(SysUser user, Set<String> permissions) {\n        this.user = user;\n        this.permissions = permissions;\n    }\n\n    public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions) {\n        this.userId = userId;\n        this.deptId = deptId;\n        this.user = user;\n        this.permissions = permissions;\n    }\n\n    @JSONField(serialize = false)\n    @Override\n    public String getPassword() {\n        return user.getPassword();\n    }\n\n    @Override\n    public String getUsername() {\n        return user.getUserName();\n    }\n\n    /**\n     * 账户是否未过期,过期无法验证\n     */\n    @JSONField(serialize = false)\n    @Override\n    public boolean isAccountNonExpired() {\n        return true;\n    }\n\n    /**\n     * 指定用户是否解锁,锁定的用户无法进行身份验证\n     *\n     * @return\n     */\n    @JSONField(serialize = false)\n    @Override\n    public boolean isAccountNonLocked() {\n        return true;\n    }\n\n    /**\n     * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证\n     *\n     * @return\n     */\n    @JSONField(serialize = false)\n    @Override\n    public boolean isCredentialsNonExpired() {\n        return true;\n    }\n\n    /**\n     * 是否可用 ,禁用的用户不能身份验证\n     *\n     * @return\n     */\n    @JSONField(serialize = false)\n    @Override\n    public boolean isEnabled() {\n        return true;\n    }\n\n\n    @Override\n    public Collection<? extends GrantedAuthority> getAuthorities() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/Sys.java",
    "content": "package ginyi.system.domain;\n\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\n/**\n * 系统相关信息\n *\n * @author ruoyi\n */\n@Data\npublic class Sys {\n    /**\n     * 服务器名称\n     */\n    @ApiModelProperty(\"服务器名称\")\n    private String computerName;\n\n    /**\n     * 服务器Ip\n     */\n    @ApiModelProperty(\"服务器ip\")\n    private String computerIp;\n\n    /**\n     * 项目路径\n     */\n    @ApiModelProperty(\"项目路径\")\n    private String userDir;\n\n    /**\n     * 操作系统\n     */\n    @ApiModelProperty(\"操作系统\")\n    private String osName;\n\n    /**\n     * 系统架构\n     */\n    @ApiModelProperty(\"系统架构\")\n    private String osArch;\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysConfig.java",
    "content": "package ginyi.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\nimport org.apache.commons.lang3.builder.ToStringStyle;\n\n/**\n * 参数配置表 sys_config\n *\n * @author ruoyi\n */\n@Data\npublic class SysConfig extends BaseEntity {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 参数主键\n     */\n    @TableId\n    private Long configId;\n\n    /**\n     * 参数名称\n     */\n    private String configName;\n\n    /**\n     * 参数键名\n     */\n    private String configKey;\n\n    /**\n     * 参数键值\n     */\n    private String configValue;\n\n    /**\n     * 系统内置（Y是 N否）\n     */\n    private String configType;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysCpu.java",
    "content": "package ginyi.system.domain;\n\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\n/**\n * CPU相关信息\n * @author ruoyi\n */\n@Data\npublic class SysCpu {\n    /**\n     * 核心数，直接回显数字\n     */\n    @ApiModelProperty(\"核心数\")\n    private int cpuNum;\n\n    /**\n     * CPU总的使用率，不需要回显\n     */\n    @ApiModelProperty(\"CPU总使用率，不需要回显\")\n    private double total;\n\n    /**\n     * CPU系统使用率，拼接 % 回显\n     */\n    @ApiModelProperty(\"CPU系统使用率\")\n    private double sys;\n\n    /**\n     * CPU用户使用率，拼接 % 回显\n     */\n    @ApiModelProperty(\"CPU用户使用率\")\n    private double used;\n\n    /**\n     * CPU当前等待率，拼接 % 回显\n     */\n    @ApiModelProperty(\"CPU当前等待率\")\n    private double wait;\n\n    /**\n     * CPU当前空闲率，拼接 % 回显\n     */\n    @ApiModelProperty(\"CPU当前空闲率\")\n    private double free;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysDept.java",
    "content": "package ginyi.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\nimport org.apache.commons.lang3.builder.ToStringStyle;\n\nimport javax.validation.constraints.Email;\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 部门表 sys_dept\n *\n * @author ruoyi\n */\n@EqualsAndHashCode(callSuper = true)\n@Data\npublic class SysDept extends BaseEntity {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 部门ID\n     */\n    @ApiModelProperty(\"部门id\")\n    @TableId\n    private Long deptId;\n\n    /**\n     * 父部门ID\n     */\n    @ApiModelProperty(\"父级id\")\n    private Long parentId;\n\n    /**\n     * 祖级列表\n     */\n    private String ancestors;\n\n    /**\n     * 部门名称\n     */\n    @ApiModelProperty(\"部门名称\")\n    @NotBlank(message = \"部门名称不能为空\")\n    @Size(min = 0, max = 30, message = \"部门名称长度不能超过30个字符\")\n    private String deptName;\n\n    /**\n     * 显示顺序\n     */\n    @ApiModelProperty(\"显示顺序\")\n    @NotNull(message = \"显示顺序不能为空\")\n    private Integer sort;\n\n    /**\n     * 负责人\n     */\n    @ApiModelProperty(\"部门负责人\")\n    private String leader;\n\n    /**\n     * 联系电话\n     */\n    @ApiModelProperty(\"部门负责人电话\")\n    @Size(min = 0, max = 11, message = \"联系电话长度不能超过11个字符\")\n    private String phone;\n\n    /**\n     * 邮箱\n     */\n    @ApiModelProperty(\"部门负责人邮箱\")\n    @Email(message = \"邮箱格式不正确\")\n    @Size(min = 0, max = 50, message = \"邮箱长度不能超过50个字符\")\n    private String email;\n\n    /**\n     * 部门状态:0正常,1停用\n     */\n    @ApiModelProperty(\"状态，0正常，1停用\")\n    private String status;\n\n    /**\n     * 删除标志\n     */\n    @TableLogic\n    private String deleted;\n\n\n    /**\n     * 父部门名称\n     */\n    @TableField(select = false, exist = false)\n    @ApiModelProperty(\"父级部门名称\")\n    private String parentName;\n\n    /**\n     * 子部门\n     */\n    @TableField(select = false, exist = false)\n    @ApiModelProperty(\"子部门\")\n    private List<SysDept> children = new ArrayList<SysDept>();\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysFile.java",
    "content": "package ginyi.system.domain;\n\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\n/**\n * 系统文件相关信息\n *\n * @author ruoyi\n */\n@Data\n\npublic class SysFile {\n    /**\n     * 盘符路径\n     */\n    @ApiModelProperty(\"盘符路径\")\n    private String dirName;\n\n    /**\n     * 文件类型\n     */\n    @ApiModelProperty(\"文件类型\")\n    private String typeName;\n\n    /**\n     * 总大小，单位G\n     */\n    @ApiModelProperty(\"总大小，单位G\")\n    private double total;\n\n    /**\n     * 剩余大小，单位G\n     */\n    @ApiModelProperty(\"可用大小，单位G\")\n    private double free;\n\n    /**\n     * 已经使用量，单位G\n     */\n    @ApiModelProperty(\"已用大小，单位G\")\n    private double used;\n\n    /**\n     * 资源的使用率\n     */\n    @ApiModelProperty(\"已用百分比\")\n    private double usage;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysJvm.java",
    "content": "package ginyi.system.domain;\n\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\n/**\n * JVM相关信息\n *\n * @author ruoyi\n */\n@Data\npublic class SysJvm {\n    /**\n     * 当前JVM占用的内存总数，单位M\n     */\n    @ApiModelProperty(\"当前JVM占用的内存总数，单位M\")\n    private double total;\n\n    /**\n     * JVM最大可用内存总数，单位M\n     */\n    @ApiModelProperty(\"JVM最大可用内存总数，单位M\")\n    private double max;\n\n    /**\n     * JVM空闲内存，单位M\n     */\n    @ApiModelProperty(\"JVM空闲内存，单位M\")\n    private double free;\n\n    /**\n     * JDK名称\n     */\n    @ApiModelProperty(\"JDK名称\")\n    private String name;\n\n    /**\n     * JDK版本\n     */\n    @ApiModelProperty(\"JDK版本\")\n    private String version;\n\n    /**\n     * JDK路径\n     */\n    @ApiModelProperty(\"JDK路径\")\n    private String home;\n\n    /**\n     * 已用内存，单位M\n     */\n    @ApiModelProperty(\"已用内存，单位M\")\n    private double used;\n\n    /**\n     * 使用率\n     */\n    @ApiModelProperty(\"内存使用率\")\n    private double usage;\n\n    /**\n     * 运行参数\n     */\n    @ApiModelProperty(\"运行参数\")\n    private String inputArgs;\n\n\n    /**\n     * 运行时间\n     */\n    @ApiModelProperty(\"运行时间\")\n    private String runTime;\n\n    /**\n     * 启动时间\n     */\n    @ApiModelProperty(\"启动时间\")\n    private String startTime;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysLogLogin.java",
    "content": "package ginyi.system.domain;\n\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport org.springframework.data.mongodb.core.mapping.Document;\n\nimport java.util.Date;\n\n@Data\n@ApiModel(\"登录日志返回数据\")\n@Document(\"sys_log_login\")\npublic class SysLogLogin extends BaseEntity {\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"ID\")\n    private String id;\n\n    @ApiModelProperty(\"用户账号\")\n    private String userName;\n\n    @ApiModelProperty(\"登录状态 0成功 1失败\")\n    private String status;\n\n    @ApiModelProperty(\"登录IP地址\")\n    private String ipaddr;\n\n    @ApiModelProperty(\"登录地点\")\n    private String loginLocation;\n\n    @ApiModelProperty(\"浏览器类型\")\n    private String browser;\n\n    @ApiModelProperty(\"操作系统\")\n    private String os;\n\n    @ApiModelProperty(\"提示消息\")\n    private String msg;\n\n    @JsonFormat\n    @ApiModelProperty(\"登录时间\")\n    private Date loginTime;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysLogOperation.java",
    "content": "package ginyi.system.domain;\n\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springframework.data.mongodb.core.mapping.Document;\n\nimport java.util.Date;\n\n\n@EqualsAndHashCode(callSuper = true)\n@Data\n@ApiModel(\"操作日志返回数据\")\n@Document(\"sys_log_operation\")\npublic class SysLogOperation extends BaseEntity {\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"ID\")\n    private String id;\n\n    @ApiModelProperty(\"操作模块\")\n    private String title;\n\n    @ApiModelProperty(\"业务类型（0其它 1新增 2修改 3删除）\")\n    private Integer businessType;\n\n    @ApiModelProperty(\"业务类型数组\")\n    private Integer[] businessTypes;\n\n    @ApiModelProperty(\"请求方法\")\n    private String method;\n\n    @ApiModelProperty(\"请求方式\")\n    private String requestMethod;\n\n    @ApiModelProperty(\"操作类别（0其它 1后台用户 2手机端用户）\")\n    private Integer operatorType;\n\n    @ApiModelProperty(\"操作人员\")\n    private String operationName;\n\n    @ApiModelProperty(\"部门名称\")\n    private String deptName;\n\n    @ApiModelProperty(\"请求URL\")\n    private String operationUrl;\n\n    @ApiModelProperty(\"操作地址\")\n    private String operationIp;\n\n    @ApiModelProperty(\"操作地点\")\n    private String operationLocation;\n\n    @ApiModelProperty(\"请求参数\")\n    private String operationParam;\n\n    @ApiModelProperty(\"返回参数\")\n    private String jsonResult;\n\n    @ApiModelProperty(\"操作状态（0正常 1异常）\")\n    private Integer status;\n\n    @ApiModelProperty(\"错误消息\")\n    private String errorMsg;\n\n    @JsonFormat\n    @ApiModelProperty(\"操作时间\")\n    private Date operationTime;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysMemory.java",
    "content": "package ginyi.system.domain;\n\n\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\n/**\n * 內存相关信息\n *\n * @author ruoyi\n */\n@Data\npublic class SysMemory {\n    /**\n     * 内存总量，单位G\n     */\n    @ApiModelProperty(\"内存总数，单位G\")\n    private double total;\n\n    /**\n     * 已用内存，单位G\n     */\n    @ApiModelProperty(\"已用内存，单位G\")\n    private double used;\n\n    /**\n     * 剩余内存，单位G\n     */\n    @ApiModelProperty(\"可用内存，单位G\")\n    private double free;\n\n    /**\n     * 内存使用率\n     */\n    @ApiModelProperty(\"使用率\")\n    private double usage;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysMenu.java",
    "content": "package ginyi.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 菜单权限表 sys_menu\n *\n * @author ruoyi\n */\n@Data\npublic class SysMenu extends BaseEntity {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 菜单ID\n     */\n    @TableId\n    @ApiModelProperty(\"菜单ID\")\n    private Long menuId;\n\n    /**\n     * 菜单名称\n     */\n    @ApiModelProperty(\"菜单名称\")\n    private String menuName;\n\n    /**\n     * 父菜单名称\n     */\n    @ApiModelProperty(\"父级菜单名称\")\n    @TableField(exist = false, select = false)\n    private String parentName;\n\n    /**\n     * 父菜单ID\n     */\n    @ApiModelProperty(\"父级菜单ID\")\n    private Long parentId;\n\n    /**\n     * 路由name，用于路由跳转\n     */\n    @ApiModelProperty(\"路由名称\")\n    private String name;\n\n    /**\n     * 显示顺序\n     */\n    @ApiModelProperty(\"显示顺序\")\n    private Integer sort;\n\n    /**\n     * 路由地址\n     */\n    @ApiModelProperty(\"路由地址\")\n    @TableField(value = \"path\")\n    private String path;\n\n    /**\n     * 组件路径\n     */\n    @ApiModelProperty(\"组件路径\")\n    private String component;\n\n    /**\n     * 路由参数\n     */\n    @ApiModelProperty(\"路由参数\")\n    private String query;\n\n    /**\n     * 是否为外链（0是 1否）\n     */\n    @ApiModelProperty(\"是否为外链（0是 1否）\")\n    private String isFrame;\n\n    /**\n     * 是否缓存（0缓存 1不缓存）\n     */\n    @ApiModelProperty(\"是否缓存（0缓存 1不缓存）\")\n    private String isCache;\n\n    /**\n     * 类型（M目录 C菜单 F按钮）\n     */\n    @ApiModelProperty(\"类型（M目录 C菜单 F按钮）\")\n    private String menuType;\n\n    /**\n     * 显示状态（0显示 1隐藏）\n     */\n    @ApiModelProperty(\"显示状态（0显示 1隐藏）\")\n    private String visible;\n\n    /**\n     * 菜单状态（0正常 1停用）\n     */\n    @ApiModelProperty(\"菜单状态（0正常 1停用）\")\n    private String status;\n\n    /**\n     * 权限字符串\n     */\n    @ApiModelProperty(\"权限字符串\")\n    private String perms;\n\n    /**\n     * 菜单图标\n     */\n    @ApiModelProperty(\"菜单图标\")\n    private String icon;\n\n    /**\n     * 子菜单\n     */\n    @ApiModelProperty(\"子菜单\")\n    @TableField(exist = false, select = false)\n    private List<SysMenu> children = new ArrayList<SysMenu>();\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysNotice.java",
    "content": "package ginyi.system.domain;\n\nimport ginyi.system.domain.BaseEntity;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport java.util.List;\n\n@EqualsAndHashCode(callSuper = true)\n@Data\npublic class SysNotice extends BaseEntity {\n\n    /**\n     * 通知的id\n     */\n    @ApiModelProperty(\"通知的id\")\n    private Long noticeId;\n\n    /**\n     * 通知标题\n     */\n    @ApiModelProperty(\"通知标题\")\n    private String title;\n\n    /**\n     * 通知的类型（0通知，1公告）\n     */\n    @ApiModelProperty(\"通知的类型（0通知，1公告）\")\n    private String type;\n\n    /**\n     * 通知的内容\n     */\n    @ApiModelProperty(\"通知的内容\")\n    private String content;\n\n    /**\n     * 通知的用户\n     */\n    @ApiModelProperty(\"通知的用户\")\n    private List<Long> userIds;\n\n    /**\n     * 已读的用户\n     */\n    @ApiModelProperty(\"已读的用户\")\n    private List<Long> userReadIds;\n\n    /**\n     * 通知的状态（0正常，1关闭）\n     */\n    @ApiModelProperty(\"通知的状态（0正常，1关闭）\")\n    private String status;\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysPost.java",
    "content": "package ginyi.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.apache.ibatis.annotations.Select;\n\n/**\n * 岗位表 sys_post\n *\n * @author ruoyi\n */\n@EqualsAndHashCode(callSuper = true)\n@Data\npublic class SysPost extends BaseEntity {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 岗位序号\n     */\n    @TableId\n    private Long postId;\n\n    /**\n     * 岗位编码\n     */\n    private String postCode;\n\n    /**\n     * 岗位名称\n     */\n    private String postName;\n\n    /**\n     * 岗位排序\n     */\n    private Integer sort;\n\n    /**\n     * 状态（0正常 1停用）\n     */\n    private String status;\n\n    /**\n     * 用户是否存在此岗位标识 默认不存在\n     */\n    @TableField(select = false, exist = false)\n    private boolean flag = false;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysRole.java",
    "content": "package ginyi.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport lombok.Data;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\nimport org.apache.commons.lang3.builder.ToStringStyle;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.util.Set;\n\n/**\n * 角色表 sys_role\n *\n * @author ruoyi\n */\n@Data\npublic class SysRole extends BaseEntity {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 角色ID\n     */\n    @TableId\n    private Long roleId;\n\n    /**\n     * 角色名称\n     */\n    @NotBlank(message = \"角色名称不能为空\")\n    @Size(min = 0, max = 30, message = \"角色名称长度不能超过30个字符\")\n    private String roleName;\n\n    /**\n     * 角色权限\n     */\n    @NotBlank(message = \"权限字符不能为空\")\n    @Size(min = 0, max = 100, message = \"权限字符长度不能超过100个字符\")\n    private String roleKey;\n\n    /**\n     * 角色排序\n     */\n    @NotNull(message = \"显示顺序不能为空\")\n    private Integer sort;\n\n    /**\n     * 数据范围（1：所有数据权限；2：自定义数据权限；3：本部门数据权限；4：本部门及以下数据权限；5：仅本人数据权限）\n     */\n    @TableField(select = false, exist = false)\n    private String dataScope;\n\n    /**\n     * 菜单树选择项是否关联显示（ 0：父子不互相关联显示 1：父子互相关联显示）\n     */\n    @TableField(select = false, exist = false)\n    private boolean menuCheckStrictly;\n\n    /**\n     * 部门树选择项是否关联显示（0：父子不互相关联显示 1：父子互相关联显示 ）\n     */\n    @TableField(select = false, exist = false)\n    private boolean deptCheckStrictly;\n\n    /**\n     * 角色状态（0正常 1停用）\n     */\n    private String status;\n\n    /**\n     * 删除标志（0代表存在 2代表删除）\n     */\n    @TableLogic\n    private String deleted;\n\n    /**\n     * 用户是否存在此角色标识 默认不存在\n     */\n    @TableField(select = false, exist = false)\n    private boolean flag = false;\n\n    /**\n     * 菜单组\n     */\n    @TableField(select = false, exist = false)\n    private Long[] menuIds;\n\n    /**\n     * 部门组（数据权限）\n     */\n    @TableField(select = false, exist = false)\n    private Long[] deptIds;\n\n    /**\n     * 角色菜单权限\n     */\n    @TableField(select = false, exist = false)\n    private Set<String> permissions;\n\n\n    public boolean isAdmin() {\n        return isAdmin(this.roleId);\n    }\n\n    public static boolean isAdmin(Long roleId) {\n        return roleId != null && 1L == roleId;\n    }\n\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysServer.java",
    "content": "package ginyi.system.domain;\n\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * 服务器相关信息\n *\n * @author ruoyi\n */\n@Data\npublic class SysServer {\n\n    /**\n     * 服务器信息\n     */\n    @ApiModelProperty(\"服务器信息\")\n    private Sys sys;\n\n    /**\n     * cpu\n     */\n    @ApiModelProperty(\"cpu相关信息\")\n    private SysCpu cpu;\n\n    /**\n     * 硬盘\n     */\n    @ApiModelProperty(\"系统硬盘\")\n    private List<SysFile> file;\n\n    /**\n     * jvm\n     */\n    @ApiModelProperty(\"Java虚拟机\")\n    private SysJvm jvm;\n\n    /**\n     * 内存\n     */\n    @ApiModelProperty(\"内存信息\")\n    private SysMemory memory;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysUser.java",
    "content": "package ginyi.system.domain;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport lombok.Data;\n\nimport java.util.Date;\nimport java.util.List;\n\n@Data\npublic class SysUser extends BaseEntity {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 用户ID\n     */\n    @TableId\n    private Long userId;\n\n    /**\n     * 部门ID\n     */\n    private Long deptId;\n\n    /**\n     * 用户账号\n     */\n    private String userName;\n\n    /**\n     * 用户昵称\n     */\n    private String nickName;\n\n    /**\n     * 用户邮箱\n     */\n    private String email;\n\n    /**\n     * 手机号码\n     */\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    private String password;\n\n    /**\n     * 帐号状态（0正常 1停用）\n     */\n    private String status;\n\n    /**\n     * 删除标志（0代表存在 1代表删除）\n     */\n    @TableLogic\n    private String deleted;\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, select = false)\n    private SysDept dept;\n\n    /**\n     * 角色对象\n     */\n    @TableField(exist = false, select = false)\n    private List<SysRole> roles;\n\n    /**\n     * 角色组\n     */\n    @TableField(exist = false, select = false)\n    private Long[] roleIds;\n\n    /**\n     * 岗位对象\n     */\n    @TableField(exist = false, select = false)\n    private List<SysPost> posts;\n\n    /**\n     * 岗位组\n     */\n    @TableField(exist = false, select = false)\n    private Long[] postIds;\n\n    /**\n     * 角色ID\n     */\n    @TableField(exist = false, select = false)\n    private Long roleId;\n\n\n    public boolean isAdmin() {\n        return isAdmin(this.userId);\n    }\n\n    public static boolean isAdmin(Long userId) {\n        return userId != null && 1L == userId;\n    }\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/CacheDto.java",
    "content": "package ginyi.system.domain.model.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\n@ApiModel(\"缓存数据请求参数\")\n@Data\npublic class CacheDto {\n\n    /**\n     * 键名\n     */\n    @ApiModelProperty(\"键名\")\n    private String key;\n\n    /**\n     * 类型\n     */\n    @ApiModelProperty(\"数据类型\")\n    private String type;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/DeptDto.java",
    "content": "package ginyi.system.domain.model.dto;\n\nimport ginyi.common.swagger.AddGroup;\nimport ginyi.common.swagger.UpdateGroup;\nimport ginyi.system.domain.BaseEntity;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.*;\nimport java.util.Date;\nimport java.util.List;\n\n@EqualsAndHashCode(callSuper = true)\n@Data\npublic class DeptDto extends BaseEntity {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"部门id\")\n    @NotNull(groups = UpdateGroup.class, message = \"部门id不能为空\")\n    private Long deptId;\n\n    /**\n     * 根据部门ids获取用户列表专用\n     */\n    @ApiModelProperty(\"部门id（根据部门ids获取用户列表专用）\")\n    private List<Long> deptIds;\n\n    @ApiModelProperty(\"父级id\")\n    private Long parentId;\n\n    private String ancestors;\n\n    @ApiModelProperty(\"部门名称\")\n    @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = \"部门名称不能为空\")\n    @Size(min = 0, max = 30, message = \"部门名称长度不能超过30个字符\")\n    private String deptName;\n\n    @ApiModelProperty(\"显示顺序\")\n    @NotNull(groups = {AddGroup.class, UpdateGroup.class}, message = \"显示顺序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(\"部门负责人\")\n    private String leader;\n\n    @ApiModelProperty(\"部门负责人电话\")\n    @Pattern(regexp = \"(?:0|86|\\\\+86)?1[3-9]\\\\d{9}\", message = \"手机号码格式不正确\")\n    private String phone;\n\n    @ApiModelProperty(\"部门负责人邮箱\")\n    @Email(message = \"邮箱格式不正确\")\n    @Size(min = 0, max = 50, message = \"邮箱长度不能超过50个字符\")\n    private String email;\n\n    @ApiModelProperty(\"状态，0正常，1停用\")\n    @Size(min = 0, max = 1, message = \"状态不合法，0正常，1停用\")\n    private String status;\n\n    @ApiModelProperty(\"创建时间，开始时间\")\n    private Date beginTime;\n\n    @ApiModelProperty(\"创建时间，结束时间\")\n    private Date endTime;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/LoginDto.java",
    "content": "package ginyi.system.domain.model.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport org.hibernate.validator.constraints.Length;\n\nimport javax.validation.constraints.NotEmpty;\n\n/**\n * 用户登录对象\n *\n * @author ruoyi\n */\n@Data\n@ApiModel(\"登录用户参数\")\npublic class LoginDto {\n\n    @ApiModelProperty(value = \"用户名\", required = true)\n    @NotEmpty(message = \"用户名不能为空\")\n    @Length(min = 5, max = 15, message = \"用户名长度必须在5~15个字符之间\")\n    private String username;\n\n    @ApiModelProperty(value = \"密码\", required = true)\n    @NotEmpty(message = \"密码不能为空\")\n    @Length(min = 6, max = 15, message = \"密码长度必须在6~15个字符之间\")\n    private String password;\n\n    @ApiModelProperty(value = \"验证码\")\n    private String code;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/MenuDto.java",
    "content": "package ginyi.system.domain.model.dto;\n\nimport ginyi.common.swagger.AddGroup;\nimport ginyi.common.swagger.UpdateGroup;\nimport ginyi.system.domain.BaseEntity;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\n\n@EqualsAndHashCode(callSuper = true)\n@Data\n@ApiModel(\"系统菜单请求参数\")\npublic class MenuDto extends BaseEntity {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"菜单id\")\n    @NotNull(groups = UpdateGroup.class, message = \"菜单id不能为空\")\n    private Long menuId;\n\n    @ApiModelProperty(\"菜单名称\")\n    @Size(max = 50, message = \"菜单名称长度不能超过50个字符\")\n    @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = \"菜单名称不能为空\")\n    private String menuName;\n\n    @ApiModelProperty(\"父级id\")\n    private Long parentId;\n\n    @ApiModelProperty(\"排序（1升序，0降序）\")\n    @NotNull(groups = {AddGroup.class, UpdateGroup.class}, message = \"排序不能为空\")\n    private Integer sort = 1;\n\n    @ApiModelProperty(\"路由名称\")\n    @Size(max = 50, message = \"路由名称长度不得超过50个字符\")\n    private String name;\n\n    @ApiModelProperty(\"路由地址\")\n    @Size(max = 200, message = \"路由地址长度不得超过200个字符\")\n    private String path;\n\n    @ApiModelProperty(\"组件路径\")\n    @Size(max = 200, message = \"组件路径长度不得超过200个字符\")\n    private String component;\n\n    @ApiModelProperty(\"类型（M目录 C菜单 F按钮）\")\n    @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = \"菜单类型不能为空\")\n    private String menuType;\n\n    @ApiModelProperty(\"显示状态（0显示 1隐藏）\")\n    @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = \"显示状态不能为空\")\n    private String visible;\n\n    @ApiModelProperty(\"菜单状态（0正常 1停用）\")\n    @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = \"菜单状态不能为空\")\n    private String status;\n\n    @ApiModelProperty(\"路由参数\")\n    private String query;\n\n    @ApiModelProperty(\"是否为外链（0是 1否）\")\n    private String isFrame;\n\n    @ApiModelProperty(\"是否缓存（0缓存 1不缓存）\")\n    private String isCache;\n\n    @ApiModelProperty(\"权限字符串\")\n    private String perms;\n\n    @ApiModelProperty(\"菜单图标\")\n    private String icon;\n\n    @ApiModelProperty(\"过滤按钮（0过滤 1不过滤）\")\n    private String filterButton;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/NoticeDto.java",
    "content": "package ginyi.system.domain.model.dto;\n\nimport ginyi.common.swagger.AddGroup;\nimport ginyi.common.swagger.UpdateGroup;\nimport ginyi.system.domain.BaseEntity;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.util.List;\n\n@EqualsAndHashCode(callSuper = true)\n@Data\n@ApiModel(\"系统通知公告请求参数\")\npublic class NoticeDto extends BaseEntity {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 通知的id\n     */\n    @ApiModelProperty(\"通知的id\")\n    @NotNull(groups = UpdateGroup.class, message = \"通知id不能为空\")\n    private Long noticeId;\n\n    /**\n     * 通知标题\n     */\n    @ApiModelProperty(\"通知标题\")\n    @Size(max = 50, message = \"通知标题长度不能超过50个字符\")\n    @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = \"通知标题不能为空\")\n    private String title;\n\n    /**\n     * 通知的类型（0通知，1公告）\n     */\n    @ApiModelProperty(\"通知的类型（0通知，1公告）\")\n    @NotEmpty(groups = {AddGroup.class, UpdateGroup.class}, message = \"通知的类型不能为空\")\n    private String type;\n\n    /**\n     * 通知的内容\n     */\n    @ApiModelProperty(\"通知的内容\")\n    @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = \"通知的内容不能为空\")\n    private String content;\n\n    /**\n     * 通知的用户\n     */\n    @ApiModelProperty(\"通知的用户\")\n    private List<Long> userIds;\n\n\n    /**\n     * 已读的用户\n     */\n    @ApiModelProperty(\"已读的用户\")\n    private List<Long> userReadIds;\n\n    /**\n     * 通知的状态（0正常，1关闭）\n     */\n    @ApiModelProperty(\"通知的状态（0正常，1关闭）\")\n    @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = \"通知的状态不能为空\")\n    private String status;\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/PostDto.java",
    "content": "package ginyi.system.domain.model.dto;\n\nimport ginyi.common.swagger.AddGroup;\nimport ginyi.common.swagger.UpdateGroup;\nimport ginyi.system.domain.BaseEntity;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.util.Date;\nimport java.util.List;\n\n@EqualsAndHashCode(callSuper = true)\n@Data\n@ApiModel(\"系统岗位参数\")\npublic class PostDto extends BaseEntity {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"岗位id\")\n    @NotNull(groups = UpdateGroup.class, message = \"岗位id不能为空\")\n    private Long postId;\n\n    /**\n     * 根据岗位ids获取用户列表专用\n     */\n    @ApiModelProperty(\"岗位id（根据岗位ids获取用户列表专用）\")\n    private List<Long> postIds;\n\n    @ApiModelProperty(\"岗位编码\")\n    @NotBlank(groups = {UpdateGroup.class, AddGroup.class}, message = \"岗位编码不能为空\")\n    private String postCode;\n\n    @ApiModelProperty(\"岗位名称\")\n    @NotBlank(groups = {UpdateGroup.class, AddGroup.class}, message = \"岗位名称不能为空\")\n    private String postName;\n\n    @ApiModelProperty(\"岗位排序\")\n    @NotNull(groups = {UpdateGroup.class, AddGroup.class}, message = \"岗位顺序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(\"状态（0正常 1停用）\")\n    @Size(min = 0, max = 1, message = \"状态不合法\")\n    @NotBlank(groups = {UpdateGroup.class, AddGroup.class}, message = \"岗位状态不能为空\")\n    private String status;\n\n    @ApiModelProperty(\"创建时间，开始时间\")\n    private Date beginTime;\n\n    @ApiModelProperty(\"创建时间，结束时间\")\n    private Date endTime;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/RegisterDto.java",
    "content": "package ginyi.system.domain.model.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport lombok.Data;\n\n@Data\n@ApiModel(\"用户注册参数\")\npublic class RegisterDto extends LoginDto {\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/RoleDto.java",
    "content": "package ginyi.system.domain.model.dto;\n\nimport ginyi.common.swagger.AddGroup;\nimport ginyi.common.swagger.UpdateGroup;\nimport ginyi.system.domain.BaseEntity;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.util.Date;\nimport java.util.List;\n\n@EqualsAndHashCode(callSuper = true)\n@Data\n@ApiModel(\"系统角色请求参数\")\npublic class RoleDto extends BaseEntity {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"角色ID\")\n    @NotNull(groups = UpdateGroup.class, message = \"角色id不能为空\")\n    private Long roleId;\n\n    /**\n     * 根据角色ids获取用户列表专用\n     */\n    @ApiModelProperty(\"岗位id（根据角色ids获取用户列表专用）\")\n    private List<Long> roleIds;\n\n    @ApiModelProperty(\"角色名称\")\n    @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = \"角色名称不能为空\")\n    @Size(max = 20, message = \"角色名称不能超过20个字符\")\n    private String roleName;\n\n    @ApiModelProperty(\"角色权限字符串\")\n    @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = \"角色权限字符串不能为空\")\n    @Size(max = 20, message = \"角色权限字符不能超过20个字符\")\n    private String roleKey;\n\n    @ApiModelProperty(\"角色排序\")\n    @NotNull(groups = {AddGroup.class, UpdateGroup.class}, message = \"角色排序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(\"角色状态（0正常 1停用）\")\n    @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = \"角色状态不能为空\")\n    @Size(max = 1, message = \"状态不合法\")\n    private String status;\n\n    @ApiModelProperty(\"角色菜单权限\")\n    private List<Long> permissions;\n\n    @ApiModelProperty(\"创建时间，开始时间\")\n    private Date beginTime;\n\n    @ApiModelProperty(\"创建时间，结束时间\")\n    private Date endTime;\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/UserDto.java",
    "content": "package ginyi.system.domain.model.dto;\n\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport ginyi.common.swagger.AddGroup;\nimport ginyi.common.swagger.UpdateGroup;\nimport ginyi.system.domain.BaseEntity;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.*;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Set;\n\n@EqualsAndHashCode(callSuper = true)\n@Data\n@ApiModel(\"系统用户请求参数\")\npublic class UserDto extends BaseEntity {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"用户id\")\n    @NotNull(groups = UpdateGroup.class, message = \"用户id不能为空\")\n    private Long userId;\n\n    @ApiModelProperty(\"用户账号\")\n    @NotBlank(groups = {UpdateGroup.class, AddGroup.class}, message = \"用户账号不能为空\")\n    private String userName;\n\n    @ApiModelProperty(\"部门id\")\n    @NotNull(groups = {UpdateGroup.class, AddGroup.class}, message = \"部门id不能为空\")\n    private Long deptId;\n\n    @ApiModelProperty(\"用户昵称\")\n    private String nickName;\n\n    @ApiModelProperty(\"邮箱\")\n    @Email(message = \"邮箱格式不正确\")\n    private String email;\n\n    @ApiModelProperty(\"手机号码\")\n    @Pattern(regexp = \"(?:0|86|\\\\+86)?1[3-9]\\\\d{9}\", message = \"手机号码格式不正确\")\n    private String phoneNumber;\n\n    @ApiModelProperty(\"性别（0男 1女 2未知）\")\n    @Size(min = 0, max = 2, message = \"性别不合法，0男 1女 2未知\")\n    private String sex;\n\n    @ApiModelProperty(\"头像\")\n    private String avatar;\n\n    @ApiModelProperty(\"密码\")\n    private String password;\n\n    @ApiModelProperty(\"状态（0正常 1停用）\")\n    @Size(min = 0, max = 1, message = \"状态不合法\")\n    private String status;\n\n    /**\n     * 最后登录IP\n     */\n    private String loginIp;\n\n    /**\n     * 最后登录时间\n     */\n    private Date loginDate;\n\n    @ApiModelProperty(\"创建时间，开始时间\")\n    private Date beginTime;\n\n    @ApiModelProperty(\"创建时间，结束时间\")\n    private Date endTime;\n\n    @TableLogic\n    private String deleted;\n\n    @ApiModelProperty(\"岗位id\")\n    private List<Long> postIds;\n\n    @ApiModelProperty(\"角色id\")\n    private List<Long> roleIds;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/BaseVo.java",
    "content": "package ginyi.system.domain.model.vo;\n\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * 基础 vo\n */\n@Data\npublic class BaseVo<T> {\n    @ApiModelProperty(\"列表数据\")\n    private List<T> list;\n\n    @ApiModelProperty(\"总条数\")\n    private Integer count;\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/CacheKeyVo.java",
    "content": "package ginyi.system.domain.model.vo;\n\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\n@Data\npublic class CacheKeyVo {\n\n    /**\n     * 键名\n     */\n    @ApiModelProperty(\"键名\")\n    private String key;\n\n    /**\n     * 数据类型\n     */\n    @ApiModelProperty(\"类型\")\n    private String type;\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/CacheVo.java",
    "content": "package ginyi.system.domain.model.vo;\n\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\n@Data\npublic class CacheVo {\n\n    /**\n     * 键名\n     */\n    @ApiModelProperty(\"键名\")\n    private String key;\n\n    /**\n     * 键值\n     */\n    @ApiModelProperty(\"键名\")\n    private Object value;\n\n    /**\n     * 时间\n     */\n    @ApiModelProperty(\"剩余时间\")\n    private long expire;\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/DeptVo.java",
    "content": "package ginyi.system.domain.model.vo;\n\nimport ginyi.system.domain.BaseEntity;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n@EqualsAndHashCode(callSuper = true)\n@Data\n@ApiModel(\"系统部门返回数据\")\npublic class DeptVo extends BaseEntity {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"部门id\")\n    private Long deptId;\n\n    @ApiModelProperty(\"父级id\")\n    private Long parentId;\n\n    private String ancestors;\n\n    @ApiModelProperty(\"部门名称\")\n    private String deptName;\n\n    @ApiModelProperty(\"显示顺序\")\n    private Integer sort;\n\n    @ApiModelProperty(\"部门负责人\")\n    private String leader;\n\n    @ApiModelProperty(\"部门负责人电话\")\n    private String phone;\n\n    @ApiModelProperty(\"部门负责人邮箱\")\n    private String email;\n\n    @ApiModelProperty(\"状态，0正常，1停用\")\n    private String status;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/LoginVo.java",
    "content": "package ginyi.system.domain.model.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport org.springframework.beans.factory.annotation.Value;\n\n@Data\n@ApiModel(\"登录返回数据\")\npublic class LoginVo {\n\n    @ApiModelProperty(value = \"Token令牌\", required = true)\n    private String token;\n\n    @ApiModelProperty(value = \"Token请求头Key\", required = true)\n    private String tokenHeader;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/NoticeVo.java",
    "content": "package ginyi.system.domain.model.vo;\n\nimport ginyi.system.domain.BaseEntity;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n@EqualsAndHashCode(callSuper = true)\n@Data\n@ApiModel(\"系统通知公告返回数据\")\npublic class NoticeVo extends BaseEntity {\n    /**\n     * 通知的id\n     */\n    @ApiModelProperty(\"通知的id\")\n    private Long noticeId;\n\n    /**\n     * 通知标题\n     */\n    @ApiModelProperty(\"通知标题\")\n    private String title;\n\n    /**\n     * 通知的类型（0通知，1公告）\n     */\n    @ApiModelProperty(\"通知的类型（0通知，1公告）\")\n    private String type;\n\n    /**\n     * 通知的内容\n     */\n    @ApiModelProperty(\"通知的内容\")\n    private String content;\n\n    /**\n     * 是否已读\n     */\n    @ApiModelProperty(\"是否已读\")\n    private boolean haveRead;\n\n    /**\n     * 通知的状态（0正常，1关闭）\n     */\n    @ApiModelProperty(\"通知的状态（0正常，1关闭）\")\n    private String status;\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/PostVo.java",
    "content": "package ginyi.system.domain.model.vo;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport ginyi.system.domain.BaseEntity;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n@EqualsAndHashCode(callSuper = true)\n@Data\n@ApiModel(\"系统岗位返回数据\")\npublic class PostVo extends BaseEntity {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"岗位序号\")\n    private Long postId;\n\n    @ApiModelProperty(\"岗位编码\")\n    private String postCode;\n\n    @ApiModelProperty(\"岗位名称\")\n    private String postName;\n\n    @ApiModelProperty(\"岗位排序\")\n    private Integer sort;\n\n    @ApiModelProperty(\"状态（0正常 1停用）\")\n    private String status;\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/RoleVo.java",
    "content": "package ginyi.system.domain.model.vo;\n\nimport ginyi.system.domain.BaseEntity;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport java.util.List;\n\n@EqualsAndHashCode(callSuper = true)\n@Data\n@ApiModel(\"系统角色返回数据\")\npublic class RoleVo extends BaseEntity {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"角色ID\")\n    private Long roleId;\n\n    @ApiModelProperty(\"角色名称\")\n    private String roleName;\n\n    @ApiModelProperty(\"角色权限字符串\")\n    private String roleKey;\n\n    @ApiModelProperty(\"角色排序\")\n    private Integer sort;\n\n    @ApiModelProperty(\"角色状态（0正常 1停用）\")\n    private String status;\n\n    @ApiModelProperty(\"角色菜单权限\")\n    private List<Long> permissions;\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/SessionUserVo.java",
    "content": "package ginyi.system.domain.model.vo;\n\nimport lombok.Data;\n\n@Data\npublic class SessionUserVo {\n\n    /**\n     * 用户唯一标识\n     */\n    private String token;\n\n    /**\n     * 登录时间\n     */\n    private Long loginTime;\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 String username;\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/UserVo.java",
    "content": "package ginyi.system.domain.model.vo;\n\nimport ginyi.system.domain.BaseEntity;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\n@ApiModel(\"系统用户返回数据\")\npublic class UserVo extends BaseEntity {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"用户id\")\n    private Long userId;\n\n    @ApiModelProperty(\"用户账号\")\n    private String userName;\n\n    @ApiModelProperty(\"部门id\")\n    private Long deptId;\n\n    @ApiModelProperty(\"用户昵称\")\n    private String nickName;\n\n    @ApiModelProperty(\"邮箱\")\n    private String email;\n\n    @ApiModelProperty(\"手机号码\")\n    private String phoneNumber;\n\n    @ApiModelProperty(\"性别（0男 1女 2未知）\")\n    private String sex;\n\n    @ApiModelProperty(\"头像\")\n    private String avatar;\n\n    @ApiModelProperty(\"密码\")\n    private String password;\n\n    @ApiModelProperty(\"状态（0正常 1停用）\")\n    private String status;\n\n    @ApiModelProperty(\"岗位id\")\n    private List<Long> postIds;\n\n    @ApiModelProperty(\"角色id\")\n    private List<Long> roleIds;\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysConfigMapper.java",
    "content": "package ginyi.system.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ginyi.system.domain.SysConfig;\n\n/**\n * 系统配置表\n */\npublic interface SysConfigMapper extends BaseMapper<SysConfig> {\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysDeptMapper.java",
    "content": "package ginyi.system.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ginyi.system.domain.SysDept;\nimport ginyi.system.domain.model.dto.DeptDto;\nimport org.apache.ibatis.annotations.Param;\n\npublic interface SysDeptMapper extends BaseMapper<SysDept> {\n\n    /**\n     * 查询部门列表\n     *\n     * @param deptDto\n     * @param page\n     */\n    public IPage<SysDept> list(@Param(\"deptDto\") DeptDto deptDto, Page page);\n\n    /**\n     * 新增部门\n     *\n     * @param deptDto\n     */\n    public void insertDept(@Param(\"deptDto\") DeptDto deptDto);\n\n    /**\n     * 更新部门\n     *\n     * @param deptDto\n     */\n    public void updateDept(@Param(\"deptDto\") DeptDto deptDto);\n\n    /**\n     * 更新状态\n     *\n     * @param deptDto\n     */\n    public void updateDeptStatus(@Param(\"deptDto\") DeptDto deptDto);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysLogininforMapper.java",
    "content": "package ginyi.system.mapper;\n\nimport ginyi.system.domain.SysLogLogin;\n\nimport java.util.List;\n\n/**\n * 系统访问日志情况信息 数据层\n *\n * @author ruoyi\n */\npublic interface SysLogininforMapper {\n    /**\n     * 新增系统登录日志\n     *\n     * @param logininfor 访问日志对象\n     */\n    public void insertLogininfor(SysLogLogin logininfor);\n\n    /**\n     * 查询系统登录日志集合\n     *\n     * @param logininfor 访问日志对象\n     * @return 登录记录集合\n     */\n    public List<SysLogLogin> selectLogininforList(SysLogLogin logininfor);\n\n    /**\n     * 批量删除系统登录日志\n     *\n     * @param infoIds 需要删除的登录日志ID\n     * @return 结果\n     */\n    public int deleteLogininforByIds(Long[] infoIds);\n\n    /**\n     * 清空系统登录日志\n     *\n     * @return 结果\n     */\n    public int cleanLogininfor();\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysMenuMapper.java",
    "content": "package ginyi.system.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ginyi.system.domain.SysMenu;\nimport ginyi.system.domain.model.dto.MenuDto;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * 菜单表 数据层\n *\n * @author ruoyi\n */\npublic interface SysMenuMapper extends BaseMapper<SysMenu> {\n    /**\n     * 根据用户ID查询权限\n     *\n     * @param userId 用户ID\n     * @return 权限列表\n     */\n    public List<String> selectMenuPermsByUserId(Long userId);\n\n    /**\n     * 根据角色ID查询权限\n     *\n     * @param roleId 角色ID\n     * @return 权限列表\n     */\n    public List<String> selectMenuPermsByRoleId(Long roleId);\n\n    /**\n     * 根据用户查询系统菜单列表\n     *\n     * @return 菜单列表\n     */\n    public List<SysMenu> selectMenuListByUserId(Long userId);\n\n    /**\n     * 管理员查询（管理）菜单列表\n     * @return\n     */\n    public List<SysMenu> selectMenuListByAdmin(@Param(\"menuDto\") MenuDto menuDto);\n\n    /**\n     * 添加菜单\n     * @param menuDto\n     * @return\n     */\n    public boolean insertMenu(@Param(\"menuDto\") MenuDto menuDto);\n\n    /**\n     * 更新菜单\n     * @param menuDto\n     */\n    public void updateMenu(@Param(\"menuDto\") MenuDto menuDto);\n\n    /**\n     * 更新状态\n     * @param menuDto\n     */\n    public void updateMenuStatus(@Param(\"menuDto\") MenuDto menuDto);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysNoticeMapper.java",
    "content": "package ginyi.system.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ginyi.system.domain.SysNotice;\nimport ginyi.system.domain.model.dto.NoticeDto;\nimport ginyi.system.domain.model.vo.NoticeVo;\nimport org.apache.ibatis.annotations.Param;\n\n\npublic interface SysNoticeMapper extends BaseMapper<SysNotice> {\n\n    /**\n     * 获取通知公告列表\n     */\n    public IPage<SysNotice> list(@Param(\"noticeDto\") NoticeDto noticeDto, Page page);\n\n    /**\n     * 发布通知公告\n     */\n    public void addNotice(@Param(\"noticeDto\") NoticeDto noticeDto);\n\n    /**\n     * 获取用户通知公告列表\n     * @param userId\n     * @param page\n     * @return\n     */\n    public IPage<SysNotice> getUserNoticeList(Long userId, Page page);\n\n    /**\n     * 确认收到通知公告\n     * @param notice\n     */\n    public void haveRead(@Param(\"notice\") SysNotice notice);\n\n    /**\n     * 根据 noticeId 查询通知公告\n     * @param noticeId\n     * @return\n     */\n    public SysNotice selectOne(Long noticeId);\n\n    /**\n     * 更新通知公告\n     * @param noticeDto\n     */\n    public void updateNotice(@Param(\"noticeDto\") NoticeDto noticeDto);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysOperLogMapper.java",
    "content": "package ginyi.system.mapper;\n\nimport ginyi.system.domain.SysLogOperation;\n\nimport java.util.List;\n\n/**\n * 操作日志 数据层\n *\n * @author ruoyi\n */\npublic interface SysOperLogMapper {\n    /**\n     * 新增操作日志\n     *\n     * @param operLog 操作日志对象\n     */\n    public void insertOperlog(SysLogOperation operLog);\n\n    /**\n     * 查询系统操作日志集合\n     *\n     * @param operLog 操作日志对象\n     * @return 操作日志集合\n     */\n    public List<SysLogOperation> selectOperLogList(SysLogOperation operLog);\n\n    /**\n     * 批量删除系统操作日志\n     *\n     * @param operIds 需要删除的操作日志ID\n     * @return 结果\n     */\n    public int deleteOperLogByIds(Long[] operIds);\n\n    /**\n     * 查询操作日志详细\n     *\n     * @param operId 操作ID\n     * @return 操作日志对象\n     */\n    public SysLogOperation selectOperLogById(Long operId);\n\n    /**\n     * 清空操作日志\n     */\n    public void cleanOperLog();\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysPostMapper.java",
    "content": "package ginyi.system.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ginyi.system.domain.SysPost;\nimport ginyi.system.domain.model.dto.PostDto;\nimport ginyi.system.domain.model.vo.PostVo;\nimport org.apache.ibatis.annotations.Param;\n\n/**\n * 岗位\n */\npublic interface SysPostMapper extends BaseMapper<SysPost> {\n\n    /**\n     * 查询岗位列表\n     *\n     * @param postDto\n     * @param page\n     * @return\n     */\n    public IPage<PostVo> list(@Param(\"postDto\") PostDto postDto, Page page);\n\n    /**\n     * 新增岗位\n     *\n     * @param postDto\n     */\n    public void insertPost(@Param(\"postDto\") PostDto postDto);\n\n    /**\n     * 更新岗位\n     *\n     * @param postDto\n     */\n    public void updatePost(@Param(\"postDto\") PostDto postDto);\n\n    /**\n     * 更新状态\n     * @param postDto\n     */\n    public void updatePostStatus(@Param(\"postDto\") PostDto postDto);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysRoleMapper.java",
    "content": "package ginyi.system.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ginyi.system.domain.SysRole;\nimport ginyi.system.domain.model.dto.RoleDto;\nimport ginyi.system.domain.model.vo.RoleVo;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * 角色表 数据层\n *\n * @author ruoyi\n */\npublic interface SysRoleMapper extends BaseMapper<SysRole> {\n\n\n    /**\n     * 根据用户ID查询角色\n     *\n     * @param userId 用户ID\n     * @return 角色列表\n     */\n    public List<SysRole> selectRolePermissionByUserId(Long userId);\n\n    /**\n     * 查询角色列表\n     *\n     * @param roleDto\n     * @param page\n     * @return\n     */\n    public IPage<RoleVo> list(@Param(\"roleDto\") RoleDto roleDto, Page page);\n\n    /**\n     * 新增角色\n     *\n     * @param roleDto\n     */\n    public void insertRole(@Param(\"roleDto\") RoleDto roleDto);\n\n    /**\n     * 新增角色对应的菜单权限\n     *\n     * @param roleDto\n     */\n    public void insertRoleMenu(@Param(\"roleDto\") RoleDto roleDto);\n\n    /**\n     * 更新角色\n     *\n     * @param roleDto\n     */\n    public void updateRole(@Param(\"roleDto\") RoleDto roleDto);\n\n    /**\n     * 更新角色对应的菜单权限\n     *\n     * @param roleDto\n     */\n    public void updateRoleMenu(@Param(\"roleDto\") RoleDto roleDto);\n\n    /**\n     * 根据角色id查询角色详情\n     * @param roleId\n     * @return\n     */\n    public RoleVo selectRoleByRoleId(Long roleId);\n\n    /**\n     * 更新状态\n     * @param roleDto\n     */\n    public void updateRoleStatus(@Param(\"roleDto\") RoleDto roleDto);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysUserMapper.java",
    "content": "package ginyi.system.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ginyi.system.domain.SysUser;\nimport ginyi.system.domain.model.dto.UserDto;\nimport ginyi.system.domain.model.vo.UserVo;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * 用户表\n */\npublic interface SysUserMapper extends BaseMapper<SysUser> {\n\n\n    /**\n     * 通过用户名查询用户\n     *\n     * @param userName 用户名\n     * @return 用户对象信息\n     */\n    public SysUser selectUserByUserName(String userName);\n\n    /**\n     * 校验用户名称是否唯一\n     *\n     * @param userName 用户名称\n     * @return 结果\n     */\n    public SysUser checkUserNameUnique(String userName);\n\n    /**\n     * 新增用户信息\n     *\n     * @param userDto 用户信息\n     * @return 结果\n     */\n    public int insertUser(@Param(\"userDto\") UserDto userDto);\n\n    /**\n     * 插入用户岗位中间表, 插入用户对应的岗位信息\n     * @param userDto\n     * @return\n     */\n    public int insertUserPostIds(@Param(\"userDto\") UserDto userDto);\n\n    /**\n     * 插入用户角色中间表, 插入用户对应的角色信息\n     * @param userDto\n     * @return\n     */\n    public int insertUserRoleIds(@Param(\"userDto\") UserDto userDto);\n\n    /**\n     * 修改用户信息\n     *\n     * @param userDto 用户信息\n     * @return 结果\n     */\n    public int updateUser(@Param(\"userDto\") UserDto userDto);\n\n    /**\n     * 更新用户岗位中间表, 更新用户对应的岗位信息\n     * @param userDto\n     * @return\n     */\n    public int updateUserPostIds(@Param(\"userDto\") UserDto userDto);\n\n    /**\n     * 更新用户角色中间表, 更新用户对应的角色信息\n     * @param userDto\n     * @return\n     */\n    public int updateUserRoleIds(@Param(\"userDto\") UserDto userDto);\n\n    /**\n     * 根据用户id查询用户\n     * @param userId\n     * @return\n     */\n    public UserVo selectUserByUserId(String userId);\n\n    /**\n     * 查询用户列表(不含admin)\n     * @param userDto\n     * @param page\n     * @return\n     */\n    public IPage<UserVo> list(@Param(\"userDto\") UserDto userDto, Page page);\n\n    /**\n     * 更新用户状态\n     * @param userDto\n     */\n    public void updateUserStatus(@Param(\"userDto\") UserDto userDto);\n\n    /**\n     * 根据部门ids获取用户列表\n     * @param deptIds\n     * @return\n     */\n    public List<SysUser> selectUserByDeptIds(List<Long> deptIds);\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysConfigService.java",
    "content": "package ginyi.system.service;\n\npublic interface ISysConfigService {\n\n    /**\n     * 获取验证码开关\n     *\n     * @return true开启，false关闭\n     */\n    public boolean selectCaptchaEnabled();\n\n    /**\n     * 根据键名查询参数配置信息\n     *\n     * @param configKey 参数key\n     * @return 参数键值\n     */\n    public String selectConfigByKey(String configKey);\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysDeptService.java",
    "content": "package ginyi.system.service;\n\nimport ginyi.system.domain.SysDept;\nimport ginyi.system.domain.model.dto.DeptDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.DeptVo;\n\nimport java.util.Set;\n\n/**\n * 系统部门\n */\npublic interface ISysDeptService {\n\n    /**\n     * 获取部门列表\n     * @param deptDto\n     * @param page\n     * @param pageSize\n     */\n    public BaseVo<SysDept> list(DeptDto deptDto, Long page, Long pageSize);\n\n    /**\n     * 根据部门id获取部门详情\n     * @param deptId\n     * @return\n     */\n    public DeptVo getDeptByDeptId(Long deptId);\n\n    /**\n     * 新增部门\n     * @param deptDto\n     */\n    public void addDept(DeptDto deptDto);\n\n    /**\n     * 更新部门\n     * @param deptDto\n     */\n    public void updateDept(DeptDto deptDto);\n\n    /**\n     * 删除部门\n     * @param deptId\n     */\n    public void removeDeptById(Long deptId);\n\n    /**\n     * 批量删除部门\n     * @param ids\n     */\n    public void removeDeptByIds(Set<Long> ids);\n\n    /**\n     * 更改状态（0正常 1停用）\n     * @param deptDto\n     */\n    public void updateStatus(DeptDto deptDto);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysLogService.java",
    "content": "package ginyi.system.service;\n\nimport ginyi.system.domain.SysLogLogin;\nimport ginyi.system.domain.SysLogOperation;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport java.util.List;\n\npublic interface ISysLogService {\n\n    /**\n     * 获取登录日志\n     * @param page\n     * @param pageSize\n     * @return\n     */\n    public BaseVo<SysLogLogin> getLoginLogList(Long page, Long pageSize);\n\n    /**\n     * 获取操作日志\n     * @param page\n     * @param pageSize\n     * @return\n     */\n    public BaseVo<SysLogOperation> getOperationLogList(Long page, Long pageSize);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysLoginService.java",
    "content": "package ginyi.system.service;\n\nimport ginyi.system.domain.model.dto.LoginDto;\nimport ginyi.system.domain.model.dto.RegisterDto;\nimport ginyi.system.domain.model.vo.LoginVo;\n\npublic interface ISysLoginService {\n\n    /**\n     * 登录验证\n     * @param loginDto\n     * @return\n     */\n    public LoginVo login(LoginDto loginDto);\n\n    /**\n     * 用户注册\n     * @param registerDto\n     */\n    public void register(RegisterDto registerDto);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysLogininforService.java",
    "content": "package ginyi.system.service;\n\nimport ginyi.system.domain.SysLogLogin;\n\nimport java.util.List;\n\n/**\n * 系统访问日志情况信息 服务层\n *\n * @author ruoyi\n */\npublic interface ISysLogininforService {\n    /**\n     * 新增系统登录日志\n     *\n     * @param logininfor 访问日志对象\n     */\n    public void insertLogininfor(SysLogLogin logininfor);\n\n    /**\n     * 查询系统登录日志集合\n     *\n     * @param logininfor 访问日志对象\n     * @return 登录记录集合\n     */\n    public List<SysLogLogin> selectLogininforList(SysLogLogin logininfor);\n\n    /**\n     * 批量删除系统登录日志\n     *\n     * @param infoIds 需要删除的登录日志ID\n     * @return 结果\n     */\n    public int deleteLogininforByIds(Long[] infoIds);\n\n    /**\n     * 清空系统登录日志\n     */\n    public void cleanLogininfor();\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysMenuService.java",
    "content": "package ginyi.system.service;\n\nimport ginyi.system.domain.SysMenu;\nimport ginyi.system.domain.model.dto.MenuDto;\nimport ginyi.system.domain.model.vo.BaseVo;\n\nimport java.util.Set;\n\n/**\n * 菜单 业务层\n *\n * @author ruoyi\n */\npublic interface ISysMenuService {\n    /**\n     * 根据角色ID查询权限\n     *\n     * @param roleId 角色ID\n     * @return 权限列表\n     */\n    public Set<String> selectMenuPermsByRoleId(Long roleId);\n\n    /**\n     * 根据用户ID查询权限\n     *\n     * @param userId 用户ID\n     * @return 权限列表\n     */\n    public Set<String> selectMenuPermsByUserId(Long userId);\n\n    /**\n     * 根据用户查询系统菜单列表\n     *\n     * @return 菜单列表\n     */\n    public BaseVo<SysMenu> selectMenuList();\n\n    /**\n     * 管理员查询系统菜单列表\n     *\n     * @return 菜单列表\n     */\n    public BaseVo<SysMenu> list(MenuDto menuDto);\n\n    /**\n     * 添加菜单\n     * @param menuDto\n     */\n    public void addMenu(MenuDto menuDto);\n\n    /**\n     * 根据id获取菜单详情\n     * @param menuId\n     * @return\n     */\n    public SysMenu getMenuById(Long menuId);\n\n    /**\n     * 更新菜单\n     * @param menuDto\n     */\n    public void updateMenu(MenuDto menuDto);\n\n    /**\n     * 根据菜单id删除\n     * @param menuId\n     */\n    public void removeMenuById(Long menuId);\n\n    /**\n     * 根据ids批量删除菜单\n     * @param ids\n     */\n    public void removeMenuByIds(Set<Long> ids);\n\n    /**\n     * 更改状态（0正常 1停用）\n     * @param menuDto\n     */\n    public void updateStatus(MenuDto menuDto);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysMonitorService.java",
    "content": "package ginyi.system.service;\n\nimport ginyi.system.domain.SysServer;\nimport ginyi.system.domain.model.dto.CacheDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.CacheKeyVo;\nimport ginyi.system.domain.model.vo.CacheVo;\n\n/**\n * 系统服务\n */\npublic interface ISysMonitorService {\n\n    /**\n     * 获取系统服务信息，如 cpu、内存 等\n     */\n    public SysServer getServerInfo() throws InterruptedException;\n\n    /**\n     * 获取缓存列表\n     */\n    public BaseVo<CacheKeyVo> getCacheList();\n\n    /**\n     * 获取缓存详情\n     */\n    public CacheVo getCacheDetails(CacheDto cacheDto);\n\n    /**\n     * 删除缓存\n     */\n    public void removeCache(String key);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysNoticeService.java",
    "content": "package ginyi.system.service;\n\nimport ginyi.system.domain.SysNotice;\nimport ginyi.system.domain.model.dto.NoticeDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.NoticeVo;\n\n/**\n * 通知公告\n */\npublic interface ISysNoticeService {\n\n    /**\n     * 获取通知公告列表\n     * @param noticeDto\n     * @param page\n     * @param pageSize\n     * @return\n     */\n    public BaseVo<SysNotice> list(NoticeDto noticeDto, Long page, Long pageSize);\n\n    /**\n     * 发布通知公告\n     * @param noticeDto\n     */\n    public void add(NoticeDto noticeDto);\n\n    /**\n     * 获取用户通知公告列表\n     * @param page\n     * @param pageSize\n     * @return\n     */\n    public BaseVo<NoticeVo> getUserNoticeList(Long page, Long pageSize);\n\n    /**\n     * 确认收到通知公告\n     * @param noticeId\n     */\n    public void haveRead(Long noticeId);\n\n    /**\n     * 删除通知公告\n     * @param noticeId\n     */\n    public void remove(Long noticeId);\n\n    /**\n     * 更新通知公告\n     * @param noticeDto\n     */\n    public void updateNotice(NoticeDto noticeDto);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysOnlineService.java",
    "content": "package ginyi.system.service;\n\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.SessionUserVo;\n\nimport java.util.Set;\n\npublic interface ISysOnlineService {\n\n    /**\n     * 当前在线用户列表\n     */\n    public BaseVo<SessionUserVo> getOnlineUserList(Long page, Long pageSize);\n\n    /**\n     * 强制用户下线\n     * @param sessionId\n     */\n    public void removeUser(String sessionId);\n\n    /**\n     * 批量强制用户下线\n     * @param ids\n     */\n    public void removeUser(Set<String> ids);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysOperLogService.java",
    "content": "package ginyi.system.service;\n\nimport ginyi.system.domain.SysLogOperation;\n\nimport java.util.List;\n\n/**\n * 操作日志 服务层\n *\n * @author ruoyi\n */\npublic interface ISysOperLogService {\n    /**\n     * 新增操作日志\n     *\n     * @param operLog 操作日志对象\n     */\n    public void insertOperlog(SysLogOperation operLog);\n\n    /**\n     * 查询系统操作日志集合\n     *\n     * @param operLog 操作日志对象\n     * @return 操作日志集合\n     */\n    public List<SysLogOperation> selectOperLogList(SysLogOperation operLog);\n\n    /**\n     * 批量删除系统操作日志\n     *\n     * @param operIds 需要删除的操作日志ID\n     * @return 结果\n     */\n    public int deleteOperLogByIds(Long[] operIds);\n\n    /**\n     * 查询操作日志详细\n     *\n     * @param operId 操作ID\n     * @return 操作日志对象\n     */\n    public SysLogOperation selectOperLogById(Long operId);\n\n    /**\n     * 清空操作日志\n     */\n    public void cleanOperLog();\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysPasswordService.java",
    "content": "package ginyi.system.service;\n\nimport ginyi.system.domain.SysUser;\n\npublic interface ISysPasswordService {\n\n    public void validate(SysUser user);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysPermissionService.java",
    "content": "package ginyi.system.service;\n\nimport ginyi.system.domain.SysUser;\n\nimport java.util.Set;\n\npublic interface ISysPermissionService {\n\n    /**\n     * 获取角色数据权限\n     *\n     * @param user 用户信息\n     * @return 角色权限信息\n     */\n    public Set<String> getRolePermission(SysUser user);\n\n    /**\n     * 获取菜单数据权限\n     *\n     * @param user 用户信息\n     * @return 菜单权限信息\n     */\n    public Set<String> getMenuPermission(SysUser user);\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysPostService.java",
    "content": "package ginyi.system.service;\n\nimport ginyi.system.domain.model.dto.PostDto;\nimport ginyi.system.domain.model.dto.RoleDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.PostVo;\n\nimport java.util.Set;\n\npublic interface ISysPostService {\n\n    /**\n     * 查询岗位列表\n     *\n     * @param postDto\n     * @param page\n     * @param pageSize\n     * @return\n     */\n    public BaseVo<PostVo> list(PostDto postDto, Long page, Long pageSize);\n\n    /**\n     * 根据岗位id获取岗位详情\n     *\n     * @param postId\n     * @return\n     */\n    public PostVo getPostByPostId(Long postId);\n\n    /**\n     * 添加岗位\n     *\n     * @param postDto\n     */\n    public void addPost(PostDto postDto);\n\n    /**\n     * 更新岗位\n     *\n     * @param postDto\n     */\n    public void updatePost(PostDto postDto);\n\n    /**\n     * 根据id删除岗位\n     *\n     * @param postId\n     */\n    public void removePostById(Long postId);\n\n    /**\n     * 批量删除岗位\n     *\n     * @param ids\n     */\n    void removeDeptByIds(Set<Long> ids);\n\n    /**\n     * 更改状态（0正常 1停用）\n     * @param postDto\n     */\n    public void updateStatus(PostDto postDto);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysRoleService.java",
    "content": "package ginyi.system.service;\n\nimport ginyi.system.domain.model.dto.RoleDto;\nimport ginyi.system.domain.model.dto.UserDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.RoleVo;\n\nimport java.util.Set;\n\npublic interface ISysRoleService {\n    /**\n     * 根据用户ID查询角色权限\n     *\n     * @param userId 用户ID\n     * @return 权限列表\n     */\n    public Set<String> selectRolePermissionByUserId(Long userId);\n\n    /**\n     * 角色列表\n     *\n     * @param roleDto\n     * @param page\n     * @param pageSize\n     * @return\n     */\n    BaseVo<RoleVo> list(RoleDto roleDto, Long page, Long pageSize);\n\n    /**\n     * 根据角色id获取角色\n     *\n     * @param roleId\n     * @return\n     */\n    public RoleVo getRoleByRoleId(Long roleId);\n\n    /**\n     * 新增角色\n     *\n     * @param roleDto\n     */\n    public void addRole(RoleDto roleDto);\n\n    /**\n     * 跟新角色\n     *\n     * @param roleDto\n     */\n    public void updateRole(RoleDto roleDto);\n\n    /**\n     * 删除角色\n     *\n     * @param roleId\n     */\n    public void removeByRoleId(Long roleId);\n\n    /**\n     * 批量删除角色\n     *\n     * @param ids\n     */\n    public void removeByRoleIds(Set<Long> ids);\n\n    /**\n     * 更改状态（0正常 1停用）\n     * @param roleDto\n     */\n    public void updateStatus(RoleDto roleDto);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysUserService.java",
    "content": "package ginyi.system.service;\n\nimport ginyi.system.domain.SysUser;\nimport ginyi.system.domain.model.dto.DeptDto;\nimport ginyi.system.domain.model.dto.PostDto;\nimport ginyi.system.domain.model.dto.RoleDto;\nimport ginyi.system.domain.model.dto.UserDto;\nimport ginyi.system.domain.model.vo.BaseVo;\nimport ginyi.system.domain.model.vo.UserVo;\n\nimport java.util.HashMap;\nimport java.util.Set;\n\npublic interface ISysUserService {\n\n    /**\n     * 修改用户基本信息\n     *\n     * @param userDto 用户信息\n     * @return 结果\n     */\n    public void updateUser(UserDto userDto);\n\n    /**\n     * 通过用户名查询用户\n     *\n     * @param userName 用户名\n     * @return 用户对象信息\n     */\n    public SysUser selectUserByUserName(String userName);\n\n    /**\n     * 校验用户名称是否唯一\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    public String checkUserNameUnique(SysUser user);\n\n    /**\n     * 注册用户信息\n     *\n     * @param user 用户信息\n     * @return 结果\n     */\n    public boolean registerUser(SysUser user);\n\n    /**\n     * 新增用户\n     *\n     * @param userDto\n     */\n    public void addUser(UserDto userDto);\n\n    /**\n     * 根据用户id查询用户\n     * @param userId\n     */\n    public UserVo getUserByUserId(String userId);\n\n    /**\n     * 查询用户列表（不含admin）\n     * @param userDto\n     * @param page\n     * @param pageSize\n     * @return\n     */\n    public BaseVo<UserVo> list(UserDto userDto, Long page, Long pageSize);\n\n    /**\n     * 根据用户id删除用户\n     * @param userId\n     */\n    public void removeById(Long userId);\n\n    /**\n     * 根据userId批量删除用户\n     * @param ids\n     */\n    public void removeUserByIds(Set<Long> ids);\n\n    /**\n     * 更改状态（0正常 1停用）\n     * @param userDto\n     */\n    public void updateStatus(UserDto userDto);\n\n    /**\n     * 根据部门 id 获取用户列表\n     * @param deptDto\n     * @return\n     */\n    public BaseVo<HashMap<String, Object>> getUserListByDeptIds(DeptDto deptDto);\n\n    /**\n     * 根据岗位 id 获取用户列表\n     * @param postDto\n     * @return\n     */\n    public BaseVo<HashMap<String, Object>> getUserListByPostIds(PostDto postDto);\n\n    /**\n     * 根据角色 id 获取用户列表\n     * @param roleDto\n     * @return\n     */\n    public BaseVo<HashMap<String, Object>> getUserListByRoleIds(RoleDto roleDto);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ITokenService.java",
    "content": "package ginyi.system.service;\n\nimport ginyi.system.domain.LoginUser;\n\nimport javax.servlet.http.HttpServletRequest;\n\npublic interface ITokenService {\n\n    /**\n     * 创建令牌\n     *\n     * @param loginUser 用户信息\n     * @return 令牌\n     */\n    public String createToken(LoginUser loginUser);\n\n    /**\n     * 获取用户身份信息\n     *\n     * @return 用户信息\n     */\n    public LoginUser getLoginUser(HttpServletRequest request);\n\n    /**\n     * 删除用户身份信息\n     */\n    public void delLoginUser(String token);\n\n    /**\n     * 验证令牌有效期，相差不足20分钟，自动刷新缓存\n     *\n     * @param loginUser\n     * @return 令牌\n     */\n    public void verifyToken(LoginUser loginUser);\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/IVerifyService.java",
    "content": "package ginyi.system.service;\n\npublic interface IVerifyService {\n\n    /**\n     * 图片验证码\n     */\n    public String captcha();\n\n    /**\n     * 校验验证码\n     */\n    public void checkImgCode(String code);\n\n}\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/resources/application.yml",
    "content": "# 项目应用配置\nginyi:\n  # 项目应用作者\n  project-author: ginyi@aliyun.com\n  # 项目应用名称\n  project-name: ginyi-springboot-demo\n  # 项目应用版本\n  project-version: 1.0.0\n  # 项目应用描述\n  project-description: 重构ruoyi，学习优质项目，加油~\n  # 文件路径 示例（ Windows配置D:/ginyi/uploadPath，Linux配置 /home/ginyi/uploadPath）\n  profile: D:/ginyi/uploadPath\n\n\n\n# 应用服务\nserver:\n  servlet:\n    # 应用访问的路径\n    context-path: /\n  tomcat:\n    # tomcat的URI编码\n    uri-encoding: UTF-8\n    # 连接数满后的排队数，默认为100\n    accept-count: 1000\n    threads:\n      # tomcat最大线程数，默认为200\n      max: 800\n      # Tomcat启动初始化的线程数，默认值10\n      min-spare: 100\n\n\nspring:\n  # 文件上传\n  servlet:\n    multipart:\n      # 单个文件大小\n      max-file-size: 10MB\n      # 设置总上传的文件大小\n      max-request-size: 20MB\n  mvc:\n    pathmatch:\n      matching-strategy: ant_path_matcher\n  # 数据源\n  datasource:\n    type: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceWrapper\n    druid:\n      driver-class-name: com.mysql.cj.jdbc.Driver\n      url: jdbc:mysql://127.0.0.1:3306/ginyi-spring-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8\n      username: root\n      password: 123456\n      # 初始连接数\n      initialSize: 5\n      # 最小连接池数量\n      minIdle: 10\n      # 最大连接池数量\n      maxActive: 20\n      testWhileIdle: true\n  # redis 配置\n  redis:\n    # 地址\n    host: 127.0.0.1\n    # 端口，默认为 6379\n    port: 6379\n    # 数据库索引\n    database: 0\n    # 密码\n    password:\n    # 连接超时时间\n    timeout: 10s\n    lettuce:\n      pool:\n        # 连接池中的最小空闲连接\n        min-idle: 0\n        # 连接池中的最大空闲连接\n        max-idle: 8\n        # 连接池的最大数据库连接数\n        max-active: 8\n        # #连接池最大阻塞等待时间（使用负值表示没有限制）\n        max-wait: -1ms\n  data:\n    mongodb:\n      uri: mongodb://127.0.0.1:27017/ginyi-spring-vue\n  # 资源信息\n  messages:\n    # 国际化资源文件路径\n    basename: static/i18n/messages\n  profiles:\n    active: druid\n  # 全局配置 @JsonFormat 时间格式化\n  jackson:\n    date-format: yyyy-MM-dd HH:mm:ss\n    time-zone: GMT+8\n\n\n# MyBatis-plus配置\nmybatis-plus:\n  # mapper.xml文件的位置\n  mapper-locations: classpath*:mapper/**/*Mapper.xml\n  # 类型别名的位置\n  type-aliases-package: ginyi.**.domain\n  configuration:\n    # 开启下划线转驼峰\n    map-underscore-to-camel-case: true\n    # 控制台输出sql语句\n    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl\n  global-config:\n    db-config:\n      # 更改默认的 id 生成策略\n      id-type: assign_id\n      # 逻辑删除\n      logic-delete-field: deleted\n      logic-not-delete-value: 0  # 未删除\n      logic-delete-value: 1      # 已删除\n\n\n# token配置\ntoken:\n  # 令牌自定义标识\n  header: Authorization\n  # 令牌前缀\n  prefix: Bearer\n  # 令牌密钥\n  secret: da5519948e0ac1db0ac8da59bb0770a1\n  # 令牌有效期（默认30分钟）\n  expireTime: 30\n\n\n# 用户配置\nuser:\n  password:\n    # 密码最大错误次数\n    maxRetryCount: 5\n    # 密码锁定时间（默认10分钟）\n    lockTime: 10\n\n\n# 接口文档的配置\nswagger:\n  # 联系方式\n  swagger-contact:\n    name: Author Email is Ginyi@aliyun.com\n    email: Ginyi@aliyun.com\n\n\n# 日志配置文件\nlogging:\n  config: classpath:logging-config.xml\n\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/resources/logging-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n    <property name=\"LOG_PATH\" value=\"logs\" />\n    <property name=\"LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss} %-5level [%21thread]  [%-50.50class] >>> %msg %n\" />\n\n    <!-- 控制台输出 -->\n    <appender name=\"consoleLog\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- 彩色日志 -->\n        <layout class=\"ch.qos.logback.classic.PatternLayout\">\n            <pattern>\n                %d{yyyy-MM-dd HH:mm:ss} %highlight(%-5level) %magenta([%21thread])  %green([%-50.50class]) >>> %cyan(%msg) %n\n            </pattern>\n        </layout>\n    </appender>\n\n    <!-- 按照每天生成日志文件 -->\n    <appender name=\"fileLog\"  class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_PATH}/ginyi-springboot-%d{yyyy-MM-dd}.%i.log</FileNamePattern>\n            <!--日志文件最大的大小-->\n            <timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\n                <maxFileSize>100MB</maxFileSize>\n            </timeBasedFileNamingAndTriggeringPolicy>\n            <!--日志文件保留天数-->\n            <MaxHistory>15</MaxHistory>\n        </rollingPolicy>\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${LOG_PATTERN}</pattern>\n        </encoder>\n    </appender>\n\n    <!-- 日志输出级别 -->\n    <root level=\"info\">\n        <appender-ref ref=\"consoleLog\" />\n        <appender-ref ref=\"fileLog\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/resources/mapper/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=\"ginyi.system.mapper.SysDeptMapper\">\n\n    <resultMap id=\"SysDeptResult\" 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=\"sort\" column=\"sort\"/>\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=\"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    <insert id=\"insertDept\">\n        insert into sys_dept\n        <trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n            parent_id,\n            <if test=\"deptDto.deptName != null and deptDto.deptName != ''\">dept_name,</if>\n            <if test=\"deptDto.sort != null and deptDto.sort != ''\">sort,</if>\n            <if test=\"deptDto.leader != null and deptDto.leader != ''\">leader,</if>\n            <if test=\"deptDto.phone != null and deptDto.phone != ''\">phone,</if>\n            <if test=\"deptDto.email != null and deptDto.email != ''\">email,</if>\n            <if test=\"deptDto.status != null and deptDto.status != ''\">`status`,</if>\n            <if test=\"deptDto.createBy != null and deptDto.createBy != ''\">create_by,</if>\n            <if test=\"deptDto.createTime != null\">create_time,</if>\n            <if test=\"deptDto.updateBy != null and deptDto.updateBy\">update_by,</if>\n            <if test=\"deptDto.updateTime != null\">update_time,</if>\n        </trim>\n        <trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">\n            <choose>\n                <when test=\"deptDto.parentId == null or deptDto.parentId == ''\">0,</when>\n                <otherwise>#{deptDto.parentId}</otherwise>\n            </choose>\n            <if test=\"deptDto.deptName != null and deptDto.deptName != ''\">#{deptDto.deptName},</if>\n            <if test=\"deptDto.sort != null and deptDto.sort != ''\">#{deptDto.sort},</if>\n            <if test=\"deptDto.leader != null and deptDto.leader != ''\">#{deptDto.leader},</if>\n            <if test=\"deptDto.phone != null and deptDto.phone != ''\">#{deptDto.phone},</if>\n            <if test=\"deptDto.email != null and deptDto.email != ''\">#{deptDto.email},</if>\n            <if test=\"deptDto.status != null and deptDto.status != ''\">#{deptDto.status},</if>\n            <if test=\"deptDto.createBy != null and deptDto.createBy != ''\">#{deptDto.createBy},</if>\n            <if test=\"deptDto.createTime != null\">#{deptDto.createTime},</if>\n            <if test=\"deptDto.updateBy != null and deptDto.updateBy\">#{deptDto.updateBy},</if>\n            <if test=\"deptDto.updateTime != null\">#{deptDto.updateTime},</if>\n        </trim>\n    </insert>\n\n    <!-- 更新部门 -->\n    <update id=\"updateDept\">\n        update sys_dept\n        <set>\n            <if test=\"deptDto.parentId != null\">parent_id = #{deptDto.parentId},</if>\n            <if test=\"deptDto.deptName != null and deptDto.deptName != ''\">dept_name = #{deptDto.deptName},</if>\n            <if test=\"deptDto.sort != null and deptDto.sort != ''\">sort = #{deptDto.sort},</if>\n            <if test=\"deptDto.leader != null and deptDto.leader != ''\">leader = #{deptDto.leader},</if>\n            <if test=\"deptDto.phone != null and deptDto.phone != ''\">phone = #{deptDto.phone},</if>\n            <if test=\"deptDto.email != null and deptDto.email != ''\">email = #{deptDto.email},</if>\n            <if test=\"deptDto.status != null and deptDto.status != ''\">`status` = #{deptDto.status},</if>\n            <if test=\"deptDto.createBy != null and deptDto.createBy != ''\">create_by = #{deptDto.createBy},</if>\n            <if test=\"deptDto.createTime != null\">create_time = #{deptDto.createTime},</if>\n            <if test=\"deptDto.updateBy != null and deptDto.updateBy != ''\">update_by = #{deptDto.updateBy},</if>\n            <if test=\"deptDto.updateTime != null\">update_time = #{deptDto.updateTime},</if>\n            <if test=\"deptDto.remark != null and deptDto.remark != ''\">remark = #{deptDto.remark},</if>\n        </set>\n        where dept_id = #{deptDto.deptId}\n    </update>\n\n    <!-- 更新状态 -->\n    <update id=\"updateDeptStatus\">\n        update sys_dept\n        <set>\n            `status` = #{deptDto.status},\n            <if test=\"deptDto.updateBy != null and deptDto.updateBy != ''\">update_by = #{deptDto.updateBy},</if>\n            <if test=\"deptDto.updateTime != null\">update_time = #{deptDto.updateTime},</if>\n        </set>\n        where dept_id = #{deptDto.deptId} and deleted = '0'\n    </update>\n\n    <!-- 查询部门列表 -->\n    <select id=\"list\" resultMap=\"SysDeptResult\">\n        select dept_id,\n        parent_id,\n        dept_name,\n        sort,\n        leader,\n        phone,\n        email,\n        status,\n        create_by,\n        create_time,\n        update_by,\n        update_time,\n        remark\n        from sys_dept\n        <where>\n            deleted = '0'\n            <if test=\"deptDto.deptName != null and deptDto.deptName != ''\">\n                and dept_name like concat('%',#{deptDto.deptName} , '%')\n            </if>\n            <if test=\"deptDto.leader != null and deptDto.leader != ''\">\n                and leader like concat('%',#{deptDto.leader} , '%')\n            </if>\n            <if test=\"deptDto.phone != null and deptDto.phone != ''\">\n                and phone like concat('%',#{deptDto.phone} , '%')\n            </if>\n            <if test=\"deptDto.email != null and deptDto.email != ''\">\n                and email like concat('%',#{deptDto.email} , '%')\n            </if>\n            <if test=\"deptDto.remark != null and deptDto.remark != ''\">\n                and remark like concat('%',#{deptDto.remark} , '%')\n            </if>\n            <if test=\"deptDto.status != null and deptDto.status != ''\">\n                and `status` = #{deptDto.status}\n            </if>\n            <choose>\n                <when test=\"deptDto.beginTime != null and deptDto.endTime == null\">\n                    and create_time > #{deptDto.beginTime}\n                </when>\n                <when test=\"deptDto.beginTime != null and deptDto.endTime != null\">\n                    and create_time between #{deptDto.beginTime} and #{deptDto.endTime}\n                </when>\n            </choose>\n        </where>\n    </select>\n</mapper>"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/resources/mapper/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=\"ginyi.system.mapper.SysMenuMapper\">\n\n    <resultMap id=\"sysMenuResult\" type=\"sysMenu\">\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=\"sort\" column=\"sort\"/>\n        <result property=\"path\" column=\"path\"/>\n        <result property=\"component\" column=\"component\"/>\n        <result property=\"query\" column=\"query\"/>\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=\"updateBy\" column=\"update_by\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"remark\" column=\"remark\"/>\n    </resultMap>\n\n    <!-- 公用sql片段 -->\n    <sql id=\"selectMenuRef\">\n        select sm.menu_id,\n               sm.menu_name,\n               sm.parent_id,\n               sm.sort,\n               sm.name,\n               sm.path,\n               sm.component,\n               sm.query,\n               sm.is_frame,\n               sm.is_cache,\n               sm.menu_type,\n               sm.visible,\n               sm.status,\n               sm.perms,\n               sm.icon,\n               sm.create_by,\n               sm.create_time,\n               sm.update_by,\n               sm.update_time,\n               sm.remark\n        from sys_menu as sm\n    </sql>\n\n    <!-- 根据userId查询用户菜单权限 -->\n    <select id=\"selectMenuPermsByUserId\" parameterType=\"Long\" resultType=\"String\">\n        select distinct sm.perms\n        from sys_menu as sm\n                 left join sys_role_menu as srm on json_contains(srm.menu_id, json_array(sm.menu_id))\n                 left join sys_user_role as sur on json_contains(sur.role_id, json_array(srm.role_id))\n                 left join sys_role as sr on json_contains(sur.role_id, json_array(sr.role_id))\n        where sm.status = '0'\n          and sr.status = '0'\n          and sur.user_id = #{userId}\n    </select>\n\n    <!-- 根据roleId查询菜单 -->\n    <select id=\"selectMenuPermsByRoleId\" parameterType=\"Long\" resultType=\"String\">\n        select distinct sm.perms\n        from sys_menu as sm\n                 left join sys_role_menu as srm on json_contains(srm.menu_id, json_array(sm.menu_id))\n                 left join sys_role as sr on json_contains(srm.role_id, json_array(sr.role_id))\n        where sm.status = '0'\n          and sr.status = '0'\n          and srm.role_id = #{roleId}\n    </select>\n\n    <!-- 根据userId查询菜单 -->\n    <select id=\"selectMenuListByUserId\" resultMap=\"sysMenuResult\">\n        <include refid=\"selectMenuRef\"/>\n        left join sys_role_menu as srm on json_contains(srm.menu_id, json_array(sm.menu_id))\n        left join sys_user_role as sur on json_contains(sur.role_id, json_array(srm.role_id))\n        where sur.user_id = #{userId} and sm.menu_type != 'F' and sm.visible != 1\n    </select>\n\n    <!-- 查询菜单 - admin -->\n    <select id=\"selectMenuListByAdmin\" resultMap=\"sysMenuResult\">\n        <include refid=\"selectMenuRef\"/>\n        <if test=\"menuDto.filterButton != null and menuDto.filterButton != '' and menuDto.filterButton == 0\">\n            where sm.menu_type != 'F'\n        </if>\n    </select>\n\n    <!-- 新增菜单 -->\n    <insert id=\"insertMenu\">\n        insert into sys_menu (\n        menu_name,\n        sort,\n        menu_type,\n        visible,\n        status,\n        <if test=\"menuDto.component != null and menuDto.component != ''\">component,</if>\n        <if test=\"menuDto.path != null and menuDto.path != ''\">path,</if>\n        <if test=\"menuDto.query != null and menuDto.query != ''\">query,</if>\n        <if test=\"menuDto.isFrame != null and menuDto.isFrame != ''\">is_frame,</if>\n        <if test=\"menuDto.isCache != null and menuDto.isCache != ''\">is_cache,</if>\n        <if test=\"menuDto.perms != null and menuDto.perms != ''\">perms,</if>\n        <if test=\"menuDto.icon != null and menuDto.icon != ''\">icon,</if>\n        <if test=\"menuDto.createBy != null and menuDto.createBy != ''\">create_by,</if>\n        <if test=\"menuDto.createTime != null\">create_time,</if>\n        <if test=\"menuDto.updateBy != null and menuDto.updateBy != ''\">update_by,</if>\n        <if test=\"menuDto.updateTime != null\">update_time,</if>\n        parent_id\n        ) value (\n        #{menuDto.menuName},\n        #{menuDto.sort},\n        #{menuDto.menuType},\n        #{menuDto.visible},\n        #{menuDto.status},\n        <if test=\"menuDto.component != null and menuDto.component != ''\">#{menuDto.component},</if>\n        <if test=\"menuDto.path != null and menuDto.path != ''\">#{menuDto.path},</if>\n        <if test=\"menuDto.query != null and menuDto.query != ''\">#{menuDto.query},</if>\n        <if test=\"menuDto.isFrame != null and menuDto.isFrame != ''\">#{menuDto.isFrame},</if>\n        <if test=\"menuDto.isCache != null and menuDto.isCache != ''\">#{menuDto.isCache},</if>\n        <if test=\"menuDto.perms != null and menuDto.perms != ''\">#{menuDto.perms},</if>\n        <if test=\"menuDto.icon != null and menuDto.icon != ''\">#{menuDto.icon},</if>\n        <if test=\"menuDto.createBy != null and menuDto.createBy != ''\">#{menuDto.createBy},</if>\n        <if test=\"menuDto.createTime != null\">#{menuDto.createTime},</if>\n        <if test=\"menuDto.updateBy != null and menuDto.updateBy != ''\">#{menuDto.updateBy},</if>\n        <if test=\"menuDto.updateTime != null\">#{menuDto.updateTime},</if>\n        <choose>\n            <when test=\"menuDto.parentId == null or menuDto.parentId == ''\">0</when>\n            <otherwise>#{menuDto.parentId}</otherwise>\n        </choose>\n        )\n    </insert>\n\n    <!-- 更新菜单 -->\n    <update id=\"updateMenu\">\n        update sys_menu\n        <set>\n            <if test=\"menuDto.menuName != null and menuDto.menuName != ''\">menu_name = #{menuDto.menuName},</if>\n            <if test=\"menuDto.name != null and menuDto.name != ''\">`name` = #{menuDto.name},</if>\n            <if test=\"menuDto.parentId != null\">parent_id = #{menuDto.parentId},</if>\n            <if test=\"menuDto.sort != null and menuDto.sort != ''\">sort = #{menuDto.sort},</if>\n            <if test=\"menuDto.path != null and menuDto.path != ''\">`path` = #{menuDto.path},</if>\n            <if test=\"menuDto.component != null and menuDto.component != ''\">component = #{menuDto.component},</if>\n            <if test=\"menuDto.query != null and menuDto.query != ''\">`query` = #{menuDto.query},</if>\n            <if test=\"menuDto.isFrame != null and menuDto.isFrame != ''\">is_frame = #{menuDto.isFrame},</if>\n            <if test=\"menuDto.isCache != null and menuDto.isCache != ''\">is_cache = #{menuDto.isCache},</if>\n            <if test=\"menuDto.menuType != null and menuDto.menuType != ''\">menu_type = #{menuDto.menuType},</if>\n            <if test=\"menuDto.visible != null and menuDto.visible != ''\">`visible` = #{menuDto.visible},</if>\n            <if test=\"menuDto.status != null and menuDto.status != ''\">`status` = #{menuDto.status},</if>\n            <if test=\"menuDto.perms != null and menuDto.perms != ''\">perms = #{menuDto.perms},</if>\n            <if test=\"menuDto.icon != null and menuDto.icon != ''\">icon = #{menuDto.icon},</if>\n            <if test=\"menuDto.createBy != null and menuDto.createBy != ''\">create_by = #{menuDto.createBy},</if>\n            <if test=\"menuDto.createTime != null\">create_time = #{menuDto.createTime},</if>\n            <if test=\"menuDto.updateBy != null and menuDto.updateBy != ''\">update_by = #{menuDto.updateBy},</if>\n            <if test=\"menuDto.updateTime != null\">update_time = #{menuDto.updateTime},</if>\n            <if test=\"menuDto.remark != null and menuDto.remark != ''\">remark = #{menuDto.remark},</if>\n        </set>\n        where menu_id = #{menuDto.menuId}\n    </update>\n\n    <!-- 更新状态 -->\n    <update id=\"updateMenuStatus\">\n        update sys_menu\n        <set>\n            `status` = #{menuDto.status},\n            <if test=\"menuDto.updateBy != null and menuDto.updateBy != ''\">update_by = #{menuDto.updateBy},</if>\n            <if test=\"menuDto.updateTime != null\">update_time = #{menuDto.updateTime},</if>\n        </set>\n        where menu_id = #{menuDto.menuId} and deleted = '0'\n    </update>\n\n\n</mapper>"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/resources/mapper/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=\"ginyi.system.mapper.SysNoticeMapper\">\n\n    <resultMap id=\"SysNoticeResult\" type=\"sysNotice\">\n        <id property=\"noticeId\" column=\"notice_id\"/>\n        <result property=\"title\" column=\"title\"/>\n        <result property=\"type\" column=\"type\"/>\n        <result property=\"content\" column=\"content\"/>\n        <result property=\"userIds\" column=\"user_ids\" javaType=\"Long\"\n                typeHandler=\"ginyi.common.mysql.handler.JacksonArrayTypeHandler\"/>\n        <result property=\"userReadIds\" column=\"user_read_ids\" javaType=\"Long\"\n                typeHandler=\"ginyi.common.mysql.handler.JacksonArrayTypeHandler\"/>\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    <!-- 发布通知公告 -->\n    <insert id=\"addNotice\">\n        insert into sys_notice\n        <trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n            title,\n            type,\n            content,\n            `status`,\n            user_ids,\n            <if test=\"noticeDto.createBy != null and noticeDto.createBy != ''\">create_by,</if>\n            <if test=\"noticeDto.createTime != null\">create_time,</if>\n            <if test=\"noticeDto.updateBy != null and noticeDto.updateBy != ''\">update_by,</if>\n            <if test=\"noticeDto.updateTime != null\">update_time,</if>\n            <if test=\"noticeDto.remark != null and noticeDto.remark != ''\">remark,</if>\n        </trim>\n        <trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">\n            #{noticeDto.title},\n            #{noticeDto.type},\n            #{noticeDto.content},\n            #{noticeDto.status},\n            json_array\n            <foreach collection=\"noticeDto.userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                #{userId}\n            </foreach>,\n            <if test=\"noticeDto.createBy != null and noticeDto.createBy != ''\">#{noticeDto.createBy},</if>\n            <if test=\"noticeDto.createTime != null\">#{noticeDto.createTime},</if>\n            <if test=\"noticeDto.updateBy != null and noticeDto.updateBy != ''\">#{noticeDto.updateBy},</if>\n            <if test=\"noticeDto.updateTime != null\">#{noticeDto.updateTime},</if>\n            <if test=\"noticeDto.remark != null and noticeDto.remark != ''\">#{noticeDto.remark},</if>\n        </trim>\n    </insert>\n\n    <!-- 确认收到通知公告 -->\n    <update id=\"haveRead\">\n        update sys_notice\n        <set>\n            user_read_ids = json_array\n            <foreach collection=\"notice.userReadIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                #{userId}\n            </foreach>\n        </set>\n        where notice_id = #{notice.noticeId}\n    </update>\n\n    <!-- 更新通知公告 -->\n    <update id=\"updateNotice\">\n        update sys_notice\n        <set>\n            <if test=\"noticeDto.title != null and noticeDto.title != ''\">title = #{noticeDto.title},</if>\n            <if test=\"noticeDto.type != null and noticeDto.type != ''\">type = #{noticeDto.type},</if>\n            <if test=\"noticeDto.content != null and noticeDto.content != ''\">content = #{noticeDto.content},</if>\n            <if test=\"noticeDto.status != null and noticeDto.status != ''\">`status` = #{noticeDto.status},</if>\n            <if test=\"noticeDto.userIds != null and noticeDto.userIds.size() > 0\">user_ids =\n                json_array\n                <foreach collection=\"noticeDto.userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                    #{userId}\n                </foreach>,\n            </if>\n            <if test=\"noticeDto.updateBy != null and noticeDto.updateBy != ''\">update_by = #{noticeDto.updateBy},</if>\n            <if test=\"noticeDto.updateTime != null\">update_time = #{noticeDto.updateTime},</if>\n            <if test=\"noticeDto.remark != null and noticeDto != ''\">remark = #{noticeDto.remark},</if>\n        </set>\n        where notice_id = #{noticeDto.noticeId}\n    </update>\n\n    <!-- 查询通知公告列表 -->\n    <select id=\"list\" resultMap=\"SysNoticeResult\">\n        select notice_id,\n               title,\n               type,\n               content,\n               user_ids,\n               user_read_ids,\n               status,\n               create_by,\n               create_time,\n               update_by,\n               update_time,\n               remark\n        from sys_notice\n    </select>\n\n    <!-- 获取用户通知公告列表 -->\n    <select id=\"getUserNoticeList\" resultMap=\"SysNoticeResult\">\n        select notice_id,\n               title,\n               type,\n               content,\n               user_ids,\n               user_read_ids,\n               status,\n               create_by,\n               create_time,\n               update_by,\n               update_time,\n               remark\n        from sys_notice\n        where status = '0' and json_contains(sys_notice.user_ids, json_array(#{userId}))\n    </select>\n\n    <!-- 根据 noticeId 查询通知公告 -->\n    <select id=\"selectOne\" resultMap=\"SysNoticeResult\">\n        select notice_id,\n               title,\n               type,\n               content,\n               user_ids,\n               user_read_ids,\n               status,\n               create_by,\n               create_time,\n               update_by,\n               update_time,\n               remark\n        from sys_notice\n        where status = '0' and notice_id = #{noticeId}\n    </select>\n</mapper>"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/resources/mapper/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=\"ginyi.system.mapper.SysPostMapper\">\n\n    <resultMap id=\"SysPostResult\" type=\"sysPost\">\n        <id property=\"postId\" column=\"post_id\"/>\n        <result property=\"postName\" column=\"post_name\"/>\n        <result property=\"postCode\" column=\"post_code\"/>\n        <result property=\"sort\" column=\"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    <!-- 新增岗位 -->\n    <insert id=\"insertPost\">\n        insert into sys_post\n        <trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n            post_name,\n            post_code,\n            sort,\n            `status`,\n            <if test=\"postDto.createBy != null and postDto.createBy != ''\">create_by,</if>\n            <if test=\"postDto.createTime != null\">create_time,</if>\n            <if test=\"postDto.updateBy != null and postDto.updateBy != ''\">update_by,</if>\n            <if test=\"postDto.updateTime != null\">update_time,</if>\n            <if test=\"postDto.remark != null and postDto.remark != ''\">remark,</if>\n        </trim>\n        <trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">\n            #{postDto.postName},\n            #{postDto.postCode},\n            #{postDto.sort},\n            #{postDto.status},\n            <if test=\"postDto.createBy != null and postDto.createBy != ''\">#{postDto.createBy},</if>\n            <if test=\"postDto.createTime != null\">#{postDto.createTime},</if>\n            <if test=\"postDto.updateBy != null and postDto.updateBy != ''\">#{postDto.updateBy},</if>\n            <if test=\"postDto.updateTime != null\">#{postDto.updateTime},</if>\n            <if test=\"postDto.remark != null and postDto.remark != ''\">#{postDto.remark},</if>\n        </trim>\n    </insert>\n\n    <!-- 更新岗位 -->\n    <update id=\"updatePost\">\n        update sys_post\n        <set>\n            post_name = #{postDto.postName},\n            post_code = #{postDto.postCode},\n            sort = #{postDto.sort},\n            `status` = #{postDto.status},\n            <if test=\"postDto.updateBy != null and postDto.updateBy != ''\">update_by = #{postDto.updateBy},</if>\n            <if test=\"postDto.updateTime != null\">update_time = #{postDto.updateTime},</if>\n            <if test=\"postDto.remark != null and postDto != ''\">remark = #{postDto.remark},</if>\n        </set>\n        where post_id = #{postDto.postId}\n    </update>\n\n    <!-- 更新状态 -->\n    <update id=\"updatePostStatus\">\n        update sys_post\n        <set>\n            `status` = #{postDto.status},\n            <if test=\"postDto.updateBy != null and postDto.updateBy != ''\">update_by = #{postDto.updateBy},</if>\n            <if test=\"postDto.updateTime != null\">update_time = #{postDto.updateTime},</if>\n        </set>\n        where post_id = #{postDto.postId} and deleted = '0'\n    </update>\n\n    <!-- 查询列表 -->\n    <select id=\"list\" resultMap=\"SysPostResult\">\n        select post_id,\n        post_code,\n        post_name,\n        sort,\n        status,\n        create_by,\n        create_time,\n        update_by,\n        update_time,\n        remark\n        from sys_post\n        <where>\n            <if test=\"postDto.postName != null and postDto.postName != ''\">\n                and post_name like concat('%',#{postDto.postName}, '%')\n            </if>\n            <if test=\"postDto.postCode != null and postDto.postCode != ''\">\n                and post_code like concat('%',#{postDto.postCode}, '%')\n            </if>\n            <if test=\"postDto.status != null and postDto.status != ''\">\n                and `status` = #{postDto.status}\n            </if>\n            <if test=\"postDto.createBy != null and postDto.createBy != ''\">\n                and create_by like concat('%',#{postDto.createBy}, '%')\n            </if>\n            <if test=\"postDto.updateBy != null and postDto.updateBy != ''\">\n                and update_by like concat('%',#{postDto.updateBy}, '%')\n            </if>\n            <choose>\n                <when test=\"postDto.beginTime != null and postDto.endTime == null\">\n                    and create_time > #{postDto.beginTime}\n                </when>\n                <when test=\"postDto.beginTime != null and postDto.endTime != null\">\n                    and create_time between #{postDto.beginTime} and #{postDto.endTime}\n                </when>\n            </choose>\n        </where>\n    </select>\n</mapper>"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/resources/mapper/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=\"ginyi.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=\"sort\" column=\"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=\"deleted\" column=\"deleted\"/>\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    <resultMap id=\"SysRoleListResult\" type=\"ginyi.system.domain.model.vo.RoleVo\">\n        <id property=\"roleId\" column=\"role_id\"/>\n        <result property=\"roleName\" column=\"role_name\"/>\n        <result property=\"roleKey\" column=\"role_key\"/>\n        <result property=\"sort\" column=\"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        <result property=\"permissions\" column=\"menu_id\" javaType=\"Long\"\n                typeHandler=\"ginyi.common.mysql.handler.JacksonArrayTypeHandler\"/>\n    </resultMap>\n\n    <sql id=\"selectRoleVo\">\n        select distinct r.role_id,\n                        r.role_name,\n                        r.role_key,\n                        r.sort,\n                        r.data_scope,\n                        r.menu_check_strictly,\n                        r.dept_check_strictly,\n                        r.status,\n                        r.deleted,\n                        r.create_time,\n                        r.remark\n        from sys_role r\n                 left join sys_user_role ur on ur.role_id = r.role_id\n                 left join sys_user u on u.user_id = ur.user_id\n                 left join sys_dept d on u.dept_id = d.dept_id\n    </sql>\n    <!-- 新增角色 -->\n    <insert id=\"insertRole\" useGeneratedKeys=\"true\" keyProperty=\"roleId\">\n        insert into sys_role\n        <trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n            role_name,\n            role_key,\n            sort,\n            `status`,\n            <if test=\"roleDto.createBy != null and roleDto.createBy != ''\">create_by,</if>\n            <if test=\"roleDto.createTime != null\">create_time,</if>\n            <if test=\"roleDto.remark != null and roleDto.remark != ''\">remark,</if>\n        </trim>\n        <trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">\n            #{roleDto.roleName},\n            #{roleDto.roleKey},\n            #{roleDto.sort},\n            #{roleDto.status},\n            <if test=\"roleDto.createBy != null and roleDto.createBy != ''\">#{roleDto.createBy},</if>\n            <if test=\"roleDto.createTime != null\">#{roleDto.createTime},</if>\n            <if test=\"roleDto.remark != null and roleDto.remark != ''\">#{roleDto.remark},</if>\n        </trim>\n    </insert>\n\n    <!-- 新增角色权限 -->\n    <insert id=\"insertRoleMenu\">\n        insert into sys_role_menu\n        <trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n            role_id,\n            <if test=\"roleDto.permissions != null and roleDto.permissions.size() > 0\">\n                menu_id,\n            </if>\n        </trim>\n        <trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">\n            #{roleDto.roleId},\n            <choose>\n                <when test=\"roleDto.permissions != null and roleDto.permissions.size() > 0\">\n                    json_array\n                    <foreach collection=\"roleDto.permissions\" item=\"menuId\" open=\"(\" close=\")\" separator=\",\">\n                        #{menuId}\n                    </foreach>\n                </when>\n            </choose>\n        </trim>\n    </insert>\n\n    <!-- 更新角色 -->\n    <update id=\"updateRole\">\n        update sys_role\n        <set>\n            role_name = #{roleDto.roleName},\n            role_key = #{roleDto.roleKey},\n            sort = #{roleDto.sort},\n            `status` = #{roleDto.status},\n            <if test=\"roleDto.updateBy != null and roleDto.updateBy != ''\">update_by = #{roleDto.updateBy},</if>\n            <if test=\"roleDto.updateTime != null\">update_time = #{roleDto.updateTime},</if>\n            <if test=\"roleDto.remark != null and roleDto.remark != ''\">remark = #{roleDto.remark},</if>\n        </set>\n        where role_id = #{roleDto.roleId}\n    </update>\n\n    <!-- 更新角色对应的菜单权限 -->\n    <update id=\"updateRoleMenu\">\n        update sys_role_menu\n        <set>\n            <if test=\"roleDto.permissions != null and roleDto.permissions.size() > 0\">menu_id = json_array\n                <foreach collection=\"roleDto.permissions\" item=\"menuId\" open=\"(\" close=\")\" separator=\",\">\n                    #{menuId}\n                </foreach>\n            </if>\n        </set>\n        where role_id = #{roleDto.roleId}\n    </update>\n\n    <!-- 更新状态 -->\n    <update id=\"updateRoleStatus\">\n        update sys_role\n        <set>\n            `status` = #{roleDto.status},\n            <if test=\"roleDto.updateBy != null and roleDto.updateBy != ''\">update_by = #{roleDto.updateBy},</if>\n            <if test=\"roleDto.updateTime != null\">update_time = #{roleDto.updateTime},</if>\n        </set>\n        where role_id = #{roleDto.roleId} and deleted = '0'\n    </update>\n\n    <select id=\"selectRolePermissionByUserId\" parameterType=\"Long\" resultMap=\"SysRoleResult\">\n        <include refid=\"selectRoleVo\"/>\n        WHERE r.deleted = '0' and ur.user_id = #{userId}\n    </select>\n\n    <!-- 查询角色列表 -->\n    <select id=\"list\" resultType=\"ginyi.system.domain.model.vo.RoleVo\">\n        select role_id,\n        role_name,\n        role_key,\n        sort,\n        status,\n        create_by,\n        create_time,\n        update_by,\n        update_time,\n        remark\n        from sys_role\n        <where>\n            deleted = '0' and role_id != 1\n            <if test=\"roleDto.roleName != null and roleDto.roleName != ''\">\n                and role_name like concat('%', #{roleDto.roleName},'%')\n            </if>\n            <if test=\"roleDto.roleKey != null and roleDto.roleKey != ''\">\n                and role_key like concat('%',#{roleDto.roleKey},'%')\n            </if>\n            <if test=\"roleDto.status != null and roleDto.status != ''\">\n                and `status` = #{roleDto.status}\n            </if>\n            <if test=\"roleDto.createBy != null and roleDto.createBy != ''\">\n                and create_by like concat('%',#{roleDto.createBy}, '%')\n            </if>\n            <if test=\"roleDto.updateBy != null and roleDto.updateBy != ''\">\n                and update_by like concat('%',#{roleDto.updateBy}, '%')\n            </if>\n            <if test=\"roleDto.remark != null and roleDto.remark != ''\">\n                and remark like concat('%',#{roleDto.remark}, '%')\n            </if>\n            <choose>\n                <when test=\"roleDto.beginTime != null and roleDto.endTime == null\">\n                    and create_time > #{roleDto.beginTime}\n                </when>\n                <when test=\"roleDto.beginTime != null and roleDto.endTime != null\">\n                    and create_time between #{roleDto.beginTime} and #{roleDto.endTime}\n                </when>\n            </choose>\n        </where>\n    </select>\n\n    <!-- 根据角色id查询角详情 -->\n    <select id=\"selectRoleByRoleId\" resultMap=\"SysRoleListResult\">\n        select sr.role_id,\n               sr.role_name,\n               sr.role_key,\n               sr.sort,\n               sr.status,\n               sr.create_by,\n               sr.create_time,\n               sr.update_by,\n               sr.update_time,\n               sr.remark,\n               srm.menu_id\n        from sys_role as sr\n                 left join sys_role_menu as srm on sr.role_id = srm.role_id\n        where sr.role_id = ${roleId}\n    </select>\n\n\n</mapper>"
  },
  {
    "path": "ginyi-springboot/ginyi-system/src/main/resources/mapper/SysUserMapper.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\n<!-- 接口类全限定名 -->\n<mapper namespace=\"ginyi.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=\"email\" column=\"email\"/>\n        <result property=\"phoneNumber\" column=\"phone_number\"/>\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=\"deleted\" column=\"deleted\"/>\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        <collection property=\"posts\" javaType=\"java.util.List\" resultMap=\"PostResultRef\"/>\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=\"sort\" column=\"sort\"/>\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=\"sort\" column=\"sort\"/>\n        <result property=\"dataScope\" column=\"data_scope\"/>\n        <result property=\"status\" column=\"role_status\"/>\n    </resultMap>\n\n    <resultMap id=\"PostResultRef\" type=\"sysPost\">\n        <id property=\"postId\" column=\"post_id\"/>\n        <result property=\"postCode\" column=\"post_code\"/>\n        <result property=\"postName\" column=\"post_name\"/>\n        <result property=\"sort\" column=\"sort\"/>\n        <result property=\"status\" column=\"status\"/>\n    </resultMap>\n\n    <resultMap id=\"UserVoResultRef\" type=\"ginyi.system.domain.model.vo.UserVo\">\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=\"email\" column=\"email\"/>\n        <result property=\"phoneNumber\" column=\"phone_number\"/>\n        <result property=\"sex\" column=\"sex\"/>\n        <result property=\"avatar\" column=\"avatar\"/>\n        <result property=\"status\" column=\"status\"/>\n        <result property=\"remark\" column=\"remark\"/>\n        <result property=\"createBy\" column=\"create_by\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"postIds\" column=\"post_id\" javaType=\"Long\"\n                typeHandler=\"ginyi.common.mysql.handler.JacksonArrayTypeHandler\"/>\n        <result property=\"roleIds\" column=\"role_id\" javaType=\"Long\"\n                typeHandler=\"ginyi.common.mysql.handler.JacksonArrayTypeHandler\"/>\n    </resultMap>\n\n    <!-- 更新用户 -->\n    <update id=\"updateUser\">\n        update sys_user\n        <set>\n            <if test=\"userDto.deptId != null and userDto.deptId != 0\">dept_id = #{userDto.deptId},</if>\n            <if test=\"userDto.userName != null and userDto.userName != ''\">user_name = #{userDto.userName},</if>\n            <if test=\"userDto.nickName != null and userDto.nickName != ''\">nick_name = #{userDto.nickName},</if>\n            <if test=\"userDto.email != null \">email = #{userDto.email},</if>\n            <if test=\"userDto.phoneNumber != null \">phone_number = #{userDto.phoneNumber},</if>\n            <if test=\"userDto.sex != null and userDto.sex != ''\">sex = #{userDto.sex},</if>\n            <if test=\"userDto.avatar != null and userDto.avatar != ''\">avatar = #{userDto.avatar},</if>\n            <if test=\"userDto.password != null and userDto.password != ''\">`password` = #{userDto.password},</if>\n            <if test=\"userDto.status != null and userDto.status != ''\">`status` = #{userDto.status},</if>\n            <if test=\"userDto.loginIp != null and userDto.loginIp != ''\">login_ip = #{userDto.loginIp},</if>\n            <if test=\"userDto.loginDate != null\">login_date = #{userDto.loginDate},</if>\n            <if test=\"userDto.createBy != null and userDto.createBy != ''\">create_by = #{userDto.createBy},</if>\n            <if test=\"userDto.createTime != null\">create_time = #{userDto.createTime},</if>\n            <if test=\"userDto.updateBy != null and userDto.updateBy != ''\">update_by = #{userDto.updateBy},</if>\n            <if test=\"userDto.updateTime != null\">update_time = #{userDto.updateTime},</if>\n            <if test=\"userDto.remark != null\">remark = #{userDto.remark},</if>\n        </set>\n        where user_id = #{userDto.userId}\n    </update>\n\n    <select id=\"selectUserByUserName\" parameterType=\"String\" resultMap=\"SysUserResult\">\n        select su.user_id,\n               su.dept_id,\n               su.user_name,\n               su.nick_name,\n               su.email,\n               su.avatar,\n               su.phone_number,\n               su.password,\n               su.sex,\n               su.status,\n               su.deleted,\n               su.login_ip,\n               su.login_date,\n               su.create_by,\n               su.create_time,\n               su.remark,\n               sd.dept_id,\n               sd.parent_id,\n               sd.ancestors,\n               sd.dept_name,\n               sd.sort,\n               sd.leader,\n               sd.status as dept_status,\n               sr.role_id,\n               sr.role_name,\n               sr.role_key,\n               sr.sort,\n               sr.data_scope,\n               sr.status as role_status,\n               sp.post_id,\n               sp.post_code,\n               sp.post_name,\n               sp.sort,\n               sp.status as post_status\n        from sys_user as su\n                 left join sys_dept as sd on su.dept_id = sd.dept_id\n                 left join sys_user_role as sur on su.user_id = sur.user_id\n                 left join sys_role as sr on json_contains(sur.role_id, json_array(sr.role_id))\n                 left join sys_user_post as sup on sup.user_id = su.user_id\n                 left join sys_post as sp on json_contains(sup.post_id, json_array(sp.post_id))\n        where su.user_name = #{userName}\n          and su.deleted = '0'\n    </select>\n\n    <select id=\"checkUserNameUnique\" parameterType=\"String\" resultMap=\"SysUserResult\">\n        select user_id, user_name\n        from sys_user\n        where user_name = #{userName}\n          and deleted = '0'\n        limit 1\n    </select>\n\n    <!-- 根据用户id查询用户 -->\n    <select id=\"selectUserByUserId\" resultMap=\"UserVoResultRef\">\n        select su.user_id,\n               su.dept_id,\n               su.user_name,\n               su.nick_name,\n               su.email,\n               su.phone_number,\n               su.sex,\n               su.avatar,\n               su.status,\n               su.remark,\n               su.create_time,\n               su.create_by,\n               sup.post_id,\n               sur.role_id\n        from sys_user as su\n                 left join sys_user_post as sup on su.user_id = sup.user_id\n                 left join sys_user_role sur on su.user_id = sur.user_id\n        where su.user_id = #{userId}\n          and su.deleted = '0'\n    </select>\n\n    <!-- 查询用户列表(不包含admin) -->\n    <select id=\"list\" resultMap=\"UserVoResultRef\">\n        select\n        su.user_id,\n        su.dept_id,\n        su.user_name,\n        su.nick_name,\n        su.email,\n        su.phone_number,\n        su.sex,\n        su.avatar,\n        su.status,\n        su.remark,\n        su.create_time,\n        su.create_by,\n        sup.post_id,\n        sur.role_id\n        from sys_user as su\n        left join sys_user_post as sup on su.user_id = sup.user_id\n        left join sys_user_role sur on su.user_id = sur.user_id\n        <where>\n            su.deleted = '0' and su.user_id != 1\n            <if test=\"userDto.userName != null and userDto.userName != ''\">\n                and su.user_name like concat('%', #{userDto.userName}, '%')\n            </if>\n            <if test=\"userDto.nickName != null and userDto.nickName != ''\">\n                and su.nick_name like concat('%',#{userDto.nickName}, '%')\n            </if>\n            <if test=\"userDto.phoneNumber != null and userDto.phoneNumber != ''\">\n                and su.phone_number like concat('%',#{userDto.phoneNumber}, '%')\n            </if>\n            <if test=\"userDto.email != null and userDto.email != ''\">\n                and su.email like concat('%', #{userDto.email}, '%')\n            </if>\n            <if test=\"userDto.remark != null and userDto.remark != ''\">\n                and su.remark like concat('%', #{userDto.remark}, '%')\n            </if>\n            <if test=\"userDto.sex != null and userDto.sex != ''\">\n                and su.sex = #{userDto.sex}\n            </if>\n            <if test=\"userDto.status != null and userDto.status != ''\">\n                and su.status = #{userDto.status}\n            </if>\n            <if test=\"userDto.createBy != null and userDto.createBy != ''\">\n                and su.createBy like concat('%', #{userDto.createBy}, '%')\n            </if>\n            <choose>\n                <when test=\"userDto.beginTime != null and userDto.endTime == null\">\n                    and su.create_time > #{userDto.beginTime}\n                </when>\n                <when test=\"userDto.beginTime != null and userDto.endTime != null\">\n                    and su.create_time between #{userDto.beginTime} and #{userDto.endTime}\n                </when>\n            </choose>\n        </where>\n    </select>\n\n    <!-- 根据部门ids获取用户列表 -->\n    <select id=\"selectUserByDeptIds\" resultType=\"ginyi.system.domain.SysUser\">\n        select user_id, nick_name\n        from sys_user\n        where dept_id in\n        <foreach collection=\"deptIds\" item=\"deptId\" open=\"(\" close=\")\" separator=\",\">\n            #{deptId}\n        </foreach>\n    </select>\n\n\n    <!-- 插入新用户 -->\n    <insert id=\"insertUser\" useGeneratedKeys=\"true\" keyProperty=\"userId\">\n        insert into sys_user\n        <trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n            <if test=\"userDto.userId != null and userDto.userId != 0\">user_id,</if>\n            <if test=\"userDto.deptId != null and userDto.deptId != 0\">dept_id,</if>\n            <if test=\"userDto.userName != null and userDto.userName != ''\">user_name,</if>\n            <if test=\"userDto.nickName != null and userDto.nickName != ''\">nick_name,</if>\n            <if test=\"userDto.email != null and userDto.email != ''\">email,</if>\n            <if test=\"userDto.avatar != null and userDto.avatar != ''\">avatar,</if>\n            <if test=\"userDto.phoneNumber != null and userDto.phoneNumber != ''\">phone_number,</if>\n            <if test=\"userDto.sex != null and userDto.sex != ''\">sex,</if>\n            <if test=\"userDto.password != null and userDto.password != ''\">password,</if>\n            <if test=\"userDto.status != null and userDto.status != ''\">status,</if>\n            <if test=\"userDto.createBy != null and userDto.createBy != ''\">create_by,</if>\n            <if test=\"userDto.createTime != null\">create_time,</if>\n            <if test=\"userDto.updateBy != null and userDto.updateBy != ''\">update_by,</if>\n            <if test=\"userDto.updateTime != null\">update_time,</if>\n            <if test=\"userDto.remark != null and userDto.remark != ''\">remark,</if>\n        </trim>\n        <trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">\n            <if test=\"userDto.userId != null and userDto.userId != ''\">#{userDto.userId},</if>\n            <if test=\"userDto.deptId != null and userDto.deptId != ''\">#{userDto.deptId},</if>\n            <if test=\"userDto.userName != null and userDto.userName != ''\">#{userDto.userName},</if>\n            <if test=\"userDto.nickName != null and userDto.nickName != ''\">#{userDto.nickName},</if>\n            <if test=\"userDto.email != null and userDto.email != ''\">#{userDto.email},</if>\n            <if test=\"userDto.avatar != null and userDto.avatar != ''\">#{userDto.avatar},</if>\n            <if test=\"userDto.phoneNumber != null and userDto.phoneNumber != ''\">#{userDto.phoneNumber},</if>\n            <if test=\"userDto.sex != null and userDto.sex != ''\">#{userDto.sex},</if>\n            <if test=\"userDto.password != null and userDto.password != ''\">#{userDto.password},</if>\n            <if test=\"userDto.status != null and userDto.status != ''\">#{userDto.status},</if>\n            <if test=\"userDto.createBy != null and userDto.createBy != ''\">#{userDto.createBy},</if>\n            <if test=\"userDto.createTime != null\">#{userDto.createTime},</if>\n            <if test=\"userDto.updateBy != null and userDto.updateBy != ''\">#{userDto.updateBy},</if>\n            <if test=\"userDto.updateTime != null\">#{userDto.updateTime},</if>\n            <if test=\"userDto.remark != null and userDto.remark != ''\">#{userDto.remark},</if>\n        </trim>\n    </insert>\n\n    <!-- 用户岗位中间表，插入用户对应的岗位信息 -->\n    <insert id=\"insertUserPostIds\">\n        insert into sys_user_post\n        <trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n            <if test=\"userDto.userId != null and userDto.userId != ''\">user_id,</if>\n            <if test=\"userDto.postIds != null\">post_id</if>\n        </trim>\n        <trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">\n            <if test=\"userDto.userId != null and userDto.userId != ''\">#{userDto.userId},</if>\n            <if test=\"userDto.postIds != null and userDto.postIds.size() > 0\">\n                json_array\n                <foreach collection=\"userDto.postIds\" item=\"postId\" open=\"(\" close=\")\" separator=\",\">\n                    #{postId}\n                </foreach>\n            </if>\n        </trim>\n    </insert>\n\n    <!-- 用户角色中间表，插入用户对应的岗位信息 -->\n    <insert id=\"insertUserRoleIds\">\n        insert into sys_user_role\n        <trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n            <if test=\"userDto.userId != null and userDto.userId != ''\">user_id,</if>\n            <if test=\"userDto.roleIds != null\">role_id</if>\n        </trim>\n        <trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">\n            <if test=\"userDto.userId != null and userDto.userId != ''\">#{userDto.userId},</if>\n            <if test=\"userDto.roleIds != null and userDto.roleIds.size() > 0\">\n                json_array\n                <foreach collection=\"userDto.roleIds\" item=\"roleId\" open=\"(\" close=\")\" separator=\",\">\n                    #{roleId}\n                </foreach>\n            </if>\n        </trim>\n    </insert>\n\n\n    <!-- 更新用户岗位中间表, 更新用户对应的岗位信息 -->\n    <update id=\"updateUserPostIds\">\n        update sys_user_post\n        <set>\n            <if test=\"userDto.postIds != null and userDto.postIds.size() > 0\">post_id = json_array\n                <foreach collection=\"userDto.postIds\" item=\"postId\" open=\"(\" close=\")\" separator=\",\">\n                    #{postId}\n                </foreach>\n            </if>\n        </set>\n        where user_id = #{userDto.userId}\n    </update>\n\n    <!-- 更新用户角色中间表, 更新用户对应的角色信息 -->\n    <update id=\"updateUserRoleIds\">\n        update sys_user_role\n        <set>\n            <if test=\"userDto.roleIds != null and userDto.roleIds.size() > 0\">role_id = json_array\n                <foreach collection=\"userDto.roleIds\" item=\"roleId\" open=\"(\" close=\")\" separator=\",\">\n                    #{roleId}\n                </foreach>\n            </if>\n        </set>\n        where user_id = #{userDto.userId}\n    </update>\n\n    <!-- 更新用户状态 -->\n    <update id=\"updateUserStatus\">\n        update sys_user\n        <set>\n            `status` = #{userDto.status},\n            <if test=\"userDto.updateBy != null and userDto.updateBy != ''\">update_by = #{userDto.updateBy},</if>\n            <if test=\"userDto.updateTime != null\">update_time = #{userDto.updateTime},</if>\n        </set>\n        where user_id = #{userDto.userId} and deleted = '0'\n    </update>\n\n\n</mapper>"
  },
  {
    "path": "ginyi-springboot/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>pom</packaging>\n\n    <!-- 基础信息 -->\n    <name>${project.artifactId}</name>\n    <groupId>com.ginyi</groupId>\n    <artifactId>ginyi-springboot</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <description>ginyi-demo</description>\n\n    <!-- 多模块 -->\n    <modules>\n        <module>ginyi-common</module>\n        <module>ginyi-server</module>\n        <module>ginyi-framework</module>\n        <module>ginyi-system</module>\n    </modules>\n\n    <!-- parent -->\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.5.14</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <!-- 版本管理 -->\n    <properties>\n        <ginyi.version>0.0.1-SNAPSHOT</ginyi.version>\n        <java.version>1.8</java.version>\n        <spring-boot.version>2.5.14</spring-boot.version>\n        <swagger.version>2.9.2</swagger.version>\n        <knife4j.version>2.0.9</knife4j.version>\n        <hutool.version>5.8.5</hutool.version>\n        <email.version>1.6.2</email.version>\n        <servlet.version>3.1.0</servlet.version>\n        <jansi.version>1.18</jansi.version>\n        <druid.version>1.1.23</druid.version>\n        <mybatis-plus.version>3.4.2</mybatis-plus.version>\n        <lombok.version>1.18.24</lombok.version>\n        <redis.version>2.2.5.RELEASE</redis.version>\n        <fastjson.version>1.2.76</fastjson.version>\n        <mysql.version>8.0.30</mysql.version>\n        <mybatis.version>2.2.2</mybatis.version>\n        <validation.version>2.7.3</validation.version>\n        <jwt.version>0.9.1</jwt.version>\n        <commons-pool2.version>2.9.0</commons-pool2.version>\n        <commons-lang3.version>3.12.0</commons-lang3.version>\n        <bitwalker.version>1.21</bitwalker.version>\n        <fastjson.version>2.0.16</fastjson.version>\n        <redis.version>3.0.1</redis.version>\n        <oshi.version>6.4.3</oshi.version>\n\n    </properties>\n\n    <!-- 以下依赖 全局所有的模块都会引入  -->\n    <dependencies>\n\n        <!-- 获取系统信息 -->\n        <dependency>\n            <groupId>com.github.oshi</groupId>\n            <artifactId>oshi-core</artifactId>\n            <version>${oshi.version}</version>\n        </dependency>\n\n        <!-- Java工具类 -->\n        <dependency>\n            <groupId>cn.hutool</groupId>\n            <artifactId>hutool-all</artifactId>\n            <version>${hutool.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>javax.servlet</groupId>\n            <artifactId>javax.servlet-api</artifactId>\n            <version>${servlet.version}</version>\n            <!-- 此包一般在Servlet容器中都有提供 -->\n            <scope>provided</scope>\n        </dependency>\n\n        <!-- fastjson -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <version>${fastjson.version}</version>\n        </dependency>\n\n        <!-- 参数校验工具 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n            <version>${validation.version}</version>\n        </dependency>\n\n        <!-- 控制台字体颜色 -->\n        <dependency>\n            <groupId>org.fusesource.jansi</groupId>\n            <artifactId>jansi</artifactId>\n            <version>${jansi.version}</version>\n        </dependency>\n\n        <!-- lombok -->\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>${lombok.version}</version>\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n\n    <!-- 依赖管理 -->\n    <dependencyManagement>\n        <dependencies>\n\n            <!-- SpringBoot Web容器 -->\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-web</artifactId>\n                <version>${spring-boot.version}</version>\n            </dependency>\n\n            <!--测试依赖-->\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-test</artifactId>\n                <version>${spring-boot.version}</version>\n                <scope>test</scope>\n            </dependency>\n\n            <!-- spring boot 依赖 -->\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            <!-- aop -->\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-aop</artifactId>\n                <version>${spring-boot.version}</version>\n            </dependency>\n\n            <!-- MongoDB -->\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-data-mongodb</artifactId>\n                <version>${spring-boot.version}</version>\n            </dependency>\n\n            <!-- websocket -->\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-websocket</artifactId>\n                <version>${spring-boot.version}</version>\n            </dependency>\n\n            <!-- Token生成与解析-->\n            <dependency>\n                <groupId>io.jsonwebtoken</groupId>\n                <artifactId>jjwt</artifactId>\n                <version>${jwt.version}</version>\n            </dependency>\n\n            <!-- 解析客户端操作系统、浏览器等 -->\n            <dependency>\n                <groupId>eu.bitwalker</groupId>\n                <artifactId>UserAgentUtils</artifactId>\n                <version>${bitwalker.version}</version>\n            </dependency>\n\n            <!-- spring security -->\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-security</artifactId>\n                <version>${spring-boot.version}</version>\n            </dependency>\n\n            <!--常用工具类 -->\n            <dependency>\n                <groupId>org.apache.commons</groupId>\n                <artifactId>commons-lang3</artifactId>\n                <version>${commons-lang3.version}</version>\n            </dependency>\n\n            <!-- pool 对象池 -->\n            <dependency>\n                <groupId>org.apache.commons</groupId>\n                <artifactId>commons-pool2</artifactId>\n                <version>${commons-pool2.version}</version>\n            </dependency>\n\n            <!-- swagger2接口文档 -->\n            <dependency>\n                <groupId>io.springfox</groupId>\n                <artifactId>springfox-swagger2</artifactId>\n                <version>${swagger.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>io.springfox</groupId>\n                <artifactId>springfox-swagger-ui</artifactId>\n                <version>${swagger.version}</version>\n            </dependency>\n\n            <!-- Knife4j -->\n            <dependency>\n                <groupId>com.github.xiaoymin</groupId>\n                <artifactId>knife4j-spring-boot-starter</artifactId>\n                <!-- 2.0.5可以配合@ApiOperationSupport -->\n                <version>2.0.5</version>\n            </dependency>\n\n            <!-- aop -->\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-aop</artifactId>\n                <version>${spring-boot.version}</version>\n            </dependency>\n\n            <!-- druid -->\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>druid-spring-boot-starter</artifactId>\n                <version>${druid.version}</version>\n            </dependency>\n\n            <!-- mysql -->\n            <dependency>\n                <groupId>mysql</groupId>\n                <artifactId>mysql-connector-java</artifactId>\n                <version>${mysql.version}</version>\n            </dependency>\n\n            <!-- SpringBoot集成mybatis框架 -->\n            <dependency>\n                <groupId>org.mybatis.spring.boot</groupId>\n                <artifactId>mybatis-spring-boot-starter</artifactId>\n                <version>${mybatis.version}</version>\n            </dependency>\n\n            <!-- mybatis-plus -->\n            <dependency>\n                <groupId>com.baomidou</groupId>\n                <artifactId>mybatis-plus-boot-starter</artifactId>\n                <version>${mybatis-plus.version}</version>\n            </dependency>\n\n            <!-- redis -->\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-data-redis</artifactId>\n                <version>${redis.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alibaba.fastjson2</groupId>\n                <artifactId>fastjson2</artifactId>\n                <version>${fastjson.version}</version>\n            </dependency>\n\n            <!-- 邮箱 -->\n            <dependency>\n                <groupId>com.sun.mail</groupId>\n                <artifactId>javax.mail</artifactId>\n                <version>${email.version}</version>\n            </dependency>\n\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <finalName>${project.name}</finalName>\n        <resources>\n            <resource>\n                <directory>src/main/resources</directory>\n                <includes>\n                    <include>**/*.*</include>\n                </includes>\n                <filtering>false</filtering>\n            </resource>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <include>**/*.xml</include>\n                </includes>\n                <filtering>false</filtering>\n            </resource>\n        </resources>\n        <pluginManagement>\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        </pluginManagement>\n        <plugins>\n            <plugin>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.8.0</version>\n                <configuration>\n                    <target>${maven.compiler.target}</target>\n                    <source>${maven.compiler.source}</source>\n                    <encoding>UTF-8</encoding>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n\n    <repositories>\n        <repository>\n            <id>spring-milestones</id>\n            <name>Spring Milestones</name>\n            <url>https://repo.spring.io/milestone</url>\n            <snapshots>\n                <enabled>false</enabled>\n            </snapshots>\n        </repository>\n        <repository>\n            <id>spring-snapshots</id>\n            <name>Spring Snapshots</name>\n            <url>https://repo.spring.io/snapshot</url>\n            <releases>\n                <enabled>false</enabled>\n            </releases>\n        </repository>\n    </repositories>\n    <pluginRepositories>\n        <pluginRepository>\n            <id>spring-milestones</id>\n            <name>Spring Milestones</name>\n            <url>https://repo.spring.io/milestone</url>\n            <snapshots>\n                <enabled>false</enabled>\n            </snapshots>\n        </pluginRepository>\n        <pluginRepository>\n            <id>spring-snapshots</id>\n            <name>Spring Snapshots</name>\n            <url>https://repo.spring.io/snapshot</url>\n            <releases>\n                <enabled>false</enabled>\n            </releases>\n        </pluginRepository>\n    </pluginRepositories>\n\n</project>\n"
  },
  {
    "path": "ginyi-springboot/sql/ginyi-spring-vue-mysql.sql",
    "content": "/*\n Navicat Premium Data Transfer\n\n Source Server         : MySQL8.0\n Source Server Type    : MySQL\n Source Server Version : 80027\n Source Host           : localhost:3306\n Source Schema         : ginyi-spring-vue\n\n Target Server Type    : MySQL\n Target Server Version : 80027\n File Encoding         : 65001\n\n Date: 16/09/2023 15:27:21\n*/\n\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Table structure for gen_table\n-- ----------------------------\nDROP TABLE IF EXISTS `gen_table`;\nCREATE TABLE `gen_table`  (\n  `table_id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `table_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '表名称',\n  `table_comment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '表描述',\n  `sub_table_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '关联子表的表名',\n  `sub_table_fk_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '子表关联的外键名',\n  `class_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '实体类名称',\n  `tpl_category` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'crud' COMMENT '使用的模板（crud单表操作 tree树表操作）',\n  `package_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '生成包路径',\n  `module_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '生成模块名',\n  `business_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '生成业务名',\n  `function_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '生成功能名',\n  `function_author` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '生成功能作者',\n  `gen_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '生成代码方式（0zip压缩包 1自定义路径）',\n  `gen_path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '/' COMMENT '生成路径（不填默认项目路径）',\n  `options` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '其它生成选项',\n  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注',\n  PRIMARY KEY (`table_id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '代码生成业务表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of gen_table\n-- ----------------------------\n\n-- ----------------------------\n-- Table structure for gen_table_column\n-- ----------------------------\nDROP TABLE IF EXISTS `gen_table_column`;\nCREATE TABLE `gen_table_column`  (\n  `column_id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `table_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '归属表编号',\n  `column_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '列名称',\n  `column_comment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '列描述',\n  `column_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '列类型',\n  `java_type` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'JAVA类型',\n  `java_field` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'JAVA字段名',\n  `is_pk` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否主键（1是）',\n  `is_increment` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否自增（1是）',\n  `is_required` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否必填（1是）',\n  `is_insert` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否为插入字段（1是）',\n  `is_edit` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否编辑字段（1是）',\n  `is_list` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否列表字段（1是）',\n  `is_query` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否查询字段（1是）',\n  `query_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'EQ' COMMENT '查询方式（等于、不等于、大于、小于、范围）',\n  `html_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '显示类型（文本框、文本域、下拉框、复选框、单选框、日期控件）',\n  `dict_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '字典类型',\n  `sort` int NULL DEFAULT NULL COMMENT '排序',\n  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',\n  PRIMARY KEY (`column_id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '代码生成业务表字段' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of gen_table_column\n-- ----------------------------\n\n-- ----------------------------\n-- Table structure for sys_config\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_config`;\nCREATE TABLE `sys_config`  (\n  `config_id` int NOT NULL AUTO_INCREMENT COMMENT '参数主键',\n  `config_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '参数名称',\n  `config_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '参数键名',\n  `config_value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '参数键值',\n  `config_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'N' COMMENT '系统内置（Y是 N否）',\n  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注',\n  PRIMARY KEY (`config_id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 100 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '参数配置表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_config\n-- ----------------------------\nINSERT INTO `sys_config` VALUES (1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', '2022-12-03 07:21:33', '', NULL, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow');\nINSERT INTO `sys_config` VALUES (2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', '2022-12-03 07:21:33', '', NULL, '初始化密码 123456');\nINSERT INTO `sys_config` VALUES (3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', '2022-12-03 07:21:33', '', NULL, '深色主题theme-dark，浅色主题theme-light');\nINSERT INTO `sys_config` VALUES (4, '账号自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'admin', '2022-12-03 07:21:33', '', NULL, '是否开启验证码功能（true开启，false关闭）');\nINSERT INTO `sys_config` VALUES (5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', '2022-12-03 07:21:33', '', NULL, '是否开启注册用户功能（true开启，false关闭）');\n\n-- ----------------------------\n-- Table structure for sys_dept\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_dept`;\nCREATE TABLE `sys_dept`  (\n  `dept_id` bigint NOT NULL AUTO_INCREMENT COMMENT '部门id',\n  `parent_id` bigint NULL DEFAULT 0 COMMENT '父部门id',\n  `ancestors` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '祖级列表',\n  `dept_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '部门名称',\n  `sort` int NULL DEFAULT 0 COMMENT '显示顺序',\n  `leader` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '负责人',\n  `phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '联系电话',\n  `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '邮箱',\n  `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '部门状态（0正常 1停用）',\n  `deleted` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '删除标志（0代表存在 2代表删除）',\n  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注',\n  PRIMARY KEY (`dept_id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 1003 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '部门表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_dept\n-- ----------------------------\nINSERT INTO `sys_dept` VALUES (100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', 'Ginyi', '2023-05-28 20:39:45', NULL);\nINSERT INTO `sys_dept` VALUES (101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', '', NULL, NULL);\nINSERT INTO `sys_dept` VALUES (102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', '', NULL, NULL);\nINSERT INTO `sys_dept` VALUES (103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', '', NULL, NULL);\nINSERT INTO `sys_dept` VALUES (104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', '', NULL, NULL);\nINSERT INTO `sys_dept` VALUES (105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', '', NULL, NULL);\nINSERT INTO `sys_dept` VALUES (106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', '', NULL, NULL);\nINSERT INTO `sys_dept` VALUES (107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', '', NULL, NULL);\nINSERT INTO `sys_dept` VALUES (108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', 'Ginyi', '2023-02-17 22:32:20', NULL);\nINSERT INTO `sys_dept` VALUES (109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', 'Ginyi', '2023-05-28 18:01:25', NULL);\n\n-- ----------------------------\n-- Table structure for sys_dict_data\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_dict_data`;\nCREATE TABLE `sys_dict_data`  (\n  `dict_code` bigint NOT NULL AUTO_INCREMENT COMMENT '字典编码',\n  `dict_sort` int NULL DEFAULT 0 COMMENT '字典排序',\n  `dict_label` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '字典标签',\n  `dict_value` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '字典键值',\n  `dict_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '字典类型',\n  `css_class` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '样式属性（其他样式扩展）',\n  `list_class` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '表格回显样式',\n  `is_default` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'N' COMMENT '是否默认（Y是 N否）',\n  `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '状态（0正常 1停用）',\n  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注',\n  PRIMARY KEY (`dict_code`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 100 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '字典数据表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_dict_data\n-- ----------------------------\nINSERT INTO `sys_dict_data` VALUES (1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '性别男');\nINSERT INTO `sys_dict_data` VALUES (2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '性别女');\nINSERT INTO `sys_dict_data` VALUES (3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '性别未知');\nINSERT INTO `sys_dict_data` VALUES (4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '显示菜单');\nINSERT INTO `sys_dict_data` VALUES (5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '隐藏菜单');\nINSERT INTO `sys_dict_data` VALUES (6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '正常状态');\nINSERT INTO `sys_dict_data` VALUES (7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '停用状态');\nINSERT INTO `sys_dict_data` VALUES (8, 1, '正常', '0', 'sys_job_status', '', 'primary', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '正常状态');\nINSERT INTO `sys_dict_data` VALUES (9, 2, '暂停', '1', 'sys_job_status', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '停用状态');\nINSERT INTO `sys_dict_data` VALUES (10, 1, '默认', 'DEFAULT', 'sys_job_group', '', '', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '默认分组');\nINSERT INTO `sys_dict_data` VALUES (11, 2, '系统', 'SYSTEM', 'sys_job_group', '', '', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '系统分组');\nINSERT INTO `sys_dict_data` VALUES (12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '系统默认是');\nINSERT INTO `sys_dict_data` VALUES (13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '系统默认否');\nINSERT INTO `sys_dict_data` VALUES (14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '通知');\nINSERT INTO `sys_dict_data` VALUES (15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '公告');\nINSERT INTO `sys_dict_data` VALUES (16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '正常状态');\nINSERT INTO `sys_dict_data` VALUES (17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '关闭状态');\nINSERT INTO `sys_dict_data` VALUES (18, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '其他操作');\nINSERT INTO `sys_dict_data` VALUES (19, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '新增操作');\nINSERT INTO `sys_dict_data` VALUES (20, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '修改操作');\nINSERT INTO `sys_dict_data` VALUES (21, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '删除操作');\nINSERT INTO `sys_dict_data` VALUES (22, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '授权操作');\nINSERT INTO `sys_dict_data` VALUES (23, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '导出操作');\nINSERT INTO `sys_dict_data` VALUES (24, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '导入操作');\nINSERT INTO `sys_dict_data` VALUES (25, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '强退操作');\nINSERT INTO `sys_dict_data` VALUES (26, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '生成操作');\nINSERT INTO `sys_dict_data` VALUES (27, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '清空操作');\nINSERT INTO `sys_dict_data` VALUES (28, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '正常状态');\nINSERT INTO `sys_dict_data` VALUES (29, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '停用状态');\n\n-- ----------------------------\n-- Table structure for sys_dict_type\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_dict_type`;\nCREATE TABLE `sys_dict_type`  (\n  `dict_id` bigint NOT NULL AUTO_INCREMENT COMMENT '字典主键',\n  `dict_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '字典名称',\n  `dict_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '字典类型',\n  `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '状态（0正常 1停用）',\n  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注',\n  PRIMARY KEY (`dict_id`) USING BTREE,\n  UNIQUE INDEX `dict_type`(`dict_type`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 100 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '字典类型表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_dict_type\n-- ----------------------------\nINSERT INTO `sys_dict_type` VALUES (1, '用户性别', 'sys_user_sex', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '用户性别列表');\nINSERT INTO `sys_dict_type` VALUES (2, '菜单状态', 'sys_show_hide', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '菜单状态列表');\nINSERT INTO `sys_dict_type` VALUES (3, '系统开关', 'sys_normal_disable', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '系统开关列表');\nINSERT INTO `sys_dict_type` VALUES (4, '任务状态', 'sys_job_status', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '任务状态列表');\nINSERT INTO `sys_dict_type` VALUES (5, '任务分组', 'sys_job_group', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '任务分组列表');\nINSERT INTO `sys_dict_type` VALUES (6, '系统是否', 'sys_yes_no', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '系统是否列表');\nINSERT INTO `sys_dict_type` VALUES (7, '通知类型', 'sys_notice_type', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '通知类型列表');\nINSERT INTO `sys_dict_type` VALUES (8, '通知状态', 'sys_notice_status', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '通知状态列表');\nINSERT INTO `sys_dict_type` VALUES (9, '操作类型', 'sys_oper_type', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '操作类型列表');\nINSERT INTO `sys_dict_type` VALUES (10, '系统状态', 'sys_common_status', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '登录状态列表');\n\n-- ----------------------------\n-- Table structure for sys_job\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_job`;\nCREATE TABLE `sys_job`  (\n  `job_id` bigint NOT NULL AUTO_INCREMENT COMMENT '任务ID',\n  `job_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '任务名称',\n  `job_group` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'DEFAULT' COMMENT '任务组名',\n  `invoke_target` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '调用目标字符串',\n  `cron_expression` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT 'cron执行表达式',\n  `misfire_policy` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '3' COMMENT '计划执行错误策略（1立即执行 2执行一次 3放弃执行）',\n  `concurrent` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '1' COMMENT '是否并发执行（0允许 1禁止）',\n  `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '状态（0正常 1暂停）',\n  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '备注信息',\n  PRIMARY KEY (`job_id`, `job_name`, `job_group`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 123 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '定时任务调度表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_job\n-- ----------------------------\nINSERT INTO `sys_job` VALUES (1, '系统默认（无参）', 'DEFAULT', 'ryTask.ryNoParams', '0/10 * * * * ?', '3', '1', '1', 'admin', '2022-12-03 07:21:33', '', '2022-12-07 08:22:56', '');\nINSERT INTO `sys_job` VALUES (2, '系统默认（有参）', 'DEFAULT', 'ryTask.ryParams(\\'ry\\')', '0/15 * * * * ?', '3', '1', '1', 'admin', '2022-12-03 07:21:33', '', '2022-12-07 08:22:56', '');\nINSERT INTO `sys_job` VALUES (3, '系统默认（多参）', 'DEFAULT', 'ryTask.ryMultipleParams(\\'ry\\', true, 2000L, 316.50D, 100)', '0/20 * * * * ?', '3', '1', '1', 'admin', '2022-12-03 07:21:33', '', '2022-12-07 08:22:56', '');\n\n-- ----------------------------\n-- Table structure for sys_job_log\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_job_log`;\nCREATE TABLE `sys_job_log`  (\n  `job_log_id` bigint NOT NULL AUTO_INCREMENT COMMENT '任务日志ID',\n  `job_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '任务名称',\n  `job_group` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '任务组名',\n  `invoke_target` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '调用目标字符串',\n  `job_message` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '日志信息',\n  `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '执行状态（0正常 1失败）',\n  `exception_info` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '异常信息',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  PRIMARY KEY (`job_log_id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '定时任务调度日志表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_job_log\n-- ----------------------------\n\n-- ----------------------------\n-- Table structure for sys_menu\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_menu`;\nCREATE TABLE `sys_menu`  (\n  `menu_id` bigint NOT NULL AUTO_INCREMENT COMMENT '菜单ID',\n  `menu_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '菜单名称',\n  `parent_id` bigint NULL DEFAULT 0 COMMENT '父菜单ID',\n  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '路由name，用于跳转',\n  `sort` int NULL DEFAULT 0 COMMENT '显示顺序',\n  `path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '路由地址',\n  `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '组件路径',\n  `query` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '路由参数',\n  `is_frame` int NULL DEFAULT 1 COMMENT '是否为外链（0是 1否）',\n  `is_cache` int NULL DEFAULT 0 COMMENT '是否缓存（0缓存 1不缓存）',\n  `deleted` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '删除标志（0代表存在 2代表删除）',\n  `menu_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '菜单类型（M目录 C菜单 F按钮）',\n  `visible` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '菜单状态（0显示 1隐藏）',\n  `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '菜单状态（0正常 1停用）',\n  `perms` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '权限标识',\n  `icon` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '#' COMMENT '菜单图标',\n  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '备注',\n  PRIMARY KEY (`menu_id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 2039 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '菜单权限表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_menu\n-- ----------------------------\nINSERT INTO `sys_menu` VALUES (1, '系统管理', 0, 'system', 1, 'system', NULL, '', 1, 0, '0', 'M', '0', '0', '', 'AccessibilitySharp', 'admin', '2022-12-03 07:21:33', '', NULL, '系统管理目录');\nINSERT INTO `sys_menu` VALUES (2, '系统监控', 0, 'sysmonitor', 2, 'sysmonitor', NULL, '', 1, 0, '0', 'M', '0', '0', '', 'PersonOutline', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-02-23 21:14:26', '系统监控目录');\nINSERT INTO `sys_menu` VALUES (3, '系统工具', 0, 'systools', 3, 'systools', NULL, '', 1, 0, '0', 'M', '0', '0', '', 'BuildSharp', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-02-26 17:01:21', '系统工具目录');\nINSERT INTO `sys_menu` VALUES (100, '用户管理', 1, 'user', 1, 'user', 'pages/system/user/index', '', 1, 0, '0', 'C', '0', '0', 'system:user:list', 'Duplicate', 'admin', '2022-12-03 07:21:33', '', NULL, '用户管理菜单');\nINSERT INTO `sys_menu` VALUES (101, '角色管理', 1, 'role', 2, 'role', 'pages/system/role/index', '', 1, 0, '0', 'C', '0', '0', 'system:role:list', 'FishSharp', 'admin', '2022-12-03 07:21:33', '', NULL, '角色管理菜单');\nINSERT INTO `sys_menu` VALUES (102, '菜单管理', 1, 'menu', 3, 'menu', 'pages/system/menu/index', '', 1, 0, '0', 'C', '0', '0', 'system:menu:list', 'Heart', 'admin', '2022-12-03 07:21:33', '', NULL, '菜单管理菜单');\nINSERT INTO `sys_menu` VALUES (103, '部门管理', 1, 'department', 4, 'department', 'pages/system/department/index', '', 1, 0, '0', 'C', '0', '0', 'system:dept:list', 'LayersSharp', 'admin', '2022-12-03 07:21:33', '', NULL, '部门管理菜单');\nINSERT INTO `sys_menu` VALUES (104, '岗位管理', 1, 'position', 5, 'position', 'pages/system/position/index', '', 1, 0, '0', 'C', '0', '0', 'system:post:list', 'PawSharp', 'admin', '2022-12-03 07:21:33', '', NULL, '岗位管理菜单');\nINSERT INTO `sys_menu` VALUES (105, '字典管理', 1, 'dict', 6, 'dict', 'pages/system/dict/index', '', 1, 0, '0', 'C', '0', '0', 'system:dict:list', 'LogoElectron', 'admin', '2022-12-03 07:21:33', '', NULL, '字典管理菜单');\nINSERT INTO `sys_menu` VALUES (106, '参数设置', 1, 'params', 7, 'params', 'pages/system/params/index', '', 1, 0, '0', 'C', '0', '0', 'system:config:list', 'LogoWechat', 'admin', '2022-12-03 07:21:33', '', NULL, '参数设置菜单');\nINSERT INTO `sys_menu` VALUES (107, '通知公告', 1, 'notice', 8, 'notice', 'pages/system/notice/index', '', 1, 0, '0', 'C', '0', '0', 'system:notice:list', 'MailUnreadOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '通知公告菜单');\nINSERT INTO `sys_menu` VALUES (108, '日志管理', 1, 'log', 9, 'log', '', '', 1, 0, '0', 'M', '0', '0', '', 'Mic', 'admin', '2022-12-03 07:21:33', '', NULL, '日志管理菜单');\nINSERT INTO `sys_menu` VALUES (109, '在线用户', 2, 'online', 1, 'online', 'pages/monitor/online/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:online:list', 'Disc', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-02-23 21:14:13', '在线用户菜单');\nINSERT INTO `sys_menu` VALUES (110, '定时任务', 2, 'task', 2, 'task', 'pages/monitor/task/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:job:list', 'ExtensionPuzzleSharp', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-02-26 16:59:34', '定时任务菜单');\nINSERT INTO `sys_menu` VALUES (111, '数据监控', 2, 'data', 3, 'data', 'pages/monitor/data/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:druid:list', 'FilterCircle', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-06-30 14:02:06', '数据监控菜单');\nINSERT INTO `sys_menu` VALUES (112, '服务监控', 2, 'service', 4, 'service', 'pages/monitor/service/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:server:list', 'FootballSharp', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-06-30 14:02:07', '服务监控菜单');\nINSERT INTO `sys_menu` VALUES (113, '缓存监控', 2, 'cache', 5, 'cache', 'pages/monitor/cache/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:cache:list', 'Leaf', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-06-30 14:02:08', '缓存监控菜单');\nINSERT INTO `sys_menu` VALUES (114, '缓存列表', 2, 'cacheList', 6, 'cacheList', 'pages/monitor/cacheList/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:cache:list', 'LogoOctocat', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-06-30 17:39:42', '缓存列表菜单');\nINSERT INTO `sys_menu` VALUES (116, '代码生成', 3, 'code', 2, 'code', 'pages/systools/code/index', '', 1, 0, '0', 'C', '0', '0', 'tool:gen:list', 'Mail', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-02-26 17:05:52', '代码生成菜单');\nINSERT INTO `sys_menu` VALUES (500, '操作日志', 108, 'operationLog', 1, 'operationLog', 'pages/system/log/operation/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:operlog:list', 'Notifications', 'admin', '2022-12-03 07:21:33', '', NULL, '操作日志菜单');\nINSERT INTO `sys_menu` VALUES (501, '登录日志', 108, 'loginLog', 2, 'loginLog', 'pages/system/log/login/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:loginlog:list', 'Planet', 'admin', '2022-12-03 07:21:33', '', NULL, '登录日志菜单');\nINSERT INTO `sys_menu` VALUES (1000, '用户查询', 100, NULL, 1, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:user:list', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1001, '用户新增', 100, NULL, 2, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:user:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1002, '用户修改', 100, NULL, 3, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:user:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1003, '用户删除', 100, NULL, 4, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:user:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1004, '用户导出', 100, NULL, 5, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:user:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-02-19 15:38:58', '');\nINSERT INTO `sys_menu` VALUES (1005, '用户导入', 100, NULL, 6, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:user:import', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1006, '重置密码', 100, NULL, 7, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:user:resetPwd', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1007, '角色查询', 101, NULL, 1, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:role:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1008, '角色新增', 101, NULL, 2, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:role:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1009, '角色修改', 101, NULL, 3, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:role:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1010, '角色删除', 101, NULL, 4, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:role:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1011, '角色导出', 101, NULL, 5, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:role:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1012, '菜单查询', 102, NULL, 1, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:menu:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1013, '菜单新增', 102, NULL, 2, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:menu:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1014, '菜单修改', 102, NULL, 3, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:menu:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1015, '菜单删除', 102, NULL, 4, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:menu:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1016, '部门查询', 103, NULL, 1, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:dept:list', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1017, '部门新增', 103, NULL, 2, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:dept:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1018, '部门修改', 103, NULL, 3, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:dept:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1019, '部门删除', 103, NULL, 4, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:dept:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1020, '岗位查询', 104, NULL, 1, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:post:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1021, '岗位新增', 104, NULL, 2, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:post:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1022, '岗位修改', 104, NULL, 3, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:post:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1023, '岗位删除', 104, NULL, 4, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:post:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1024, '岗位导出', 104, NULL, 5, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:post:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1025, '字典查询', 105, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:dict:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1026, '字典新增', 105, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:dict:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1027, '字典修改', 105, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:dict:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1028, '字典删除', 105, NULL, 4, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:dict:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1029, '字典导出', 105, NULL, 5, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:dict:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1030, '参数查询', 106, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:config:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1031, '参数新增', 106, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:config:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1032, '参数修改', 106, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:config:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1033, '参数删除', 106, NULL, 4, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:config:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1034, '参数导出', 106, NULL, 5, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:config:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1035, '公告查询', 107, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:notice:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1036, '公告新增', 107, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:notice:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1037, '公告修改', 107, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:notice:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1038, '公告删除', 107, NULL, 4, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:notice:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1039, '操作查询', 500, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:operlog:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1040, '操作删除', 500, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:operlog:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1041, '日志导出', 500, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:operlog:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1042, '登录查询', 501, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:loginlog:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1043, '登录删除', 501, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:loginlog:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1044, '日志导出', 501, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:loginlog:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1045, '账户解锁', 501, NULL, 4, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:loginlog:unlock', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1046, '在线查询', 109, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:online:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1047, '批量强退', 109, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:online:batchLogout', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1048, '单条强退', 109, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:online:forceLogout', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1049, '任务查询', 110, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:job:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1050, '任务新增', 110, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:job:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1051, '任务修改', 110, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:job:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1052, '任务删除', 110, NULL, 4, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:job:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1053, '状态修改', 110, NULL, 5, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:job:changeStatus', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1054, '任务导出', 110, NULL, 6, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:job:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1055, '生成查询', 116, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'tool:gen:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1056, '生成修改', 116, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'tool:gen:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1057, '生成删除', 116, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'tool:gen:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1058, '导入代码', 116, NULL, 4, '#', '', '', 1, 0, '0', 'F', '1', '0', 'tool:gen:import', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1059, '预览代码', 116, NULL, 5, '#', '', '', 1, 0, '0', 'F', '1', '0', 'tool:gen:preview', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (1060, '生成代码', 116, NULL, 6, '#', '', '', 1, 0, '0', 'F', '1', '0', 'tool:gen:code', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (2031, '订单管理', 0, 'order', 4, 'order', '', NULL, 1, 0, '0', 'M', '0', '0', '', 'BarChartSharp', 'Ginyi', '2023-02-28 21:19:38', 'Ginyi', '2023-02-28 21:20:28', '');\nINSERT INTO `sys_menu` VALUES (2032, '订单列表', 2031, 'orderList', 1, 'orderList', 'pages/order/index', NULL, 1, 0, '0', 'C', '0', '0', 'order:list', 'Card', 'Ginyi', '2023-02-28 21:22:53', 'Ginyi', '2023-02-28 21:28:47', '');\nINSERT INTO `sys_menu` VALUES (2033, '商品管理', 0, 'product', 5, 'product', NULL, NULL, 1, 0, '0', 'M', '0', '0', NULL, 'Cart', 'Ginyi', '2023-02-28 21:35:33', 'Ginyi', '2023-05-28 20:39:38', '');\nINSERT INTO `sys_menu` VALUES (2034, '商品列表', 2033, 'productList', 1, 'productList', 'pages/product/index', NULL, 1, 0, '0', 'C', '0', '0', 'product:list', 'Documents', 'Ginyi', '2023-02-28 21:37:50', 'Ginyi', '2023-05-28 18:01:16', '');\nINSERT INTO `sys_menu` VALUES (2035, '查看缓存详情', 114, NULL, 0, '', NULL, NULL, 1, 0, NULL, 'F', '0', '0', 'monitor:cache:details', '#', 'Ginyi', '2023-06-30 17:35:50', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (2036, '删除缓存', 114, NULL, 2, '', NULL, NULL, 1, 0, NULL, 'F', '0', '0', 'monitor:cache:remove', '#', 'Ginyi', '2023-06-30 17:37:58', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (2037, '已阅公告', 107, NULL, 5, '', NULL, NULL, 1, 0, NULL, 'F', '0', '0', 'system:notice:haveRead', '#', 'Ginyi', '2023-07-09 00:31:01', '', NULL, '');\nINSERT INTO `sys_menu` VALUES (2038, '公告查询（管理员）', 107, NULL, 0, '', NULL, NULL, 1, 0, NULL, 'F', '0', '0', 'system:notice:query:admin', '#', 'Ginyi', '2023-07-09 00:32:29', '', NULL, '');\n\n-- ----------------------------\n-- Table structure for sys_notice\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_notice`;\nCREATE TABLE `sys_notice`  (\n  `notice_id` int NOT NULL AUTO_INCREMENT COMMENT '公告ID',\n  `title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '公告标题',\n  `type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '公告类型（0通知 1公告）',\n  `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '公告内容',\n  `user_ids` json NOT NULL COMMENT '通知的用户',\n  `user_read_ids` json NULL COMMENT '已阅的用户',\n  `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0' COMMENT '公告状态（0正常 1关闭）',\n  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',\n  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注',\n  PRIMARY KEY (`notice_id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '通知公告表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_notice\n-- ----------------------------\nINSERT INTO `sys_notice` VALUES (1, '温馨提醒：2018-07-01 若依新版本发布啦', '2', '新版本内容', '[1, 2]', '[1, 2]', '0', 'admin', '2022-12-03 07:21:33', '', NULL, NULL);\nINSERT INTO `sys_notice` VALUES (2, '维护通知：2018-07-01 若依系统凌晨维护', '1', '维护内容', '[1, 2]', '[2]', '0', 'admin', '2022-12-03 07:21:33', '', NULL, NULL);\nINSERT INTO `sys_notice` VALUES (14, '测试标题', '1', '测试标题内容', '[1]', NULL, '0', 'admin', '2023-07-02 18:42:14', '', NULL, NULL);\n\n-- ----------------------------\n-- Table structure for sys_post\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_post`;\nCREATE TABLE `sys_post`  (\n  `post_id` bigint NOT NULL AUTO_INCREMENT COMMENT '岗位ID',\n  `post_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '岗位编码',\n  `post_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '岗位名称',\n  `sort` int NOT NULL COMMENT '显示顺序',\n  `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '状态（0正常 1停用）',\n  `deleted` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '删除标志（0代表存在 2代表删除）',\n  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注',\n  PRIMARY KEY (`post_id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '岗位信息表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_post\n-- ----------------------------\nINSERT INTO `sys_post` VALUES (1, 'ceo', '董事长', 1, '0', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_post` VALUES (2, 'se', '项目经理', 2, '0', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_post` VALUES (3, 'hr', '人力资源', 3, '0', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_post` VALUES (4, 'user', '普通员工', 4, '0', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '');\nINSERT INTO `sys_post` VALUES (9, 'test', '测试测试', 5, '0', '0', 'ginyi', '2022-12-31 18:23:34', 'Ginyi', '2023-06-28 22:04:13', NULL);\n\n-- ----------------------------\n-- Table structure for sys_role\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_role`;\nCREATE TABLE `sys_role`  (\n  `role_id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色ID',\n  `role_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色名称',\n  `role_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色权限字符串',\n  `sort` int NOT NULL COMMENT '显示顺序',\n  `data_scope` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '1' COMMENT '数据范围（1：全部数据权限 2：自定数据权限 3：本部门数据权限 4：本部门及以下数据权限）',\n  `menu_check_strictly` tinyint(1) NULL DEFAULT 1 COMMENT '菜单树选择项是否关联显示',\n  `dept_check_strictly` tinyint(1) NULL DEFAULT 1 COMMENT '部门树选择项是否关联显示',\n  `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色状态（0正常 1停用）',\n  `deleted` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '删除标志（0代表存在 2代表删除）',\n  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注',\n  PRIMARY KEY (`role_id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 113 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '角色信息表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_role\n-- ----------------------------\nINSERT INTO `sys_role` VALUES (1, '超级管理员', 'admin', 1, '1', 1, 1, '0', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '超级管理员');\nINSERT INTO `sys_role` VALUES (2, '系统管理员', 'system', 1, '2', 1, 1, '0', '0', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-07-09 00:40:25', '专门管理系统的');\nINSERT INTO `sys_role` VALUES (111, '订单管理员', 'order', 0, '1', 1, 1, '0', '0', 'Ginyi', '2023-02-26 16:05:39', 'Ginyi', '2023-02-28 21:33:53', '专门管理订单的');\nINSERT INTO `sys_role` VALUES (112, '商品管理员', 'conduct', 2, '1', 1, 1, '0', '0', 'Ginyi', '2023-02-26 16:06:28', 'Ginyi', '2023-05-28 20:39:27', '专门管理商品的');\n\n-- ----------------------------\n-- Table structure for sys_role_dept\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_role_dept`;\nCREATE TABLE `sys_role_dept`  (\n  `role_id` bigint NOT NULL COMMENT '角色ID',\n  `dept_id` bigint NOT NULL COMMENT '部门ID',\n  PRIMARY KEY (`role_id`, `dept_id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '角色和部门关联表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_role_dept\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-- Table structure for sys_role_menu\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_role_menu`;\nCREATE TABLE `sys_role_menu`  (\n  `role_id` bigint NOT NULL COMMENT '角色ID',\n  `menu_id` json NULL COMMENT '菜单ID',\n  PRIMARY KEY (`role_id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '角色和菜单关联表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_role_menu\n-- ----------------------------\nINSERT INTO `sys_role_menu` VALUES (2, '[1, 100, 101, 102, 103, 104, 105, 106, 107, 108, 500, 501, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 2, 109, 1046, 1047, 1048, 110, 1049, 1050, 1051, 1052, 1053, 1054, 111, 112, 113, 114, 3, 116, 1055, 1056, 1057, 1058, 1059, 1060, 2035, 2036, 2037, 2038]');\nINSERT INTO `sys_role_menu` VALUES (111, '[2031, 2032]');\nINSERT INTO `sys_role_menu` VALUES (112, '[2033, 2034]');\n\n-- ----------------------------\n-- Table structure for sys_user\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_user`;\nCREATE TABLE `sys_user`  (\n  `user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',\n  `dept_id` bigint NULL DEFAULT NULL COMMENT '部门ID',\n  `user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户账号',\n  `nick_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户昵称',\n  `user_type` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '00' COMMENT '用户类型（00系统用户）',\n  `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '用户邮箱',\n  `phone_number` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '手机号码',\n  `sex` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '用户性别（0男 1女 2未知）',\n  `avatar` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '头像地址',\n  `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '密码',\n  `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '帐号状态（0正常 1停用）',\n  `deleted` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '删除标志（0代表存在 2代表删除）',\n  `login_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '最后登录IP',\n  `login_date` datetime NULL DEFAULT NULL COMMENT '最后登录时间',\n  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注',\n  PRIMARY KEY (`user_id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 148 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户信息表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_user\n-- ----------------------------\nINSERT INTO `sys_user` VALUES (1, 103, 'admin', '若依', '00', 'ry@163.com', '15888888888', '1', '', '$2a$10$0zqV6SsWmLkjBroBlzgnVO/mNRNTjbJrrCbUhRPOKZa9vyLnyJAR6', '0', '0', '192.168.0.101', '2023-07-09 01:06:17', 'admin', '2022-12-03 07:21:32', 'Ginyi', '2023-07-09 01:06:17', '管理员');\nINSERT INTO `sys_user` VALUES (2, 105, 'Ginyi', '(´･_･`)', '00', 'Ginyi@aliyun.com', '15666666666', '1', '', '$2a$10$0zqV6SsWmLkjBroBlzgnVO/mNRNTjbJrrCbUhRPOKZa9vyLnyJAR6', '0', '0', '192.168.0.103', '2023-09-16 15:14:49', 'admin', '2022-12-03 07:21:32', 'Ginyi', '2023-09-16 15:14:49', '测试员');\nINSERT INTO `sys_user` VALUES (133, 101, 'order', '订单管理员', '00', '', '13800138000', '0', '', '$2a$10$0zqV6SsWmLkjBroBlzgnVO/mNRNTjbJrrCbUhRPOKZa9vyLnyJAR6', '0', '0', '127.0.0.1', '2023-03-02 23:06:23', 'ginyi', '2022-12-23 07:40:55', 'Ginyi', '2023-03-02 23:06:23', NULL);\nINSERT INTO `sys_user` VALUES (134, 104, 'product', '商品管理员', '00', '', '13800138000', '0', '', '$2a$10$0zqV6SsWmLkjBroBlzgnVO/mNRNTjbJrrCbUhRPOKZa9vyLnyJAR6', '0', '0', '127.0.0.1', '2023-05-27 22:38:58', 'ginyi', '2022-12-23 07:41:02', 'Ginyi', '2023-05-28 17:04:05', NULL);\nINSERT INTO `sys_user` VALUES (138, 106, 'tony', '测试用户3', '00', '', '13800138000', '0', '', '', '0', '0', '', NULL, 'ginyi', '2022-12-23 07:41:28', 'admin', '2023-03-03 21:50:14', '没有初始化密码，不能登录');\nINSERT INTO `sys_user` VALUES (139, 109, 'lucy', '测试用户4', '00', '', '13800138000', '0', '', '', '1', '0', '', NULL, 'ginyi', '2022-12-23 13:31:22', 'Ginyi', '2023-07-09 13:20:26', '没有初始化密码，不能登录');\nINSERT INTO `sys_user` VALUES (140, 101, 'carry', '测试用户5', '00', '', '', '0', '', '', '0', '0', '', NULL, 'ginyi', '2022-12-23 21:35:13', 'Ginyi', '2023-05-28 20:35:40', '没有初始化密码，不能登录');\nINSERT INTO `sys_user` VALUES (145, 109, 'test6', '测试用户6', '00', '', '', '0', '', '', '0', '0', '', NULL, 'Ginyi', '2023-02-26 16:40:51', 'Ginyi', '2023-07-09 13:20:24', '没有初始化密码，不能登录');\n\n-- ----------------------------\n-- Table structure for sys_user_post\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_user_post`;\nCREATE TABLE `sys_user_post`  (\n  `user_id` bigint NOT NULL COMMENT '用户ID',\n  `post_id` json NOT NULL COMMENT '岗位ID',\n  PRIMARY KEY (`user_id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户与岗位关联表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_user_post\n-- ----------------------------\nINSERT INTO `sys_user_post` VALUES (1, '[1]');\nINSERT INTO `sys_user_post` VALUES (2, '[2]');\nINSERT INTO `sys_user_post` VALUES (133, '[4]');\nINSERT INTO `sys_user_post` VALUES (134, '[4]');\nINSERT INTO `sys_user_post` VALUES (138, '[2, 4]');\nINSERT INTO `sys_user_post` VALUES (139, '[1, 2]');\nINSERT INTO `sys_user_post` VALUES (140, '[2, 9]');\nINSERT INTO `sys_user_post` VALUES (145, '[9]');\n\n-- ----------------------------\n-- Table structure for sys_user_role\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_user_role`;\nCREATE TABLE `sys_user_role`  (\n  `user_id` bigint NOT NULL COMMENT '用户ID',\n  `role_id` json NOT NULL COMMENT '角色ID',\n  PRIMARY KEY (`user_id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户和角色关联表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_user_role\n-- ----------------------------\nINSERT INTO `sys_user_role` VALUES (1, '[1]');\nINSERT INTO `sys_user_role` VALUES (2, '[2]');\nINSERT INTO `sys_user_role` VALUES (133, '[111, 112]');\nINSERT INTO `sys_user_role` VALUES (134, '[112]');\nINSERT INTO `sys_user_role` VALUES (138, '[111]');\nINSERT INTO `sys_user_role` VALUES (139, '[112]');\nINSERT INTO `sys_user_role` VALUES (140, '[111]');\nINSERT INTO `sys_user_role` VALUES (145, '[112]');\n\nSET FOREIGN_KEY_CHECKS = 1;\n"
  },
  {
    "path": "ginyi-vue3/.editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nindent_size = 4\n"
  },
  {
    "path": "ginyi-vue3/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "ginyi-vue3/README.md",
    "content": "### 温馨提示\n\n1. `Ginyi-vue3`是一个`Vue3`的项目，如果你不了解`Vue3`，可以去看看其他框架。\n2. `Ginyi-vue3`全量使用`script-return`，如果你不喜欢它，可以去看看其他框架。\n\n\n\n### 项目地址\n\n演示地址：http://114.132.120.190:3800 （`仅PC端`）\n\n代码仓库：https://gitee.com/Ginyi/ginyi-spring-vue\n\n\n\n### 帮助文档\n\n- 没有~不想写文档...如果`Star`数量破`1k`，可以考虑一下！\n\n\n\n### 项目特色\n\n- 界面简洁、舒适！\n\n- 动态菜单，粒子化的权限控制！\n\n- 不使用代码规范插件，代码灵活不受约束，如需要可自行添加！\n\n- 丰富的题主配置，内置多款主题色可供选择，同时可切换亮色/暗色模式！\n\n- 使用最新技术栈`Vite4 + Vue3 + TypeScript + Pinia`，搭配优秀`Vue3`专用组件库`Naive UI`!\n\n- 二次封装常用组件：`CommonTable、CommonForm、CommonModal`强强联合，15分钟搞定一套CURD！\n\n- 提供多个自定义指令：`v-focus、v-draggable、v-permission`等，同时提供多个常用数据字典：`是否、正常禁用、性别`等，减少重复代码！\n\n\n\n### 前置知识\n\n**重点：**使用本项目时，你需要具备以下基础知识：\n\n1. `Vue3`语法基础，可以不熟悉`<script setup>`语法糖，但需要掌握`setup-return`基础！\n2. `TypeScript`语法基础，需要掌握的包括基础数据类型，接口等以及`TSX`语法！\n3. `Pinia`语法基础，需要掌握如何创建`pinia`对象，如何管理组件的状态！\n4. `Naive UI`语法基础，需要掌握组件库的使用！\n\n\n\n### 项目环境\n\n**注意：**使用本项目时，为了你能愉快地进行开发，请尽可能使用以下环境进行开发：\n\n1. `Node v14.19.0`\n2. `yarn v1.22.19` or `npm v6.14.16`，本项目使用`yarn`作为包管理工具\n3. `IntelliJ IDEA 2021.3.1`，本项目使用`IDEA`作为开发工具\n\n\n\n### 项目运行\n\n```bash\n# 安装依赖\nyarn install 或者 npm install\n\n# 启动项目\nyarn dev 或者 npm run dev\n\n# 打包项目\nyarn build 或者 npm run build\n```\n\n\n\n### 目录结构\n\n```bash\nginyi-vue3 \n|\n├─ src  # 源文件夹\n│  ├─ api           # 网络请求\n│  ├─ assets        # 静态资源文件，参与打包\n│  ├─ components    # 全局组件\n│  ├─ config        # 项目配置\n│  ├─ dictionary    # 全局字典\n│  ├─ directives    # 全局指令\n│  ├─ enums         # 全局枚举\n│  ├─ hooks         # 钩子函数（包含工具类）\n│  ├─ interface     # 接口类型\n│  ├─ layout        # 布局\n│  ├─ plugins       # 插件\n│  ├─ router        # 路由\n│  ├─ store         # 状态管理\n│  ├─ style         # 全局样式\n│  └─ views         # 视图文件\n|\n└─ public  # 静态资源文件，不参与打包\n```\n\n"
  },
  {
    "path": "ginyi-vue3/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\"/>\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/favicon.ico\"/>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>\n    <style>\n        html,\n        body,\n        #app {\n            height: 100%;\n            margin: 0px;\n            padding: 0px;\n        }\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<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<script type=\"module\" src=\"/src/main.ts\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "ginyi-vue3/package.json",
    "content": "{\n  \"name\": \"ginyi-vue3\",\n  \"private\": true,\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"web-types\": \"web-types.json\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@types/node\": \"^18.11.18\",\n    \"@vicons/ionicons5\": \"^0.12.0\",\n    \"@vitejs/plugin-vue-jsx\": \"^3.0.0\",\n    \"@vueuse/core\": \"^9.10.0\",\n    \"axios\": \"^1.2.2\",\n    \"less\": \"^4.1.3\",\n    \"less-loader\": \"^11.1.0\",\n    \"mitt\": \"^3.0.0\",\n    \"monaco-editor\": \"^0.36.0\",\n    \"naive-ui\": \"^2.34.3\",\n    \"pinia\": \"^2.0.28\",\n    \"vue\": \"^3.2.45\",\n    \"vue-router\": \"^4.1.6\",\n    \"vuedraggable\": \"^4.1.0\"\n  },\n  \"devDependencies\": {\n    \"@vitejs/plugin-vue\": \"^4.0.0\",\n    \"typescript\": \"^4.9.3\",\n    \"vite\": \"^4.0.0\",\n    \"vue-tsc\": \"^1.0.11\"\n  }\n}\n"
  },
  {
    "path": "ginyi-vue3/src/App.vue",
    "content": "<template>\n    <n-config-provider :theme=\"getTheme\"\n                       :theme-overrides=\"getThemeOverrides\"\n                       :locale=\"zhCN\"\n                       :date-locale=\"dateZhCN\">\n        <n-loading-bar-provider>\n            <n-dialog-provider>\n                <n-notification-provider>\n                    <n-message-provider>\n                        <router-view/>\n                    </n-message-provider>\n                </n-notification-provider>\n            </n-dialog-provider>\n        </n-loading-bar-provider>\n    </n-config-provider>\n</template>\n\n<script setup lang=\"ts\">\nimport {useSystemStore} from \"@/store/modules/useSystemStore\";\nimport {useThrottle} from \"@/hooks/useDebthro\";\nimport {storeToRefs} from \"pinia\";\nimport {computed} from \"vue\";\nimport {useLighten} from \"@/hooks/useColor\";\nimport { zhCN, dateZhCN } from \"naive-ui\";\n\nconst {getTheme, themeColor} = storeToRefs(useSystemStore())\nconst getThemeOverrides = computed(() => {\n    return {\n        common: {\n            primaryColor: themeColor?.value,\n            primaryColorHover: useLighten(themeColor?.value as string, 6),\n            primaryColorPressed: useLighten(themeColor?.value as string, 6),\n        },\n        LoadingBar: {\n            colorLoading: themeColor?.value as string,\n        },\n    };\n});\n\n// 节流，可以适当增加时间\nwindow.addEventListener(\"resize\", useThrottle(() => {\n    useSystemStore().setClientWidth(document.body.clientWidth)\n    useSystemStore().setClientHeight(document.body.clientHeight)\n}, 0))\n\n\n</script>\n"
  },
  {
    "path": "ginyi-vue3/src/api/controller/deptController.ts",
    "content": "import useRequest from \"@/api/useRequest\";\nimport {IPage} from \"@/interface/modules/system\";\n\nexport class deptController {\n    /**\n     * 获取部门列表\n     * @param data\n     * @param pagination\n     */\n    static list(data: any, pagination?: IPage): Promise<any> {\n        return useRequest({\n            url: \"/api/dept/list\",\n            method: \"post\",\n            params: pagination,\n            data\n        })\n    }\n\n    /**\n     * 获取部门详情\n     * @param deptId\n     */\n    static getDeptDetailsById(deptId: number | string): Promise<any> {\n        return useRequest({\n            url: `/api/dept/getDeptByDeptId/${deptId}`,\n            method: \"get\"\n        })\n    }\n\n    /**\n     * 新增部门\n     * @param data\n     */\n    static add(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/dept/add\",\n            method: \"post\",\n            data\n        })\n    }\n\n    /**\n     * 更新部门\n     * @param data\n     */\n    static edit(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/dept/update\",\n            method: \"post\",\n            data\n        })\n    }\n\n    /**\n     * 删除部门\n     * @param deptId\n     */\n    static deleteById(deptId: number | string): Promise<any> {\n        return useRequest({\n            url: `/api/dept/delete/${deptId}`,\n            method: \"post\"\n        })\n    }\n\n    /**\n     * 批量删除部门\n     * @param data\n     */\n    static deleteByIds(data: Array<number | string>): Promise<any> {\n        return useRequest({\n            url: \"/api/dept/delete\",\n            method: \"post\",\n            data\n        })\n    }\n\n    /**\n     * 更新部门状态\n     * @param data\n     */\n    static updateStatus(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/dept/updateStatus\",\n            method: \"post\",\n            data\n        })\n    }\n\n}"
  },
  {
    "path": "ginyi-vue3/src/api/controller/logController.ts",
    "content": "import useRequest from \"@/api/useRequest\";\nimport {IPage} from \"@/interface/modules/system\";\n\nexport class logController {\n    /**\n     * 获取操作日志\n     * @param data\n     * @param pagination\n     */\n    static getOperationLogList(data: any, pagination?: IPage): Promise<any> {\n        return useRequest({\n            url: \"/api/log/getOperationLogList\",\n            method: \"get\",\n            params: pagination,\n        })\n    }\n\n    /**\n     * 获取登录日志\n     * @param data\n     * @param pagination\n     */\n    static getLoginLogList(data: any, pagination?: IPage): Promise<any> {\n        return useRequest({\n            url: \"/api/log/getLoginLogList\",\n            method: \"get\",\n            params: pagination,\n        })\n    }\n\n}"
  },
  {
    "path": "ginyi-vue3/src/api/controller/menuController.ts",
    "content": "import useRequest from \"@/api/useRequest\";\nimport {IPage} from \"@/interface/modules/system\";\n\nexport class menuController {\n    /**\n     * 获取路由列表\n     */\n    static getRouterList(): Promise<any> {\n        return useRequest({\n            url: \"/api/menu/getRouterList\",\n            method: \"get\",\n        })\n    }\n\n    /**\n     * 获取菜单列表\n     * @param data\n     * @param pagination\n     */\n    static list(data: any, pagination?: IPage): Promise<any> {\n        return useRequest({\n            url: \"/api/menu/list\",\n            method: \"post\",\n            params: pagination,\n            data\n        })\n    }\n\n    /**\n     * 获取部门详情\n     * @param menuId\n     */\n    static getMenuDetailsById(menuId: number | string): Promise<any> {\n        return useRequest({\n            url: `/api/menu/getMenuByMenuId/${menuId}`,\n            method: \"get\"\n        })\n    }\n\n    /**\n     * 新增部门\n     * @param data\n     */\n    static add(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/menu/add\",\n            method: \"post\",\n            data\n        })\n    }\n\n    /**\n     * 更新部门\n     * @param data\n     */\n    static edit(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/menu/update\",\n            method: \"post\",\n            data\n        })\n    }\n\n    /**\n     * 删除部门\n     * @param menuId\n     */\n    static deleteById(menuId: number | string): Promise<any> {\n        return useRequest({\n            url: `/api/menu/delete/${menuId}`,\n            method: \"post\"\n        })\n    }\n\n    /**\n     * 批量删除菜单\n     * @param data\n     */\n    static deleteByIds(data: Array<number | string>): Promise<any> {\n        return useRequest({\n            url: \"/api/menu/delete\",\n            method: \"post\",\n            data\n        })\n    }\n\n    /**\n     * 更新菜单状态\n     * @param data\n     */\n    static updateStatus(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/menu/updateStatus\",\n            method: \"post\",\n            data\n        })\n    }\n}"
  },
  {
    "path": "ginyi-vue3/src/api/controller/monitorController.ts",
    "content": "import useRequest from \"@/api/useRequest\";\n\nexport class monitorController {\n    /**\n     * 获取服务器信息\n     */\n    static getServerInfo(): Promise<any> {\n        return useRequest({\n            url: \"/api/monitor/getServerInfo\",\n            method: \"get\",\n        })\n    }\n\n    /**\n     * 获取缓存列表\n     */\n    static getCacheList(): Promise<any> {\n        return useRequest({\n            url: \"/api/monitor/getCacheList\",\n            method: \"get\",\n        })\n    }\n\n    /**\n     * 获取缓存数据\n     */\n    static getCacheValue(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/monitor/getCacheValue\",\n            method: \"post\",\n            data: data\n        })\n    }\n\n    /**\n     * 删除缓存\n     */\n    static removeCache(key: string): Promise<any> {\n        return useRequest({\n            url: `/api/monitor/removeCache/${key}`,\n            method: \"post\",\n        })\n    }\n\n}"
  },
  {
    "path": "ginyi-vue3/src/api/controller/onlineController.ts",
    "content": "import useRequest from \"@/api/useRequest\";\nimport {IPage} from \"@/interface/modules/system\";\n\nexport class onlineController {\n    /**\n     * 获取在线用户\n     * @param data\n     * @param pagination\n     */\n    static getOnlineUserList(data: any, pagination?: IPage): Promise<any> {\n        return useRequest({\n            url: \"/api/online/getOnlineUserList\",\n            method: \"get\",\n            params: pagination,\n        })\n    }\n\n    /**\n     * 强制退出\n     * @param token\n     */\n    static removeUser(token: any): Promise<any> {\n        return useRequest({\n            url: `/api/online/removeUser/${token}`,\n            method: \"post\",\n        })\n    }\n\n    /**\n     * 批量强制退出\n     * @param data\n     */\n    static batchRemoveUser(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/online/removeUser\",\n            method: \"post\",\n            data\n        })\n    }\n\n}"
  },
  {
    "path": "ginyi-vue3/src/api/controller/postController.ts",
    "content": "import useRequest from \"@/api/useRequest\";\nimport {IPage} from \"@/interface/modules/system\";\n\nexport class postController {\n    /**\n     * 获取岗位列表\n     * @param data\n     * @param pagination\n     */\n    static list(data: any, pagination?: IPage): Promise<any> {\n        return useRequest({\n            url: \"/api/post/list\",\n            method: \"post\",\n            params: pagination,\n            data\n        })\n    }\n\n    /**\n     * 获取岗位详情\n     * @param postId\n     */\n    static getPostDetailsById(postId: number | string): Promise<any> {\n        return useRequest({\n            url: `/api/post/getPostByRoleId/${postId}`,\n            method: \"get\"\n        })\n    }\n\n    /**\n     * 新增岗位\n     * @param data\n     * @param pagination\n     */\n    static add(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/post/add\",\n            method: \"post\",\n            data\n        })\n    }\n\n    /**\n     * 更新岗位\n     * @param data\n     */\n    static edit(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/post/update\",\n            method: \"post\",\n            data\n        })\n    }\n\n    /**\n     * 删除岗位\n     * @param userId\n     */\n    static deleteById(userId: number | string): Promise<any> {\n        return useRequest({\n            url: `/api/post/delete/${userId}`,\n            method: \"post\"\n        })\n    }\n\n    /**\n     * 批量删除岗位\n     * @param data\n     */\n    static deleteByIds(data: Array<number | string>): Promise<any> {\n        return useRequest({\n            url: \"/api/post/delete\",\n            method: \"post\",\n            data\n        })\n    }\n\n    /**\n     * 更新岗位状态\n     * @param data\n     */\n    static updateStatus(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/post/updateStatus\",\n            method: \"post\",\n            data\n        })\n    }\n\n}"
  },
  {
    "path": "ginyi-vue3/src/api/controller/roleController.ts",
    "content": "import useRequest from \"@/api/useRequest\";\nimport {IPage} from \"@/interface/modules/system\";\n\nexport class roleController {\n    /**\n     * 获取角色列表\n     * @param data\n     * @param pagination\n     */\n    static list(data: any, pagination?: IPage): Promise<any> {\n        return useRequest({\n            url: \"/api/role/list\",\n            method: \"post\",\n            params: pagination,\n            data\n        })\n    }\n\n    /**\n     * 获取角色详情\n     * @param roleId\n     */\n    static getRoleDetailsById(roleId: number | string): Promise<any> {\n        return useRequest({\n            url: `/api/role/getRoleByRoleId/${roleId}`,\n            method: \"get\"\n        })\n    }\n\n    /**\n     * 新增角色\n     * @param data\n     * @param pagination\n     */\n    static add(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/role/add\",\n            method: \"post\",\n            data\n        })\n    }\n\n    /**\n     * 更新角色\n     * @param data\n     */\n    static edit(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/role/update\",\n            method: \"post\",\n            data\n        })\n    }\n\n    /**\n     * 删除角色\n     * @param userId\n     */\n    static deleteById(userId: number | string): Promise<any> {\n        return useRequest({\n            url: `/api/role/delete/${userId}`,\n            method: \"post\"\n        })\n    }\n\n    /**\n     * 批量删除角色\n     * @param data\n     */\n    static deleteByIds(data: Array<number | string>): Promise<any> {\n        return useRequest({\n            url: \"/api/role/delete\",\n            method: \"post\",\n            data\n        })\n    }\n\n    /**\n     * 更新角色状态\n     * @param data\n     */\n    static updateStatus(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/role/updateStatus\",\n            method: \"post\",\n            data\n        })\n    }\n\n}"
  },
  {
    "path": "ginyi-vue3/src/api/controller/userController.ts",
    "content": "import useRequest from \"@/api/useRequest\";\nimport {IPage} from \"@/interface/modules/system\";\n\nexport class userController {\n    /**\n     * 用户登录\n     * @param data\n     */\n    static login(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/user/login\",\n            method: \"post\",\n            data\n        })\n    }\n\n    /**\n     * 退出登录\n     * @param data\n     */\n    static logout(): Promise<any> {\n        return useRequest({\n            url: \"/api/user/logout\",\n            method: \"post\",\n        })\n    }\n\n    /**\n     * 获取验证码\n     */\n    static captcha(): Promise<any> {\n        return useRequest({\n            url: \"/api/verify/captcha\",\n            method: \"get\"\n        })\n    }\n\n    /**\n     * 用户列表\n     */\n    static getUserList(data: any, pagination?: IPage): Promise<any> {\n        return useRequest({\n            url: \"/api/user/list\",\n            method: \"post\",\n            params: pagination,\n            data\n        })\n    }\n\n    /**\n     * 获取用户详情\n     * @param userId\n     */\n    static getUserById(userId: number | string): Promise<any> {\n        return useRequest({\n            url: `/api/user/getUserByUserId/${userId}`,\n            method: \"get\"\n        })\n    }\n\n\n    /**\n     * 新增用户\n     * @param data\n     */\n    static add(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/user/add\",\n            method: \"post\",\n            data\n        })\n    }\n\n\n    /**\n     * 更新用户\n     * @param data\n     */\n    static edit(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/user/update\",\n            method: \"post\",\n            data\n        })\n    }\n\n    /**\n     * 更新用户状态\n     * @param data\n     */\n    static updateStatus(data: any): Promise<any> {\n        return useRequest({\n            url: \"/api/user/updateStatus\",\n            method: \"post\",\n            data\n        })\n    }\n\n    /**\n     * 删除用户\n     * @param userId\n     */\n    static deleteById(userId: number | string): Promise<any> {\n        return useRequest({\n            url: `/api/user/delete/${userId}`,\n            method: \"post\"\n        })\n    }\n\n    /**\n     * 批量删除用户\n     * @param data\n     */\n    static deleteByIds(data: Array<number | string>): Promise<any> {\n        return useRequest({\n            url: \"/api/user/delete\",\n            method: \"post\",\n            data\n        })\n    }\n\n}"
  },
  {
    "path": "ginyi-vue3/src/api/index.ts",
    "content": "import {userController} from \"@/api/controller/userController\";\nimport {menuController} from \"@/api/controller/menuController\";\nimport {deptController} from \"@/api/controller/deptController\";\nimport {postController} from \"@/api/controller/postController\";\nimport {roleController} from \"@/api/controller/roleController\";\nimport {logController} from \"@/api/controller/logController\";\nimport {onlineController} from \"@/api/controller/onlineController\";\nimport {monitorController} from \"@/api/controller/monitorController\";\n\nexport {\n    userController,\n    menuController,\n    deptController,\n    postController,\n    roleController,\n    logController,\n    onlineController,\n    monitorController\n}"
  },
  {
    "path": "ginyi-vue3/src/api/useRequest.tsx",
    "content": "import axios, {AxiosResponse, InternalAxiosRequestConfig} from \"axios\";\nimport {useUserStore} from \"@/store/modules/useUserStore\";\nimport {useFormatTime} from \"@/hooks/useFormat\";\nimport {storeToRefs} from \"pinia\";\nimport {store} from \"@/store\";\nimport {setting} from \"@/config/setting\";\nimport {useCommonRouter} from \"@/router\";\nimport {storage} from \"@/hooks/useStorage\";\nimport {useSystemStore} from \"@/store/modules/useSystemStore\";\n\nconst {devBaseURL, prodBaseURL} = setting\n\n/**\n * 创建axios示例\n */\nconst service = axios.create({\n    baseURL: import.meta.env.DEV ? devBaseURL : prodBaseURL,\n    timeout: 5000,\n});\n\n/**\n * 请求拦截\n */\nservice.interceptors.request.use((config: InternalAxiosRequestConfig<any>) => {\n    const {tokenKey, authorization} = storeToRefs(useUserStore(store));\n    if (authorization && config && config.headers) {\n        typeof config.headers.set === 'function' && config.headers.set(tokenKey?.value, authorization.value)\n    }\n    return config\n}, (error: any) => {\n    window.$notification.error({\n        title: \"网络请求错误\",\n        description: `From ${import.meta.env.DEV ? devBaseURL : prodBaseURL}`,\n        meta: useFormatTime(new Date().valueOf()),\n        content: () => (\n            <span>{typeof error.message === \"string\" ? error.message : JSON.stringify(error.message)}</span>\n        ),\n        duration: 5000,\n        keepAliveOnHover: true\n    })\n})\n\n/**\n * 响应拦截\n */\nservice.interceptors.response.use(\n    (response: AxiosResponse<any, any>) => {\n        const res = response.data;\n        if (res.code !== 200) {\n            switch (res.code) {\n                case 5005:\n                    useUserStore(store).$reset()\n                    useSystemStore(store).removeAllTabs()\n                    useSystemStore(store).resetBreadMenuList()\n                    storage.clear()\n                    useCommonRouter(\"login\")\n                    window.$message.warning(res.msg)\n                    break;\n                default:\n                    window.$notification.error({\n                        title: res.msg,\n                        description: `From ${import.meta.env.DEV ? devBaseURL : prodBaseURL}`,\n                        meta: useFormatTime(new Date().valueOf()),\n                        content: () => (\n                            <span>{typeof res.data === \"string\" ? res.data : JSON.stringify(res.data)}</span>\n                        ),\n                        duration: 5000,\n                        keepAliveOnHover: true\n                    })\n            }\n            return Promise.reject(res);\n        } else {\n            return res;\n        }\n    },\n    (error: any) => {\n        /* 响应拦截失败的情况 */\n        window.$notification.error({\n            title: \"请求响应错误\",\n            description: `From ${import.meta.env.DEV ? devBaseURL : prodBaseURL}`,\n            meta: useFormatTime(new Date().valueOf()),\n            content: () => (\n                <span>{typeof error.message === \"string\" ? error.message : JSON.stringify(error.message)}</span>\n            ),\n            duration: 5000,\n            keepAliveOnHover: true\n        })\n        return Promise.reject(error);\n    }\n);\n\nexport default service"
  },
  {
    "path": "ginyi-vue3/src/components/commonForm/index.vue",
    "content": "<template>\n    <slot></slot>\n    <div style=\"display: flex; justify-content: flex-end\">\n        <n-space>\n            <n-button type=\"primary\" attr-type=\"button\" @click=\"onSubmit\">{{ submitButtonText }}</n-button>\n            <n-button attr-type=\"button\" @click=\"onReset\">{{ cancelButtonText }}</n-button>\n        </n-space>\n    </div>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, onMounted} from \"vue\";\nimport {definedProps} from \"@/components/commonForm/props\";\n\n\nexport default defineComponent({\n    name: \"CommonForm\",\n    emits: [\"onReset\", \"onSubmit\"],\n    props: {\n        ...definedProps\n    },\n    setup(props, context) {\n\n        const onSubmit = () => {\n            context.emit(\"onSubmit\")\n        }\n        const onReset = () => {\n            context.emit(\"onReset\")\n        }\n\n        onMounted(() => {\n\n        })\n        return {\n            onSubmit,\n            onReset\n        }\n    }\n})\n</script>\n\n<style scoped>\n\n</style>"
  },
  {
    "path": "ginyi-vue3/src/components/commonForm/props.ts",
    "content": "export const definedProps = {\n    /**\n     * 确认按钮的文本内容\n     */\n    submitButtonText: {\n        type: String,\n        default: \"提交\"\n    },\n    /**\n     * 取消按钮的文本内容\n     */\n    cancelButtonText: {\n        type: String,\n        default: \"重置\"\n    },\n}"
  },
  {
    "path": "ginyi-vue3/src/components/commonForm/useCommonForm.ts",
    "content": "import {ref} from \"vue\";\n\nenum actionEnum {\n    ADD = \"新增\",\n    EDIT = \"编辑\",\n    QUERY = \"查询\"\n}\n\nexport const useCommonForm = (formDefaultValue: any = {},\n                              addApi?: Function,\n                              editApi?: Function,) => {\n\n    const formRef = ref(undefined)\n    const formActionType = ref<string | undefined>(undefined)\n    const formValue = ref<any>({...formDefaultValue})\n\n    /**\n     * 新增\n     */\n    const onAdd = () => {\n        formActionType.value = actionEnum.ADD\n        formValue.value = {...formDefaultValue}\n    }\n\n    /**\n     * 编辑\n     * @param row\n     */\n    const onEdit = (row: any) => {\n        formActionType.value = actionEnum.EDIT\n        formValue.value = {...row}\n    }\n\n    /**\n     * 查询\n     */\n    const onQuery = () => {\n        return new Promise((resolve, reject) => {\n            formActionType.value = actionEnum.QUERY\n            resolve(formValue.value)\n        })\n    }\n\n\n    /**\n     * 保存\n     * 返回 Promise，解决函数执行完后需要续写逻辑的需求\n     */\n    const onSubmit = () => {\n        return new Promise((resolve, reject) => {\n            // @ts-ignore\n            formRef.value?.validate(err => {\n                if (!err) {\n\n                } else {\n\n                }\n            })\n        })\n    }\n\n    /**\n     * 重置\n     * 返回 Promise，解决函数执行完后需要续写逻辑的需求\n     */\n    const onReset = () => {\n        return new Promise((resolve, reject) => {\n            const formDefaultKeys = Object.keys(formDefaultValue)\n            const formKeys = Object.keys(formValue.value)\n            formKeys.map(key => {\n                if (formDefaultKeys.includes(key)) {\n                    formValue.value[key] = formDefaultValue[key]\n                } else {\n                    formValue.value[key] = null\n                }\n            })\n            resolve(formValue.value)\n        })\n    }\n\n\n    return {\n        onAdd,\n        onEdit,\n        onQuery,\n        onSubmit,\n        onReset,\n        formValue,\n        formRef\n    }\n}\n"
  },
  {
    "path": "ginyi-vue3/src/components/commonModal/index.vue",
    "content": "<template>\n    <n-modal\n        v-model:show=\"showModal\"\n        :preset=\"preset\"\n        :title=\"title\"\n        positiveText=\"提交\"\n        negativeText=\"取消\"\n        @positive-click=\"onSubmit\"\n        @negative-click=\"showModal = false\">\n        <div style=\"padding-top: 15px\">\n            <slot></slot>\n        </div>\n    </n-modal>\n</template>\n\n<script lang=\"ts\">\nimport {computed, defineComponent} from \"vue\";\nimport {definedProps} from \"@/components/commonModal/props\";\n\nexport default defineComponent({\n    name: \"CommonModal\",\n    props: {\n        ...definedProps\n    },\n    emits: [\"onSubmit\", \"update:show\"],\n    setup(props, context) {\n\n        const onSubmit = () => {\n            context.emit(\"onSubmit\")\n        }\n\n        const showModal = computed({\n            get() {\n                return props.show\n            },\n            set(value) {\n                context.emit(\"update:show\", value)\n            }\n        })\n        return {\n            onSubmit,\n            showModal\n        }\n    }\n})\n</script>\n\n<style scoped>\n\n</style>"
  },
  {
    "path": "ginyi-vue3/src/components/commonModal/props.ts",
    "content": "export const definedProps = {\n    /**\n     * 模型标题\n     */\n    title: {\n        type: String,\n        default: \"标题\"\n    },\n    /**\n     * 模型预设\n     */\n    preset: {\n        type: String,\n        default: \"dialog\"\n    },\n    /**\n     * 确定按钮文本内容\n     */\n    positiveText: {\n        type: String,\n        default: \"确认\"\n    },\n    /**\n     * 取消按钮文本内容\n     */\n    negativeText: {\n        type: String,\n        default: \"取消\"\n    },\n    show: {\n        type: Boolean,\n        default: false\n    }\n}"
  },
  {
    "path": "ginyi-vue3/src/components/commonModal/useCommonModal.ts",
    "content": "import {computed, ref} from \"vue\";\n\nenum actionEnum {\n    ADD = \"新增\",\n    EDIT = \"编辑\",\n}\n\nexport const useCommonModal = (name: string,\n                               formDefaultValue: any = {},\n                               addApi: Function,\n                               editApi: Function,\n                               deleteApi?: Function,\n                               batchDeleteApi?: Function,\n                               getDetailsById?: Function) => {\n\n    const modalShow = ref<boolean>(false)\n    const modalLoading = ref<boolean>(false)\n    const modalTitle = ref<string | undefined>(undefined)\n    const modalFormRef = ref(undefined)\n    const modalForm = ref<any>({...formDefaultValue})\n\n    /**\n     * 新增\n     */\n    const onAdd = () => {\n        modalShow.value = true\n        modalLoading.value = false\n        modalTitle.value = actionEnum.ADD\n        modalForm.value = {...formDefaultValue}\n    }\n\n    /**\n     * 编辑\n     * @param row\n     * @param id\n     */\n    const onEdit = (row: any, id: number | string) => {\n        modalShow.value = true\n        modalLoading.value = false\n        modalTitle.value = actionEnum.EDIT\n        if(getDetailsById && id){\n            getDetailsById(id).then((res: any) => {\n                modalForm.value = {...res.data}\n            })\n        }else {\n            modalForm.value = {...row}\n        }\n    }\n\n\n    /**\n     * 保存\n     * 返回 Promise，解决函数执行完后需要续写逻辑的需求\n     */\n    const onSubmit = (): Promise<any> => {\n        return new Promise((resolve, reject) => {\n            // @ts-ignore\n            modalFormRef.value?.validate(err => {\n                if (!err) {\n                    if (modalTitle.value === actionEnum.ADD) {\n                        addApi(modalForm.value).then((res: any) => {\n                            window.$message.success(res.msg)\n                            modalLoading.value = true\n                            resolve(null)\n                        })\n                    }\n                    if (modalTitle.value === actionEnum.EDIT) {\n                        editApi(modalForm.value).then((res: any) => {\n                            window.$message.success(res.msg)\n                            modalLoading.value = true\n                            resolve(null)\n                        })\n                    }\n                } else {\n                    modalShow.value = true\n                }\n            })\n        })\n    }\n\n    /**\n     * 删除\n     * 返回 Promise，解决函数执行完后需要续写逻辑的需求\n     */\n    const onDeleteById = (id: number | string) => {\n        return new Promise((resolve, reject) => {\n            modalLoading.value = false\n            window.$dialog.error({\n                title: \"温馨提醒\",\n                content: \"删除操作不可逆，是否继续？\",\n                positiveText: \"确定\",\n                negativeText: \"取消\",\n                onPositiveClick: () => {\n                    deleteApi && deleteApi(id).then((res: any) => {\n                        window.$message.success(res.msg)\n                        modalLoading.value = true\n                        resolve(null)\n                    })\n                }\n            })\n        })\n    }\n\n    /**\n     * 批量删除\n     * 返回 Promise，解决函数执行完后需要续写逻辑的需求\n     */\n    const onDeleteByIds = (ids: Array<number | string>) => {\n        return new Promise((resolve, reject) => {\n            modalLoading.value = false\n            window.$dialog.error({\n                title: \"温馨提醒\",\n                content: \"删除操作不可逆，是否继续？\",\n                positiveText: \"确定\",\n                negativeText: \"取消\",\n                onPositiveClick: () => {\n                    batchDeleteApi && batchDeleteApi(ids).then((res: any) => {\n                        window.$message.success(res.msg)\n                        modalLoading.value = true\n                        resolve(null)\n                    })\n                }\n            })\n        })\n    }\n\n    return {\n        onAdd,\n        onEdit,\n        onSubmit,\n        onDeleteById,\n        onDeleteByIds,\n        modalForm,\n        modalFormRef,\n        modalShow,\n        modalLoading,\n        modalTitle: computed(() => {\n            return modalTitle.value + name\n        }),\n    }\n}\n"
  },
  {
    "path": "ginyi-vue3/src/components/commonTable/index.vue",
    "content": "<template>\n    <n-layout class=\"layout\" content-style=\"display:flex; flex-direction: column;\"\n              :style=\"{backgroundColor: darkTheme ? null : '#f5f6fa'}\">\n        <n-card title=\"查询条件\" :size=\"size\" v-if=\"hasQuerySlot('query')\" style=\"margin-bottom: 15px\">\n            <slot name=\"query\"></slot>\n        </n-card>\n        <n-layout-content>\n            <n-card style=\"height: 100%; box-sizing: border-box\">\n                <div style=\"display: flex; justify-content: space-between; margin-bottom: 5px\">\n                    <n-space>\n                        <n-button :type=\"buttonConfig.addButton.colorType\" :size=\"size\" @click=\"onEvent(tableActionEnums.ADD)\"\n                                  v-if=\"buttonConfig.addButton.show\">\n                            <template #icon>\n                                <n-icon :component=\"AddCircleOutline\"/>\n                            </template>\n                            {{ buttonConfig.addButton.title }}\n                        </n-button>\n                        <n-button :type=\"buttonConfig.batchDeleteButton.colorType\"\n                                  :size=\"size\" v-if=\"hasSelect\"\n                                  :disabled=\"!hasSelect || checkedRowList.length === 0\"\n                                  v-show=\"buttonConfig.batchDeleteButton.show\"\n                                  @click=\"onEvent(tableActionEnums.BATCH_DELETE)\">\n                            <template #icon>\n                                <n-icon :component=\"TrashBinOutline\"/>\n                            </template>\n                            {{ buttonConfig.batchDeleteButton.title }}\n                        </n-button>\n                    </n-space>\n                    <div style=\"display: flex; align-items: center;\"\n                         :style=\"{ width: !buttonConfig.addButton.show ? '100%' : null, justifyContent: !buttonConfig.addButton.show ? 'flex-end' : null}\">\n                        <n-radio-group :size=\"size\" v-model:value=\"tableSize\">\n                            <n-radio-button label=\"紧凑\" value=\"small\"/>\n                            <n-radio-button label=\"默认\" value=\"medium\"/>\n                            <n-radio-button label=\"宽松\" value=\"large\"/>\n                        </n-radio-group>\n                        <n-tooltip placement=\"top-start\" trigger=\"hover\" :show-arrow=\"false\">\n                            <template #trigger>\n                                <n-icon :component=\"Reload\" :size=\"20\" style=\"margin-left: 10px; cursor: pointer\" @click=\"reloadData\" />\n                            </template>\n                            刷新表格数据\n                        </n-tooltip>\n                        <n-space style=\"margin-left: 10px; cursor: pointer\">\n                            <n-popover :show-arrow=\"false\" placement=\"bottom-end\" trigger=\"click\">\n                                <template #trigger>\n                                    <n-icon style=\" display: flex; align-items: center\" :size=\"20\">\n                                        <SettingsOutline/>\n                                    </n-icon>\n                                </template>\n                                <div style=\"width: 350px; height: 400px;\">\n                                    <n-divider title-placement=\"center\">其他配置</n-divider>\n                                    <div style=\"display: flex; justify-content: space-between\">\n                                        <div>\n                                            <span style=\"margin-right: 10px\">序号</span>\n                                            <n-radio-group :size=\"size\" v-model:value=\"hasIndex\">\n                                                <n-radio-button label=\"开启\" :value=\"true\"/>\n                                                <n-radio-button label=\"隐藏\" :value=\"false\"/>\n                                            </n-radio-group>\n                                        </div>\n                                        <div>\n                                            <span style=\"margin-right: 10px\">可选列</span>\n                                            <n-radio-group :size=\"size\" v-model:value=\"hasSelect\">\n                                                <n-radio-button label=\"开启\" :value=\"true\"/>\n                                                <n-radio-button label=\"隐藏\" :value=\"false\"/>\n                                            </n-radio-group>\n                                        </div>\n                                    </div>\n                                    <n-divider title-placement=\"center\">列的排序</n-divider>\n                                    <n-scrollbar style=\"max-height: 240px\">\n                                        <div>\n                                            <n-checkbox-group v-model:value=\"checkedList\"\n                                                              @update:value=\"handleChecked\">\n                                                <Draggable v-model=\"tableConfigColumns\" group=\"people\"\n                                                           @start=\"drag = true\" @end=\"onDragEnd\"\n                                                           item-key=\"id\"\n                                                           animation=\"300\"\n                                                           chosen-class=\"chosenClass\">\n                                                    <template #item=\"{element}\">\n                                                        <div class=\"item\" :style=\"{borderColor: themeColor}\">\n                                                            <n-space item-style=\"display: flex;\" align=\"center\">\n                                                                <n-icon :component=\"Move\"></n-icon>\n                                                                <n-checkbox :value=\"element[labelField]\"\n                                                                            :label=\"[undefined, null, ''].includes(element[labelField])\n                                                            ? '可选列' : element[labelField]\"/>\n                                                            </n-space>\n                                                        </div>\n                                                    </template>\n                                                </Draggable>\n                                            </n-checkbox-group>\n                                        </div>\n                                    </n-scrollbar>\n                                </div>\n                            </n-popover>\n                        </n-space>\n                    </div>\n                </div>\n                <n-data-table :remote=\"true\"\n                              :columns=\"tableColumns\"\n                              :data=\"dataList\"\n                              :size=\"tableSize\"\n                              :pagination=\"pagination\"\n                              :row-key=\"rowKey\"\n                              :scroll-x=\"scrollX\"\n                              :loading=\"loading\"\n                              default-expand-all\n                              @update:page=\"handlePageChange\"\n                              @update:page-size=\"handlePageSizeChange\"\n                              @update:checked-row-keys=\"handleCheckRows\"\n                              flex-height style=\"height: 95%;\"/>\n            </n-card>\n        </n-layout-content>\n    </n-layout>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, h, onMounted, reactive, ref, toRefs, watch} from \"vue\";\nimport {storeToRefs} from \"pinia\";\nimport {useSystemStore} from \"@/store/modules/useSystemStore\";\nimport {AddCircleOutline, Move, SettingsOutline, TrashBinOutline, Reload} from \"@vicons/ionicons5\";\nimport {DataTableRowKey, NButton, NSpace, PaginationInfo} from \"naive-ui\";\nimport Draggable from 'vuedraggable'\nimport {useDeepClone} from \"@/hooks/useObject\";\nimport {definedProps} from \"@/components/commonTable/props\";\nimport {Size} from \"naive-ui/es/button/src/interface\";\nimport {IButtonConfig} from \"@/interface/modules/system\";\nimport {tableActionEnums} from \"@/enums/tableActionEnums\";\n\nexport default defineComponent({\n    name: \"CommonTable\",\n    emits: [\"onPageChange\", \"onPageSizeChange\", \"onEvent\"],\n    props: {\n        ...definedProps\n    },\n    components: {\n        SettingsOutline, Draggable, Move\n    },\n    setup(props, context) {\n        // 可选列\n        const selectCol = ref<Array<any>>([{\n            type: 'selection'\n        }])\n        // 操作列\n        const actionCol = ref<Array<IButtonConfig>>([\n            props.buttonConfig.editButton,\n            props.buttonConfig.deleteButton,\n        ])\n        // draggable 配置项\n        const dragData = reactive({\n            drag: false,\n        })\n        const {\n            layoutHeaderHeight,\n            clientHeight,\n            layoutFooterHeight,\n            themeColor,\n            darkTheme\n        } = storeToRefs((useSystemStore()));\n        // 表格的尺寸\n        const tableSize = ref<string>(\"medium\")\n        // 是否开启序号列\n        const hasIndex = ref<boolean>(false)\n        // 是否开启可选列\n        const hasSelect = ref<boolean>(false)\n        // 表格的列\n        const tableColumns = ref<Array<any>>([])\n        // 更多设置中的表格的列\n        const tableConfigColumns = ref<Array<any>>([])\n        // 更多设置中的单选框选中的\n        const checkedList = ref<Array<string>>([])\n        // 表格中启用可选列后选中的\n        const checkedRowList = ref<Array<any>>([])\n\n        watch(() => hasSelect.value, () => {\n            concatActionCol()\n        })\n\n        // 拼接操作列\n        const concatActionCol = () => {\n            if (hasSelect.value) {\n                tableColumns.value = selectCol.value.concat(\n                    tableConfigColumns.value.filter(column => {\n                        return checkedList.value.includes(column[props.labelField as string])\n                    })\n                )\n            } else if (checkedList.value.length < tableConfigColumns.value.length) {\n                tableColumns.value = tableConfigColumns.value.filter(column => {\n                    return checkedList.value.includes(column[props.labelField as string])\n                })\n            } else {\n                tableColumns.value = props.columns as Array<any>\n            }\n            tableColumns.value = [...tableColumns.value, ...renderActionCol()]\n        }\n\n\n        // 渲染操作列\n        const renderActionCol = () => {\n            return props.showActionCol ? [{\n                title: \"操作\",\n                key: \"action\",\n                fixed: \"right\",\n                width: props.actionWidth,\n                render: (row: any) => {\n                    return h(NSpace, null, () => [\n                            [...actionCol.value.filter(action => action.show), ...props.actionColData].map((action) => {\n                                return h(NButton, {\n                                        size: props.size as Size,\n                                        type: action.colorType,\n                                        onClick: () => onEvent(action.actionType, row)\n                                    },\n                                    {default: () => action.title}\n                                )\n                            })\n                        ]\n                    )\n                }\n            }] : []\n        }\n\n        // 检查插槽是否有内容\n        const hasQuerySlot = (name: string) => !!context.slots[name];\n        // 勾选的行\n        const handleCheckRows = (rowKeys: DataTableRowKey[]) => {\n            checkedRowList.value = rowKeys\n        }\n        // 更多设置里边的复选框选中事件\n        const handleChecked = (values: Array<any>) => {\n            // 返回勾选的列，重新赋值给 tableColumns (回显)\n            tableColumns.value = tableConfigColumns.value.filter(column => {\n                return values.includes(column[props.labelField as string])\n            })\n            tableColumns.value = [...tableColumns.value, ...renderActionCol()]\n            // 添加操作列\n            if (hasSelect.value) {\n                tableColumns.value.unshift(...selectCol.value)\n            }\n        }\n\n        const onDragEnd = () => {\n            dragData.drag = false\n            // 将排序后的 columns 重新赋值给 tableColumns (回显)\n            tableColumns.value = [...tableConfigColumns.value.filter(column => {\n                return checkedList.value.includes(column[props.labelField as string])\n            }), ...renderActionCol()]\n            if (hasSelect.value) {\n                tableColumns.value.unshift(...selectCol.value)\n            }\n        }\n\n        // 页码改变事件\n        const handlePageChange = (page: number) => {\n            context.emit(\"onPageChange\", page)\n        }\n        // 每页数据大小改变事件\n        const handlePageSizeChange = (pageSize: number) => {\n            context.emit(\"onPageSizeChange\", pageSize)\n        }\n\n        const onEvent = (type: any, row?: any) => {\n            if ([tableActionEnums.ADD, tableActionEnums.BATCH_DELETE].includes(type)) {\n                context.emit(\"onEvent\", {type: type, data: type === tableActionEnums.BATCH_DELETE ? checkedRowList.value : undefined})\n            } else {\n                context.emit(\"onEvent\", {type: type, row: row})\n            }\n        }\n\n        const reloadData = () => {\n            const pagination = props.pagination as PaginationInfo\n            handlePageChange(pagination.page)\n        }\n\n        onMounted(() => {\n            concatActionCol()\n            checkedList.value = tableColumns.value.map(item => item[props.labelField as string])\n            tableConfigColumns.value = useDeepClone(tableColumns.value.filter(column => {\n                return column.key !== \"action\"\n            }))\n            setTimeout(() => hasSelect.value = true, 0)\n        })\n\n        return {\n            tableColumns,\n            SettingsOutline,\n            Move, AddCircleOutline, TrashBinOutline, Reload,\n            hasQuerySlot,\n            tableSize,\n            hasIndex,\n            hasSelect,\n            handleCheckRows,\n            ...toRefs(dragData),\n            handleChecked,\n            checkedList,\n            tableConfigColumns,\n            checkedRowList,\n            handlePageChange,\n            handlePageSizeChange,\n            layoutHeaderHeight, clientHeight, layoutFooterHeight,\n            onDragEnd,\n            themeColor,\n            darkTheme,\n            onEvent,\n            tableActionEnums,\n            reloadData\n        }\n    }\n})\n</script>\n\n<style scoped lang=\"less\">\n.layout {\n    position: absolute;\n    top: 0;\n    left: 0;\n    right: 0;\n    bottom: 0;\n    padding: 20px;\n}\n\n.chosenClass {\n    opacity: 1;\n}\n\n.item {\n    height: 40px;\n    line-height: 40px;\n    border-radius: 4px;\n    margin: 5px 0;\n    padding: 0 5px;\n    border: 1px solid;\n    display: flex;\n    align-items: center;\n}\n\n.item:hover {\n    cursor: move;\n}\n</style>"
  },
  {
    "path": "ginyi-vue3/src/components/commonTable/props.ts",
    "content": "import {tableActionEnums} from \"@/enums/tableActionEnums\";\n\nexport const definedProps = {\n    /**\n     * 表格的列\n     */\n    columns: {\n        require: true,\n        type: Array<any>\n    },\n    /**\n     * 表格的数据\n     */\n    dataList: {\n        type: Array<any>\n    },\n    /**\n     * 整个组件的尺寸\n     */\n    size: {\n        type: String,\n        default: \"small\"\n    },\n    /**\n     * 表格的列显示中文的字段，如 {label: 年龄, value: age}，则传 label\n     */\n    labelField: {\n        type: String\n    },\n    /**\n     * 每一行数据都要有唯一的 key\n     */\n    rowKey: {\n        require: true,\n        type: Function\n    },\n    /**\n     * 操作列宽度\n     */\n    actionWidth: {\n        type: Number,\n        default: 250\n    },\n    /**\n     * 操作列的数据\n     */\n    actionColData: {\n        default: () => [],\n        type: Array<any>\n    },\n    /**\n     * 分页参数对象\n     */\n    pagination: {\n        type: [Object, Boolean],\n        default: () => false,\n    },\n    /**\n     * 是否启用loading加载\n     */\n    loading: {\n        type: Boolean\n    },\n    /**\n     * 横屏滚动区域，启用固定列时必须设置值\n     */\n    scrollX: {\n        type: Number,\n        default: 2000\n    },\n    /**\n     * 是否显示操作列\n     */\n    showActionCol: {\n        type: Boolean,\n        default: true\n    },\n    /**\n     * 按钮配置\n     */\n    buttonConfig: {\n        type: Object,\n        default: () => {\n            return {\n                addButton: {\n                    type: \"add\",\n                    title: \"新增数据\",\n                    actionType: tableActionEnums.ADD,\n                    show: true,\n                    colorType: \"primary\",\n                },\n                editButton: {\n                    type: \"edit\",\n                    title: \"编辑\",\n                    actionType: tableActionEnums.EDIT,\n                    show: true,\n                    colorType: \"primary\",\n                },\n                deleteButton: {\n                    type: \"delete\",\n                    title: \"删除\",\n                    actionType: tableActionEnums.DELETE,\n                    show: true,\n                    colorType: \"error\",\n                },\n                batchDeleteButton: {\n                    type: \"batchDelete\",\n                    colorType: \"error\",\n                    actionType: tableActionEnums.BATCH_DELETE,\n                    show: true,\n                    title: \"批量删除\",\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "ginyi-vue3/src/components/commonTable/useCommonTable.ts",
    "content": "import {reactive, ref} from \"vue\";\nimport {PaginationInfo, PaginationSizeOption} from \"naive-ui\";\n\nexport const useCommonTable = (getListApi: Function) => {\n\n    const tableTotal = ref<number>(0)\n    const tableDataList = ref<Array<any>>([])\n    const tableLoading = ref<boolean>(true)\n    const tablePagination = reactive<PaginationInfo | any>({\n        page: 1,\n        pageSize: 10,\n        pageSizes: [10, 30, 50, 100] as Array<number | PaginationSizeOption>,\n        itemCount: 0,\n        size: \"small\",\n        showQuickJumper: true,\n        showSizePicker: true,\n        prefix(info: PaginationInfo) {\n            return `总共 ${info.itemCount} 条`\n        }\n    })\n\n    const onPageChange = (page: number) => {\n        tablePagination.page = page\n        getDataList()\n    }\n    const onPageSizeChange = (pageSize: number) => {\n        tablePagination.pageSize = pageSize\n        getDataList()\n\n    }\n\n    /**\n     * 获取列表数据\n     * @param data\n     */\n    const getDataList = (data: any = {}) => {\n        tableLoading.value = true\n        getListApi(data, {\n            page: tablePagination.page,\n            pageSize: tablePagination.pageSize\n        }).then((res: any) => {\n            tableDataList.value = res.data.list\n            tablePagination.itemCount = res.data.count\n            tableLoading.value = false\n        }).catch((e: any) => {\n            tableLoading.value = true\n        })\n    }\n\n    return {\n        tableTotal,\n        tableDataList,\n        tablePagination,\n        tableLoading,\n        onPageChange,\n        onPageSizeChange,\n        getDataList\n    }\n}"
  },
  {
    "path": "ginyi-vue3/src/components/monacoEditor/index.vue",
    "content": "<template>\n    <div ref=\"codeEditBox\" class=\"codeEditBox\"></div>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, onBeforeUnmount, onMounted, ref, watch} from 'vue'\nimport {editorProps} from './monacoEditorType'\nimport JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'\nimport CssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'\nimport HtmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'\nimport TsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'\nimport EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'\nimport * as monaco from 'monaco-editor'\n\n\nexport default defineComponent({\n    name: 'monacoEditor',\n    props: editorProps,\n    emits: ['update:modelValue', 'change', 'editorMounted'],\n    setup(props, {emit}) {\n        self.MonacoEnvironment = {\n            getWorker(_: string, label: string) {\n                if (label === 'json') return new JsonWorker()\n                if (['css', 'scss', 'less'].includes(label)) return new CssWorker()\n                if (['html', 'handlebars', 'razor'].includes(label)) return new HtmlWorker()\n                if (['typescript', 'javascript'].includes(label)) return new TsWorker()\n                return new EditorWorker()\n            },\n        }\n        let editor: monaco.editor.IStandaloneCodeEditor\n        const codeEditBox = ref()\n\n        // 初始化\n        const init = () => {\n            monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({\n                noSemanticValidation: true,\n                noSyntaxValidation: false,\n            })\n            monaco.languages.typescript.javascriptDefaults.setCompilerOptions({\n                target: monaco.languages.typescript.ScriptTarget.ES2020,\n                allowNonTsExtensions: true,\n            })\n\n            editor = monaco.editor.create(codeEditBox.value, {\n                value: props.modelValue,\n                language: props.language,\n                theme: props.theme,\n                ...props.options,\n            })\n\n            // 监听值的变化\n            editor.onDidChangeModelContent(() => {\n                const value = editor.getValue()\n                //给父组件实时返回最新文本\n                emit('update:modelValue', value)\n                emit('change', value)\n            })\n\n            emit('editorMounted', editor)\n        }\n        watch(() => props.modelValue, newValue => {\n                if (editor) {\n                    const value = editor.getValue()\n                    if (newValue !== value) {\n                        editor.setValue(newValue)\n                    }\n                }\n            }\n        )\n\n        watch(() => props.options, newValue => editor.updateOptions(newValue), {deep: true})\n        watch(() => props.language, newValue => monaco.editor.setModelLanguage(editor.getModel()!, newValue))\n\n        onBeforeUnmount(() => {\n            editor.dispose()\n        })\n\n        onMounted(() => {\n            init()\n        })\n\n        return {\n            codeEditBox\n        }\n    },\n})\n</script>\n\n<style lang=\"less\" scoped>\n.codeEditBox {\n    width: 100%;\n    height: v-bind(height);\n}\n</style>\n"
  },
  {
    "path": "ginyi-vue3/src/components/monacoEditor/monacoEditorType.ts",
    "content": "import { PropType } from 'vue'\n\nexport type Theme = 'vs' | 'hc-black' | 'vs-dark'\nexport type FoldingStrategy = 'auto' | 'indentation'\nexport type RenderLineHighlight = 'all' | 'line' | 'none' | 'gutter'\nexport interface Options {\n    automaticLayout: boolean // 自适应布局\n    foldingStrategy: FoldingStrategy // 折叠方式  auto | indentation\n    renderLineHighlight: RenderLineHighlight // 行亮\n    selectOnLineNumbers: boolean // 显示行号\n    minimap: {\n        // 关闭小地图\n        enabled: boolean\n    }\n    readOnly: boolean // 只读\n    fontSize: number // 字体大小\n    scrollBeyondLastLine: boolean // 取消代码后面一大段空白\n    overviewRulerBorder: boolean // 不要滚动条的边框\n}\n\nexport const editorProps = {\n    modelValue: {\n        type: String as PropType<string>,\n        default: null,\n    },\n    width: {\n        type: [String, Number] as PropType<string | number>,\n        default: '100%',\n    },\n    height: {\n        type: [String, Number] as PropType<string | number>,\n        default: '100%',\n    },\n    language: {\n        type: String as PropType<string>,\n        default: 'javascript',\n    },\n    theme: {\n        type: String as PropType<Theme>,\n        validator(value: string): boolean {\n            return ['vs', 'hc-black', 'vs-dark'].includes(value)\n        },\n        default: 'vs-dark',\n    },\n    options: {\n        type: Object as PropType<Options>,\n        default: function () {\n            return {\n                automaticLayout: true,\n                foldingStrategy: 'indentation',\n                renderLineHighlight: 'all',\n                selectOnLineNumbers: true,\n                minimap: {\n                    enabled: true,\n                },\n                readOnly: false,\n                fontSize: 16,\n                scrollBeyondLastLine: false,\n                overviewRulerBorder: false,\n            }\n        },\n    },\n}\n"
  },
  {
    "path": "ginyi-vue3/src/config/console.log.ts",
    "content": "export const banner: string = \"%c\" +\n    \"  _______  __   __   __  ____    ____  __     \\n\" +\n    \" /  _____||  | |  \\\\ |  | \\\\   \\\\  /   / |  | \\n\" +\n    \"|  |  __  |  | |   \\\\|  |  \\\\   \\\\/   /  |  | \\n\" +\n    \"|  | |_ | |  | |       |   \\\\_    _/   |  | Version: %c1.0.0%c\\n\" +\n    \"|  |__| | |  | |  |\\\\   |     |  |     |  | 如果你喜欢这个项目，欢迎Star！https://gitee.com/Ginyi/ginyi-spring-vue 💖💖💖\\n\" +\n    \" \\\\______| |__| |__| \\\\__|     |__|     |__| Copyright © 2023-Now Ginyi@aliyun.com. All rights reserved.\\n\";\n\nconsole.log(banner, \"color:#00a3a3\",\"color:#fdbe24\", \"color:#00a3a3\")\n"
  },
  {
    "path": "ginyi-vue3/src/config/eventBus.ts",
    "content": "/**\n * 事件总线\n */\nimport mitt from 'mitt';\nexport const eventBus = mitt();"
  },
  {
    "path": "ginyi-vue3/src/config/setting.ts",
    "content": "import {IProject} from \"@/interface/modules/system\";\nimport Logo from \"@/assets/img/logo.jpg\";\n\n/**\n * 项目配置\n */\nexport const setting: IProject = {\n    devBaseURL: \"http://127.0.0.1:8066\",\n    prodBaseURL: \"http://192.168.0.102:8066\",\n    logo: Logo,\n    title: \"Ginyi\",\n    name: \"管理系统\",\n    author: \"Ginyi@aliyun.com\",\n}"
  },
  {
    "path": "ginyi-vue3/src/dictionary/useDynamicDict.ts",
    "content": "import {deptController, menuController, postController, roleController} from \"@/api\";\nimport {useRemoveEmptyChildrenField} from \"@/hooks/useTree\";\nimport {ref} from \"vue\";\n\n\nexport const useDynamicDict = () => {\n\n    // 部门字典\n    const deptDict = ref<Array<any>>([]);\n    // 岗位字典\n    const postDict = ref<Array<any>>([]);\n    // 角色字典\n    const roleDict = ref<Array<any>>([]);\n    // 菜单字典\n    const menuDict = ref<Array<any>>([]);\n\n    /**\n     * 获取部门字典\n     */\n    (async () => {\n        if (deptDict.value.length === 0) {\n            const {data} = await deptController.list({})\n            deptDict.value = useRemoveEmptyChildrenField(data.list)\n        }\n    })();\n\n    /**\n     * 获取角色字典\n     */\n    (async () => {\n        if (roleDict.value.length === 0) {\n            const {data} = await roleController.list({})\n            roleDict.value = data.list\n        }\n    })();\n\n    /**\n     * 获取岗位字典\n     */\n    (async () => {\n        if (postDict.value.length === 0) {\n            const {data} = await postController.list({})\n            postDict.value = data.list\n        }\n    })();\n\n    /**\n     * 获取菜单字典\n     */\n    (async () => {\n        if (menuDict.value.length === 0) {\n            const {data} = await menuController.list({filterButton: \"0\"})\n            menuDict.value = useRemoveEmptyChildrenField(data.list)\n        }\n    })();\n\n\n    return {\n        deptDict,\n        postDict,\n        roleDict,\n        menuDict\n    }\n}\n\n"
  },
  {
    "path": "ginyi-vue3/src/dictionary/useStaticDict.ts",
    "content": "import {ref} from \"vue\";\n\nexport const useStaticDict = () => {\n    return {\n        // 状态字典\n        statusDict: ref<Array<any>>([\n            {\n                label: \"正常\",\n                value: \"0\",\n            },\n            {\n                label: \"禁用\",\n                value: \"1\"\n            }\n        ]),\n\n        // 成功与否字典\n        successDict: ref<Array<any>>([\n            {\n                label: \"成功\",\n                value: \"0\",\n            },\n            {\n                label: \"失败\",\n                value: \"1\"\n            }\n        ]),\n\n        // 性别字典\n        sexDict: ref<Array<any>>([\n            {\n                label: \"男\",\n                value: \"0\",\n            },\n            {\n                label: \"女\",\n                value: \"1\"\n            },\n            {\n                label: \"未知\",\n                value: \"2\"\n            }\n        ]),\n\n        // 菜单类型字典\n        menuTypeDict: ref<Array<any>>([\n            {\n                label: \"目录\",\n                value: \"M\",\n            },\n            {\n                label: \"菜单\",\n                value: \"C\"\n            },\n            {\n                label: \"按钮\",\n                value: \"F\"\n            }\n        ]),\n\n        // 显示隐藏字典\n        showDict: ref<Array<any>>([\n            {\n                label: \"显示\",\n                value: \"0\",\n            },\n            {\n                label: \"隐藏\",\n                value: \"1\"\n            }\n        ]),\n\n        // 是否字典\n        whetherDict: ref<Array<any>>([\n            {\n                label: \"是\",\n                value: \"0\",\n            },\n            {\n                label: \"否\",\n                value: \"1\"\n            }\n        ]),\n\n        // 操作类型\n        operationType: ref<Array<any>>([\n            {\n                label: \"其他\",\n                value: \"0\",\n            },\n            {\n                label: \"新增\",\n                value: \"1\",\n            },\n            {\n                label: \"修改\",\n                value: \"2\",\n            },\n            {\n                label: \"删除\",\n                value: \"3\",\n            },\n            {\n                label: \"清除\",\n                value: \"9\",\n            }\n        ])\n    }\n}\n\n"
  },
  {
    "path": "ginyi-vue3/src/directives/draggable.ts",
    "content": "import {Directive} from \"vue\";\n\nexport const drag: Directive = {\n    mounted(el: HTMLElement) {\n        // 设置目标元素基础属性\n        el.style.cursor = \"move\";\n        el.style.position = \"fixed\";\n        el.style.zIndex = \"99999\";\n\n        // 监听鼠标在目标元素上按下\n        el.addEventListener(\"mousedown\", (e) => {\n            // 当前目标元素的left与top\n            const left = el.offsetLeft;\n            const top = el.offsetTop;\n            // 保存按下的鼠标的X与Y\n            const mouseX = e.clientX;\n            const mouseY = e.clientY;\n\n            // 监听鼠标移动\n            document.onmousemove = (e) => {\n                // 鼠标移动的距离\n                let disX = e.clientX - mouseX;\n                let disY = e.clientY - mouseY;\n\n                el.style.left = (left + disX) + \"px\";\n                el.style.top = (top + disY) + \"px\";\n                return false // 防止选中文本，文本拖动的问题\n            }\n\n            // 监听鼠标抬起\n            document.onmouseup = () => {\n                document.onmousemove = null;\n                document.onmouseup = null;\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "ginyi-vue3/src/directives/focus.ts",
    "content": "import {Directive, DirectiveBinding} from \"vue\";\n\n/**\n * 自动获取输入框焦点\n * 同个页面使用多个v-focus，最后一个生效\n */\nexport const focus: Directive = {\n    mounted(el: HTMLElement, binding: DirectiveBinding) {\n\n        /**\n         * 原生输入框 v-focus.native\n         */\n        if (binding.modifiers?.native) {\n            el.focus()\n            return;\n        }\n        /**\n         * n-input  v-focus\n         */\n        const input = el?.childNodes[0]?.childNodes[1]?.childNodes[0];\n        // @ts-ignore\n        input.focus();\n    }\n}"
  },
  {
    "path": "ginyi-vue3/src/directives/index.ts",
    "content": "import {App} from 'vue';\nimport {focus} from \"@/directives/focus\";\nimport {loading} from \"@/directives/loading\";\nimport {drag} from \"@/directives/draggable\"\n\n/**\n * 注册全局自定义指令\n * 注意：需要在web-types.json声明一下，避免编译器警告\n * @param app\n */\nexport const initDirectives = (app: App) => {\n    // 自动获取焦点\n    app.directive('focus', focus);\n    app.directive('loading', loading);\n    app.directive('drag', drag);\n}\n"
  },
  {
    "path": "ginyi-vue3/src/directives/loading.ts",
    "content": "import {createApp, Directive} from \"vue\";\nimport NSpin from \"naive-ui\"\n\n/**\n * 加载中...\n */\nexport const loading: Directive = {\n\n}\n\n"
  },
  {
    "path": "ginyi-vue3/src/enums/storeKeyEnums.ts",
    "content": "export const enum storeKeyEnums {\n    SYSTEM = \"system\",\n    USER = \"user\",\n    ROUTER = \"router\",\n}"
  },
  {
    "path": "ginyi-vue3/src/enums/tableActionEnums.ts",
    "content": "export enum tableActionEnums {\n    // 新增\n    ADD = \"add\",\n    // 编辑\n    EDIT = \"edit\",\n    // 删除\n    DELETE = \"delete\",\n    // 批量删除\n    BATCH_DELETE = \"BATCH_DELETE\",\n}"
  },
  {
    "path": "ginyi-vue3/src/hooks/useColor.ts",
    "content": "/**\n * 将传递的百分比与十六进制颜色的R、G或B相加\n * @param {string} color 要更改的颜色\n * @param {number} amount 更改颜色的量\n * @returns {string} 颜色的处理部分\n */\nfunction useAddLight(color: string, amount: number): string {\n    const cc = parseInt(color, 16) + amount;\n    const c = cc > 255 ? 255 : cc;\n    return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`;\n}\n\n/**\n * 根据传递的百分比点亮6个字符的HEX颜色\n * @param {string} color 要更改的颜色\n * @param {number} amount 更改颜色的量\n * @returns {string} 处理后的颜色表示为HEX\n */\nexport function useLighten(color: string, amount: number): string {\n    color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color;\n    amount = Math.trunc((255 * amount) / 100);\n    return `#${useAddLight(color.substring(0, 2), amount)}${useAddLight(\n        color.substring(2, 4),\n        amount\n    )}${useAddLight(color.substring(4, 6), amount)}`;\n}\n\n/**\n * hex转rgba\n * @param bgColor\n * @param alpha\n */\nexport const useHexToRgba = (bgColor: string, alpha = 0.2): string => {\n    let color = bgColor.slice(1); // 去掉'#'号\n    let rgba = [\n        parseInt(\"0x\" + color.slice(0, 2)),\n        parseInt(\"0x\" + color.slice(2, 4)),\n        parseInt(\"0x\" + color.slice(4, 6)),\n        alpha\n    ];\n    return `rgba(${rgba.toString()})`;\n};"
  },
  {
    "path": "ginyi-vue3/src/hooks/useCommonColums.ts",
    "content": "import {useStaticDict} from \"@/dictionary/useStaticDict\";\n\nexport const useCommonColumns = () => {\n    const {statusDict, successDict} = useStaticDict()\n\n\n    /**\n     * 渲染状态字典\n     * @param stateId\n     */\n    const useRenderStateById = (stateId: string) => {\n        const temp = statusDict.value.filter(state => {\n            return state.value === stateId\n        })\n        return temp.length !== 0 ? temp[0].label : undefined;\n    }\n\n    /**\n     * 渲染成功字典\n     * @param successId\n     */\n    const useRenderSuccessById = (successId: string) => {\n        const temp = successDict.value.filter(state => {\n            return state.value === successId.toString()\n        })\n        return temp.length !== 0 ? temp[0].label : undefined;\n    }\n\n    return {\n        useRenderStateById,\n        useRenderSuccessById\n    }\n}"
  },
  {
    "path": "ginyi-vue3/src/hooks/useDebthro.ts",
    "content": "/**\n * 节流\n * @param {*} fn 执行函数\n * @param {*} wait 节流时间,毫秒\n */\nexport const useThrottle = function (fn: Function, wait?: number) {\n    let timer: any;\n    return function (this: any) {\n        if (!timer) {\n            timer = setTimeout(() => {\n                fn.apply(this, arguments)\n                timer = null\n            }, wait)\n        }\n    }\n}\n\n/**\n * 防抖\n * @param {*} fn 执行函数\n * @param {*} wait 防抖时间,毫秒\n */\nexport const useDebounce = function (fn: Function, wait: number) {\n    let timer: any\n    return function (this: any) {\n        // 如果多次触发将上次记录延迟清除掉\n        if (timer !== null) {\n            clearTimeout(timer)\n        } else {\n            timer = setTimeout(() => {\n                fn.apply(this, arguments)\n                timer = null\n            }, wait)\n        }\n    }\n}"
  },
  {
    "path": "ginyi-vue3/src/hooks/useFormat.ts",
    "content": "export const useFormatTime = (times: number, showTime = true) => {\n    try {\n        const time = new Date(times)\n        const year = time.getFullYear();\n        const month = time.getMonth() + 1;\n        const date = time.getDate();\n        const hour = time.getHours();\n        const minute = time.getMinutes();\n        const second = time.getSeconds();\n        const NewDate = year + \"-\" + (month < 10 ? '0' + month : month) + \"-\" + (date < 10 ? '0' + date : date)\n        const NewTime = hour + \":\" + (minute < 10 ? '0' + minute : minute) + \":\" + (second < 10 ? '0' + second : second);\n        if (showTime) return `${NewDate} ${NewTime}`\n        return NewDate\n    } catch (e) {\n        console.error('时间戳不合法')\n    }\n}"
  },
  {
    "path": "ginyi-vue3/src/hooks/useMenu.ts",
    "content": "import {useLoadIcon, useRenderIcon} from \"@/plugins/naive-ui/common\";\n\n/**\n * 重新格式菜单列表，主要是为了渲染 icon 和 删除空children\n * @param menuList\n * @param formatIcon 是否需要处理icon\n */\nexport const useMenuFormat = (menuList: any, formatIcon?: boolean): any => {\n    return menuList.map((menu: any) => {\n        if (formatIcon) {\n            menu.icon = useRenderIcon(useLoadIcon(menu.icon))\n        }\n        if (menu.children?.length > 0) {\n            useMenuFormat(menu.children, formatIcon)\n        } else {\n            delete menu.children\n        }\n        return menu\n    })\n}"
  },
  {
    "path": "ginyi-vue3/src/hooks/useObject.ts",
    "content": "import * as Icons from \"@vicons/ionicons5\";\n\n/**\n * 简单实现对象的深度克隆\n * 注意：\n *      1、使用该方式会出现一些问题，如值为undefined、函数、Date类型等属性会无法被克隆，此处待完善！！！\n *      2、非得使用undefined的话，可以使用null替代\n * @param obj\n */\nexport function useDeepClone<T>(obj: T): T {\n    // 如果是基本数据类型，直接返回\n    if (typeof obj !== 'object' || obj === null) {\n        return obj;\n    }\n    // 根据类型创建新对象或数组\n    const newObj = Array.isArray(obj) ? [] : {};\n    // 递归复制属性或元素\n    for (const key in obj) {\n        // @ts-ignore\n        newObj[key] = useDeepClone(obj[key]);\n    }\n    return newObj as T;\n}"
  },
  {
    "path": "ginyi-vue3/src/hooks/usePagination.ts",
    "content": "import {reactive} from \"vue\";\nimport {PaginationInfo, PaginationSizeOption} from \"naive-ui\";\n\nexport const usePagination = () => {\n    const pagination = reactive<PaginationInfo | any>({\n        page: 1,\n        pageSize: 5,\n        pageSizes: [5, 10, 20, 50] as Array<number | PaginationSizeOption>,\n        itemCount: 0,\n        size: \"small\",\n        showQuickJumper: true,\n        showSizePicker: true,\n        prefix(info: PaginationInfo) {\n            return `总共 ${info.itemCount} 条`\n        }\n    })\n\n    const onPageChange = (page: number) => {\n        pagination.page = page\n    }\n    const onPageSizeChange = (pageSize: number) => {\n        pagination.pageSize = pageSize\n    }\n\n    return {\n        pagination\n    }\n}\n\n"
  },
  {
    "path": "ginyi-vue3/src/hooks/usePending.ts",
    "content": "import axios, {AxiosRequestConfig} from \"axios\";\n\nconst pendingMap = new Map();\n\n/**\n * 生成每个请求唯一的键\n * @param {*} config\n * @returns string\n */\nexport const getPendingKey = (config: AxiosRequestConfig<any>) => {\n    let {url, method, params, data} = config;\n    // response里面返回的config.data是个字符串对象\n    if (typeof data === 'string') {\n        data = JSON.parse(data);\n    }\n    return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&');\n}\n\n/**\n * 储存每个请求唯一值, 也就是cancel()方法, 用于取消请求\n * @param {*} config\n */\nexport const addPending = (config: AxiosRequestConfig<any>) => {\n    const pendingKey = getPendingKey(config);\n    config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => {\n        if (!pendingMap.has(pendingKey)) {\n            pendingMap.set(pendingKey, cancel);\n        }\n    });\n}\n\n/**\n * 删除重复的请求\n * @param {*} config\n */\nexport const removePending = (config: AxiosRequestConfig<any>): boolean => {\n    const pendingKey = getPendingKey(config);\n    if (pendingMap.has(pendingKey)) {\n        const cancelToken = pendingMap.get(pendingKey);\n        cancelToken(pendingKey);\n        pendingMap.delete(pendingKey);\n        return true;\n    }else {\n        return false;\n    }\n}"
  },
  {
    "path": "ginyi-vue3/src/hooks/useStorage.ts",
    "content": "import {storeKeyEnums} from \"@/enums/storeKeyEnums\";\n\nexport class storage {\n\n    /**\n     * 默认的存储时间（天）\n     */\n    private static expireTime: number = 7\n\n    /**\n     * 设置缓存数据\n     * @param key\n     * @param value\n     * @param expire 过期时间，单位天\n     */\n    static set(key: string, value: any, expire?: number) {\n        const data = JSON.stringify({\n            value,\n            expire: expire !== undefined ? Date.now() + expire * 60 * 60 * 24 * 7 * 1000 : Date.now() + this.expireTime * 60 * 60 * 24 * 7 * 1000,\n        });\n        localStorage.setItem(key, data)\n    }\n\n    /**\n     * 获取缓存数据\n     * @param key\n     */\n    static get(key: string): any {\n        const data: any = localStorage.getItem(key)\n        if (data) {\n            const {value, expire} = JSON.parse(data);\n            // 在有效期内直接返回\n            if ([null, undefined].includes(expire) || expire >= Date.now()) {\n                return value;\n            }\n            // 清除缓存\n            this.remove(key)\n            return null\n        }\n    }\n\n    /**\n     * 清除缓存\n     * @param key\n     */\n    static remove(key: string) {\n        localStorage.removeItem(key)\n    }\n\n    /**\n     * 清除缓存，除系统配置外\n     */\n    static clear() {\n        let keys: Array<String> = [];\n        for (let i = 0; i < localStorage.length; i++) {\n            keys.push(localStorage.key(i) as string)\n        }\n        keys.map(key => {\n            if (key.indexOf(storeKeyEnums.SYSTEM) === -1) {\n                this.remove(key as string)\n            }\n        })\n    }\n}\n"
  },
  {
    "path": "ginyi-vue3/src/hooks/useTree.ts",
    "content": "/**\n *\n * @param name 子节点 name\n * @param data  要查找的 list\n * @param result  结果集\n * @returns {boolean|*[]}\n */\n\nexport const useFindParentName = (name: string, data: Array<any> = [], result: Array<string> = []): Array<string> | undefined => {\n    for (let i = 0; i < data.length; i++) {\n        const item = data[i];\n        if (item.name === name) {\n            return result;\n        }\n        if (item.children?.length > 0) {\n            result.push(item.name)\n            const temp = useFindParentName(name, item.children, result);\n            if (temp !== undefined) {\n                return result;\n            }\n            result = []\n        }\n    }\n}\n\n/**\n * 根据 name 查找匹配的 node\n *\n * @param name\n * @param data\n */\nexport const useFindNodeByName = (name: string, data: Array<any>): any => {\n    for (let i = 0; i < data.length; i++) {\n        const item = data[i];\n        if (item.name === name) {\n            return item;\n        } else {\n            if (item.children?.length > 0) {\n                const temp = useFindNodeByName(name, item.children);\n                if (temp) {\n                    return temp;\n                }\n            }\n        }\n    }\n}\n\n\n/**\n * 根据子节点指定的key和对应的value，向上查找其父级\n * @param key 如 userId: 123 中的 userId\n * @param value 如 userId: 123 中的 123\n * @param childrenField\n * @param data\n * @param result\n */\nexport const useFindParentNodes = (key: string,\n                                   value: string | number,\n                                   childrenField: string = \"children\",\n                                   data: Array<any> = [],\n                                   result: Array<any> = []): Array<any> | undefined => {\n    for (let i = 0; i < data.length; i++) {\n        const item = data[i];\n        result.push(item);\n        if (item[key] === value) {\n            return result;\n        }\n        if (item.children?.length > 0) {\n            const temp = useFindParentNodes(key, value, childrenField, item.children, result);\n            if (temp !== undefined) {\n                // 表示找到了 name 匹配的, 替换 result 中的最后一项 (最后一项保存的是同级别下的上一项)\n                if (temp.length === 1) {\n                    result[result.length - 1] = temp[0];\n                }\n                return result;\n            }\n        }\n        result.pop()\n    }\n}\n\n\n/**\n * 格式化树\n */\nexport const useRemoveEmptyChildrenField = (data: Array<any>, childrenField: string = \"children\"): Array<any> => {\n    return data.map((item: any) => {\n        if (item[childrenField]?.length > 0) {\n            useRemoveEmptyChildrenField(item[childrenField], childrenField)\n        } else {\n            delete item[childrenField]\n        }\n        return item\n    })\n}\n\n/**\n * 数组扁平化\n * @param data\n * @param result\n * @param childrenField\n */\nexport const useTreeToArray = (data: Array<any>, result: Array<any> = [], childrenField: string = \"children\"): Array<any> => {\n    data.map(item => {\n        result.push(item)\n        if (item[childrenField]?.length > 0) {\n            useTreeToArray(item[childrenField], result)\n        }\n    })\n    return result\n}\n\n\n/**\n * 搭配 useFindParentNodes 使用（先查找出所有的父级节点）\n * 数组分割显示 如 总公司 / 市场部门 / 销售岗\n */\n\nexport const useArraySeparator = (data: Array<any>, key: string, separator: string = \"/\") => {\n    let result: string = \"\";\n    data.forEach((item, index) => {\n        if (index === data.length - 1) {\n            result = `${result} ${item[key]}`\n        } else {\n            result = `${result} ${item[key]} ${separator}`\n        }\n    })\n    return result;\n}\n\n/**\n * 克隆出一个对象的属性\n * @param data\n * @param sourceField\n * @param targetField\n * @param childrenField\n */\nexport const useFieldClone = (data: Array<any>, sourceField: string, targetField: string, childrenField: string = \"children\") => {\n    const temp =  data.map((item: any) => {\n        item[targetField] = item[sourceField]\n        if (item[childrenField]?.length > 0) {\n            useFieldClone(item[childrenField], sourceField, targetField, childrenField)\n        } else {\n            delete item[childrenField]\n        }\n        return item\n    })\n    return temp\n}"
  },
  {
    "path": "ginyi-vue3/src/interface/modules/system/index.ts",
    "content": "/**\n * 项目配置\n */\nimport {Component} from \"vue\";\n\nexport interface IProject {\n    // 开发环境请求地址\n    devBaseURL: string | undefined;\n    // 生产环境请求地址\n    prodBaseURL: string | undefined;\n    // 项目logo\n    logo?: string | undefined;\n    // 项目名称 = title + name\n    title?: string | undefined;\n    // 项目名称 = title + name\n    name?: string | undefined;\n    // 作者\n    author?: string | undefined;\n}\n\n/**\n * pinia - 系统状态\n */\nexport interface ISystemState {\n    // 系统深色主题\n    darkTheme?: boolean | undefined;\n    // 系统主题色\n    themeColor?: string | undefined;\n    // 系统主题色列表\n    themeColorList?: Array<string>;\n    // 客户端宽度\n    clientWidth?: number | undefined;\n    // 客户端高度\n    clientHeight?: number | undefined;\n    // Layout - header 的高度\n    layoutHeaderHeight?: number | undefined;\n    // Layout - footer 的高度\n    layoutFooterHeight?: number | undefined;\n    // 侧边栏折叠\n    collapsed?: boolean | undefined;\n    // logo处的高度\n    logoHeight?: number | undefined;\n    // 多标签的高度\n    tabsHeight?: number | undefined;\n    // 多标签页 选中的的索引\n    tabIndex?: number\n    // 多标签页 列表\n    tabsList?: Array<ITabType>;\n    // 左侧菜单栏列表\n    menuList?: Array<any>;\n    // 面包屑导航列表\n    breadMenuList?: Array<any>;\n}\n\nexport interface ITabType {\n    id: number,\n    tabKey: string,\n    tabName: string,\n    icon?: string | Component | Function\n}\n\n/**\n * pinia - 系统路由\n */\nexport interface IRouterType {\n    // 路由列表\n    routesList?: Array<any>;\n    // 需要被缓存的路由列表\n    routesKeepAliveList?: Array<any>\n}\n\n/**\n * 登录表单\n */\nexport interface ILoginFormType {\n    // 用户名\n    username: string | undefined;\n    // 密码\n    password: string | undefined;\n    // 验证码\n    code: string | undefined;\n}\n\n/**\n * 注册表单\n */\nexport interface IRegisterFormType {\n    // 用户名\n    username: string | undefined;\n    // 密码\n    password: string | number | undefined;\n    // 重复密码\n    password2: string | number | undefined;\n    // 验证码\n    code: string | undefined;\n}\n\n/**\n * 用户\n */\nexport interface IUser {\n    // 用户名\n    username?: string | undefined;\n    // Token key\n    tokenKey?: string | undefined;\n    // Token令牌\n    authorization?: string | undefined;\n}\n\n/**\n * 表格列\n */\nexport interface IColumnType {\n    key: string;\n    title: string;\n    width?: number\n}\n\n/**\n * 分页\n */\nexport interface IPage {\n    page: number | string;\n    pageSize: number | string\n}\n\n/**\n * CommonTable按钮配置\n */\nexport interface IButtonConfig {\n    // 按钮类型，如 add 新增\n    type?: string,\n    // 按钮文本，如 新增\n    title?: string,\n    // 按钮事件类型\n    actionType?: any,\n    // 是否显示，必传\n    show: boolean,\n    // 按钮颜色类型\n    colorType?: \"default\" | \"tertiary\" | \"primary\" | \"success\" | \"warning\" | \"error\" | \"info\",\n}\nexport interface IButtonType {\n    addButton: IButtonConfig,\n    editButton: IButtonConfig,\n    deleteButton: IButtonConfig,\n    batchDeleteButton:IButtonConfig\n}\n"
  },
  {
    "path": "ginyi-vue3/src/layout/header/breadcrumb.vue",
    "content": "<template>\n    <n-breadcrumb>\n        <transition-group name=\"bread\">\n            <n-breadcrumb-item v-for=\"(item, index) in breadMenuList\" :key=\"index\">\n                <n-icon style=\"margin-right: 5px\">\n                    <Icon :icon=\"item.icon\"/>\n                </n-icon>\n                <n-dropdown v-if=\"index !== breadMenuList.length - 1\"\n                            :options=\"useMenuFormat(useDeepClone(breadMenuList[index].children), true)\"\n                            placement=\"bottom-start\"\n                            key-field=\"menuId\"\n                            label-field=\"menuName\"\n                            @select=\"handleSelect\"\n                            trigger=\"click\">\n                    {{ item.menuName }}\n                </n-dropdown>\n                <span v-else>{{ item.menuName }}</span>\n            </n-breadcrumb-item>\n        </transition-group>\n    </n-breadcrumb>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, watch} from \"vue\";\nimport {ApertureOutline, Bicycle, BulbOutline} from \"@vicons/ionicons5\";\nimport {useRoute} from \"vue-router\";\nimport {useFindParentNodes} from \"@/hooks/useTree\";\nimport {useDeepClone} from \"@/hooks/useObject\";\nimport {storeToRefs} from \"pinia\";\nimport {useSystemStore} from \"@/store/modules/useSystemStore\";\nimport {DropdownOption} from \"naive-ui\";\nimport {useCommonRouter} from \"@/router\";\nimport {useMenuFormat} from \"@/hooks/useMenu\";\n\n\nexport default defineComponent({\n    name: \"Breadcrumb \",\n    setup() {\n        const currentRoute = useRoute();\n        const {menuList, breadMenuList} = storeToRefs(useSystemStore());\n\n        const handleSelect = (key: string, option: DropdownOption) => {\n            useCommonRouter(option.name as string)\n        }\n\n        /**\n         * 监听路由变化，实时更新面包屑导航的内容\n         */\n        watch(() => currentRoute.name, () => {\n            const list = useFindParentNodes(\"name\", currentRoute.name as string, \"children\",\n                useMenuFormat(useDeepClone(menuList?.value as Array<any>), false)\n            ) as Array<any>\n            useSystemStore().setBreadMenuList(list)\n        })\n\n        return {\n            breadMenuList,\n            handleSelect,\n            useDeepClone,\n            useMenuFormat,\n            ApertureOutline, Bicycle, BulbOutline\n        }\n    }\n})\n</script>\n\n<style lang=\"less\" scoped>\n.bread-enter-active,\n.bread-leave-active {\n    transition: all 0.5s ease;\n}\n\n.bread-enter-from,\n.bread-leave-active {\n    opacity: 0;\n    transform: translateX(20px);\n}\n\n.bread-leave-active {\n    position: absolute;\n    z-index: -1;\n}\n</style>"
  },
  {
    "path": "ginyi-vue3/src/layout/header/index.vue",
    "content": "<template>\n    <div style=\"display: flex; align-items: center;height: 100%; justify-content: space-between\">\n        <Breadcrumb/>\n        <Personnel />\n    </div>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent} from \"vue\";\nimport Breadcrumb from \"@/layout/header/breadcrumb.vue\";\nimport Personnel from \"@/layout/header/personnel.vue\";\n\nexport default defineComponent({\n    name: \"Headers\",\n    components: {\n        Breadcrumb,\n        Personnel\n    },\n    setup() {\n\n        return {\n            Breadcrumb, Personnel\n        }\n    }\n})\n</script>"
  },
  {
    "path": "ginyi-vue3/src/layout/header/personnel.vue",
    "content": "<template>\n    <div style=\"display: flex; align-items: center\">\n        <span style=\"margin-right: 15px\">{{ username }}</span>\n        <n-dropdown :options=\"options\" @select=\"handleSelect\" :show-arrow=\"true\">\n            <n-avatar round size=\"medium\" :src=\"logo\" style=\"margin-right: 10px\"/>\n        </n-dropdown>\n    </div>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, ref} from \"vue\";\nimport {storeToRefs} from \"pinia\";\nimport {LogOutOutline as LogoutIcon, Moon, PersonCircleOutline as UserIcon, SunnySharp} from '@vicons/ionicons5'\nimport {useRenderIcon} from \"@/plugins/naive-ui/common\";\nimport {useUserStore} from \"@/store/modules/useUserStore\";\nimport {useCommonRouter} from \"@/router\";\nimport {setting} from \"@/config/setting\";\nimport {useRouterStore} from \"@/store/modules/useRouterStore\";\n\n\nexport default defineComponent({\n    name: \"Personnel\",\n    components: {\n        Moon, SunnySharp\n    },\n    setup() {\n        const {username} = storeToRefs(useUserStore());\n        const {logo} = setting;\n        const options = ref<Array<{ label: string, key: string, icon?: Function }>>([\n            {\n                label: '个人中心',\n                key: 'person',\n                icon: useRenderIcon(UserIcon)\n            },\n            {\n                label: '退出登录',\n                key: 'logout',\n                icon: useRenderIcon(LogoutIcon)\n            }\n        ])\n        const handleSelect = (key: string) => {\n            if (key === \"logout\") {\n                useUserStore().logout().then(() => {\n                    useCommonRouter(\"login\")\n                    useRouterStore().$reset()\n                    window.$message.success(\"退出成功\")\n                })\n            }\n        }\n\n        return {\n            logo,\n            options,\n            username,\n            handleSelect\n        }\n    }\n})\n</script>\n\n<style scoped>\n\n</style>"
  },
  {
    "path": "ginyi-vue3/src/layout/index.vue",
    "content": "<template>\n    <n-layout has-sider class=\"layout\">\n        <n-layout-sider\n            class=\"layout-side\"\n            bordered\n            show-trigger=\"bar\"\n            collapse-mode=\"width\"\n            :collapsed-width=\"64\"\n            :collapsed=\"collapsed\"\n            :width=\"240\"\n            :inverted=\"true\"\n            :native-scrollbar=\"false\"\n            :on-update:collapsed=\"(val) => setCollapsed(val)\">\n            <Logo :style=\"{height: logoHeight + 'px'}\"/>\n            <Menu/>\n        </n-layout-sider>\n        <n-layout content-style=\"display: flex;flex-direction: column;\">\n            <n-layout-header :style=\"{height: (layoutHeaderHeight + tabsHeight) + 'px', padding: '0px 10px 10px 10px'}\">\n                <div>\n                    <Headers :style=\"{height: layoutHeaderHeight - 10 + 'px'}\"/>\n                    <TabsView/>\n                </div>\n            </n-layout-header>\n            <n-layout-content :content-style=\"{padding: '15px 15px 15px 20px', backgroundColor: darkTheme ? null : '#f5f6fa'}\">\n                <router-view v-slot=\"{ Component }\">\n                    <transition name=\"fade-main\" mode=\"out-in\">\n                        <keep-alive>\n                            <component :is=\"Component\"/>\n                        </keep-alive>\n                    </transition>\n                </router-view>\n            </n-layout-content>\n            <n-layout-footer\n                :style=\"{height: layoutFooterHeight + 'px', display: 'flex', alignItems: 'center', justifyContent: 'center'}\">\n                {{ `Copyright © 2023-Now ${author}. All rights reserved.` }}\n            </n-layout-footer>\n        </n-layout>\n        <Theme/>\n    </n-layout>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent} from 'vue'\nimport {CaretDownOutline, SparklesOutline} from '@vicons/ionicons5'\nimport Logo from \"@/layout/logo/index.vue\";\nimport {useSystemStore} from \"@/store/modules/useSystemStore\";\nimport Headers from \"@/layout/header/index.vue\";\nimport {storeToRefs} from \"pinia\";\nimport {setting} from \"@/config/setting\";\nimport TabsView from \"@/layout/tabs/index.vue\";\nimport Theme from \"@/layout/theme/index.vue\";\nimport Menu from \"@/layout/menu/index.vue\";\nimport {useRouterStore} from \"@/store/modules/useRouterStore\";\n\nexport default defineComponent({\n    name: \"Layout\",\n    components: {\n        Logo,\n        Headers,\n        TabsView,\n        Theme,\n        Menu\n    },\n    setup() {\n        const {\n            clientHeight,\n            layoutHeaderHeight,\n            layoutFooterHeight,\n            collapsed,\n            logoHeight,\n            darkTheme,\n            tabsHeight\n        } = storeToRefs(useSystemStore());\n        const {routesKeepAliveList} = storeToRefs(useRouterStore())\n        const {title, author} = setting;\n        const {setCollapsed} = useSystemStore();\n\n\n        return {\n            title,\n            author,\n            clientHeight,\n            layoutHeaderHeight,\n            layoutFooterHeight,\n            collapsed,\n            logoHeight,\n            setCollapsed,\n            darkTheme,\n            tabsHeight,\n            Logo,\n            Headers,\n            CaretDownOutline,\n            SparklesOutline,\n            routesKeepAliveList\n        }\n    }\n})\n</script>\n\n<style lang=\"less\" scoped>\n.layout {\n    position: fixed;\n    top: 0;\n    left: 0;\n    right: 0;\n    bottom: 0;\n    &-side {\n        box-shadow: 2px 0 8px 0 rgb(29 35 41 / 5%);\n        transition: all 0.3s ease-in-out;\n    }\n}\n</style>\n"
  },
  {
    "path": "ginyi-vue3/src/layout/logo/index.vue",
    "content": "<template>\n    <div class=\"logo-box\" @click=\"goHome\">\n        <n-avatar v-if=\"collapsed\" round size=\"medium\" :src=\"logo\" style=\"margin-right: 10px\"/>\n        <div v-else style=\"display: flex; align-items: center; margin-left: 15px;\">\n            <n-avatar round size=\"medium\" :src=\"logo\" style=\"margin-right: 10px\"/>\n            <h3 style=\"white-space: nowrap\">{{ `${title} ${name}` }}</h3>\n        </div>\n    </div>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent} from \"vue\";\nimport {useSystemStore} from \"@/store/modules/useSystemStore\";\nimport {storeToRefs} from \"pinia\";\nimport {setting} from \"@/config/setting\";\nimport {useCommonRouter} from \"@/router\";\n\nexport default defineComponent({\n    name: \"Logo\",\n    setup() {\n        const {collapsed} = storeToRefs(useSystemStore());\n        const {logo, title, name} = setting;\n\n        const goHome = () => {\n            useCommonRouter(\"home\")\n        }\n        return {\n            goHome,\n            logo, title, name, collapsed\n        }\n    }\n})\n</script>\n\n<style lang=\"less\" scoped>\n.logo-box {\n    display: flex;\n    align-items: center;\n    margin-left: 15px;\n}\n.logo-box:hover {\n    cursor: pointer;\n}\n</style>"
  },
  {
    "path": "ginyi-vue3/src/layout/menu/index.vue",
    "content": "<template>\n    <n-scrollbar>\n        <n-menu key-field=\"name\"\n                label-field=\"menuName\"\n                :collapsed-width=\"64\"\n                :accordion=\"true\"\n                :inverted=\"!darkTheme\"\n                :collapsed-icon-size=\"22\"\n                :options=\"menuOptions\"\n                :expand-icon=\"useRenderIcon(CaretDownOutline)\"\n                :expanded-keys=\"openKeys\"\n                v-model:value=\"activeMenuKey\"\n                @update:expanded-keys=\"handleUpdateExpandedKeys\"\n                @update:value=\"handleClickMenu\"/>\n    </n-scrollbar>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, ref, watch, watchEffect} from \"vue\";\nimport {useRenderIcon} from \"@/plugins/naive-ui/common\";\nimport {CaretDownOutline} from \"@vicons/ionicons5\";\nimport {useSystemStore} from \"@/store/modules/useSystemStore\";\nimport {storeToRefs} from \"pinia\";\nimport {useMenuFormat} from \"@/hooks/useMenu\";\nimport {MenuOption} from \"naive-ui\";\nimport {useCommonRouter} from \"@/router\";\nimport {useRoute} from \"vue-router\";\nimport {useFindNodeByName, useFindParentName} from \"@/hooks/useTree\";\nimport {useDeepClone} from \"@/hooks/useObject\";\nimport {ITabType} from \"@/interface/modules/system\";\n\nexport default defineComponent({\n    name: \"Menu\",\n    setup() {\n        const currentRoute = useRoute();\n        const {darkTheme, menuList, tabIndex} = storeToRefs(useSystemStore());\n        const menuOptions = ref<Array<any>>([])\n        const activeMenuKey = ref<string | undefined>(undefined)\n        const openKeys = ref<Array<string>>([])\n\n        const handleClickMenu = (key: string, item: MenuOption) => {\n            useCommonRouter(item.name as string)\n\n        }\n        const handleUpdateExpandedKeys = (keys: string[]) => {\n            openKeys.value = keys\n        }\n\n        /**\n         * 监听路由的变化，实时更新多标签页的内容\n         */\n        watch(() => currentRoute.name, () => {\n            const clickedMenu = useFindNodeByName(currentRoute.name as string, useDeepClone(menuList?.value as Array<any>))\n            const bread: ITabType = {\n                id: clickedMenu?.menuId,\n                icon: clickedMenu?.icon,\n                tabKey: clickedMenu?.name,\n                tabName: clickedMenu?.menuName,\n            }\n            useSystemStore().addTab(bread)\n        })\n        watchEffect(() => {\n            activeMenuKey.value = currentRoute.name as string\n            menuOptions.value = useMenuFormat(useDeepClone(menuList?.value as Array<any>), true)\n            openKeys.value = useFindParentName(currentRoute.name as string, menuList?.value as Array<any>) as Array<string>\n        })\n\n        return {\n            darkTheme,\n            menuOptions,\n            useRenderIcon,\n            handleClickMenu,\n            activeMenuKey,\n            handleUpdateExpandedKeys,\n            openKeys,\n            CaretDownOutline\n        }\n    }\n})\n</script>"
  },
  {
    "path": "ginyi-vue3/src/layout/tabs/index.vue",
    "content": "<template>\n    <div style=\"display: flex; align-items: center;\">\n        <n-button strong secondary circle size=\"tiny\" @click=\"onBothSideIcons('left')\" style=\"margin-right: 8px\">\n            <template #icon>\n                <n-icon :component=\"ArrowBackOutline\" />\n            </template>\n        </n-button>\n        <div class=\"tabsView\" id=\"tabsView\">\n            <div id=\"tabsTransition\">\n                <transition-group name=\"tab\" tag=\"div\" class=\"tabs-transition\">\n                    <div :id=\"'tabView_' + item.id\" :class=\"item.id === tabIndex ? 'tabs-active' : 'tabs'\"\n                         v-for=\"(item, index) in tabsList\"\n                         :key=\"item.id\"\n                         :style=\"{color: getTheme ||  item.id === tabIndex ? activeFontColor :  null,\n                                  backgroundColor: item.id === tabIndex ? useHexToRgba(activeBackgroundColor) : null}\"\n                         @click=\"onClickTag(item)\"\n                         @contextmenu=\"handleContextMenu($event, item, index)\">\n                        <div class=\"tabs-title\">\n                            <n-icon v-if=\"item.icon\">\n                                <Icon :icon=\"item.icon\"/>\n                            </n-icon>\n                            <span>{{ item.tabName }}</span>\n                            <n-icon style=\"border-radius: 50%\" :component=\"CloseOutline\" v-if=\"index !== 0\"\n                                    @click.stop=\"() => removeTab(item.id, index)\"\n                                    :id=\"`tabs-close-${item.id}`\">\n                            </n-icon>\n                        </div>\n                        <svg :id=\"'tabLeftSvg_' + item.id\"\n                             :class=\"item.id === tabIndex ? 'tabs-active-before' : 'tabs-before'\" width=\"7\" height=\"7\"\n                             :style=\"{fill: item.id === tabIndex ? useHexToRgba(activeBackgroundColor) : 'transparent'}\">\n                            <path d=\"M 0 7 A 7 7 0 0 0 7 0 L 7 7 Z\"/>\n                        </svg>\n                        <svg :id=\"'tabRightSvg_' + item.id\"\n                             :class=\"item.id === tabIndex ? 'tabs-active-after' : 'tabs-after'\" width=\"7\" height=\"7\"\n                             :style=\"{fill: item.id === tabIndex ? useHexToRgba(activeBackgroundColor) : 'transparent'}\">\n                            <path d=\"M 0 7 A 7 7 0 0 0 7 0 L 7 7 Z\"></path>\n                        </svg>\n                    </div>\n                </transition-group>\n            </div>\n        </div>\n        <n-button strong secondary circle size=\"tiny\" @click=\"onBothSideIcons('right')\" style=\"margin: 0 8px\">\n            <template #icon>\n                <n-icon :component=\"ArrowForwardOutline\" />\n            </template>\n        </n-button>\n        <n-button strong secondary circle size=\"tiny\" @click=\"location\">\n            <template #icon>\n                <n-icon :component=\"LocateOutline\" />\n            </template>\n        </n-button>\n    </div>\n    <n-dropdown\n        placement=\"bottom-start\"\n        trigger=\"manual\"\n        :x=\"xRef\"\n        :y=\"yRef\"\n        :options=\"dropOptions\"\n        :show=\"showDropdownRef\"\n        :on-clickoutside=\"() => showDropdownRef = false\"\n        @select=\"handleSelect\"\n    />\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, nextTick, onMounted, ref, watchEffect} from \"vue\";\nimport {\n    ChevronBack,\n    ChevronForward,\n    CloseCircleOutline,\n    CloseOutline,\n    CodeWorking,\n    GameController,\n    LocateOutline,\n    PlaySkipBack,\n    PlaySkipForward,\n    ArrowBackOutline,\n    ArrowForwardOutline,\n} from '@vicons/ionicons5'\nimport {storeToRefs} from \"pinia\";\nimport {useSystemStore} from \"@/store/modules/useSystemStore\";\nimport {useHexToRgba} from \"@/hooks/useColor\";\nimport {useRenderIcon} from \"@/plugins/naive-ui/common\";\nimport {useCommonRouter} from \"@/router\";\n\nexport default defineComponent({\n    name: \"TabsView\",\n    components: {\n        CloseOutline, GameController, CodeWorking,\n        ChevronBack, ChevronForward, LocateOutline,\n        PlaySkipForward, PlaySkipBack, CloseCircleOutline,\n    },\n    setup() {\n        const {getTheme, themeColor, clientWidth, tabIndex, tabsList} = storeToRefs(useSystemStore());\n        // 选中的tag颜色\n        const activeBackgroundColor = ref<string | undefined | null>(useHexToRgba(themeColor?.value as string));\n        // 选中的文字颜色\n        const activeFontColor = themeColor\n\n        // 右击下拉菜单\n        const dropOptions = ref<Array<{ label: string, key: string, icon?: Function }>>([\n            {\n                label: '关闭其他',\n                key: 'other',\n                icon: useRenderIcon(CodeWorking)\n            },\n            {\n                label: '关闭全部',\n                key: 'all',\n                icon: useRenderIcon(CloseCircleOutline)\n            },\n            {\n                label: '关闭左侧',\n                key: 'left',\n                icon: useRenderIcon(PlaySkipBack)\n            },\n            {\n                label: '关闭右侧',\n                key: 'right',\n                icon: useRenderIcon(PlaySkipForward)\n            }\n        ])\n        const whichIndex = ref<number | undefined>(undefined)\n        const showDropdownRef = ref<boolean>(false)\n        const xRef = ref<number>(0)\n        const yRef = ref<number>(0)\n\n\n        watchEffect(() => {\n            activeBackgroundColor.value = themeColor?.value\n        })\n\n        // 初始化选中的颜色\n        const initActiveBackgroundColor = () => {\n            const tabsActive = document.getElementsByClassName(\"tabs-active\")[0];\n            const tabsBeforeActive = document.getElementsByClassName(\"tabs-active-before\")[0];\n            const tabsAfterActive = document.getElementsByClassName(\"tabs-active-after\")[0];\n\n            if (tabsActive instanceof HTMLElement) {\n                tabsActive.style.backgroundColor = useHexToRgba(activeBackgroundColor.value as string);\n            }\n            if (tabsBeforeActive instanceof SVGElement) {\n                tabsBeforeActive.style.fill = useHexToRgba(activeBackgroundColor.value as string);\n            }\n            if (tabsAfterActive instanceof SVGElement) {\n                tabsAfterActive.style.fill = useHexToRgba(activeBackgroundColor.value as string);\n            }\n        }\n        // 初始化鼠标事件\n        const initMouseEvent = (index?: number) => {\n            tabsList?.value?.forEach((item, key) => {\n                const tagsView = document.getElementById(`tabView_${item.id}`);\n                if (tagsView) {\n                    const childNodes = tagsView.childNodes;\n                    // 给 tagsView 添加鼠标【到达】事件监听\n                    tagsView.onmouseover = () => {\n                        tagsView.style.backgroundColor = useHexToRgba(activeBackgroundColor.value as string);\n                        tagsView.style.color = activeFontColor?.value as string\n                        childNodes.forEach(child => {\n                            if (child.nodeName.toUpperCase() === \"SVG\" && child instanceof SVGElement) {\n                                child.style.fill = useHexToRgba(activeBackgroundColor.value as string);\n                            }\n                        })\n                    }\n                    // 给 tagsView 添加鼠标【离开】事件监听\n                    tagsView.onmouseout = () => {\n                        if (tagsView.id.split(\"_\")[1] !== tabIndex?.value?.toString()) {\n                            tagsView.style.backgroundColor = \"\";\n                            tagsView.style.color = getTheme.value !== undefined ? activeFontColor?.value as string : \"\";\n                        }\n                        childNodes.forEach(child => {\n                            if (child.nodeName === \"svg\" && child instanceof SVGElement) {\n                                if (tagsView.id.split(\"_\")[1] !== tabIndex?.value?.toString()) {\n                                    child.style.fill = \"transparent\";\n                                }\n                            }\n                        })\n                    }\n\n                    // 关闭icon的鼠标监听事件\n                    const closeIcon = document.getElementById(`tabs-close-${item.id}`);\n                    if (closeIcon) {\n                        closeIcon.onmouseover = () => {\n                            closeIcon.style.backgroundColor = useHexToRgba(activeBackgroundColor.value as string, 0.3);\n                            closeIcon.style.opacity = \"0.5\"\n                        }\n                        closeIcon.onmouseout = () => {\n                            closeIcon.style.backgroundColor = \"\"\n                            closeIcon.style.opacity = \"1\"\n                        }\n                    }\n\n                }\n            })\n\n            const leftIcon = document.getElementsByClassName(\"both-icon\")[0];\n            const rightIcon = document.getElementsByClassName(\"both-icon\")[1];\n            const locationIcon = document.getElementsByClassName(\"both-icon\")[2];\n            [leftIcon, rightIcon, locationIcon].forEach(icon => {\n                if (icon instanceof HTMLElement) {\n                    icon.onmouseover = () => {\n                        icon.style.color = activeBackgroundColor.value as string;\n                    }\n                    icon.onmouseout = () => {\n                        icon.style.color = \"\"\n                    }\n                }\n            })\n        }\n\n        const onClickTab = (item: any) => {\n            useSystemStore().setTagIndex(item.id);\n            initMouseEvent(tabIndex?.value);\n            useCommonRouter(item.tabKey)\n        }\n\n        const removeTab = (tabId: number, index: number) => {\n            useSystemStore().removeTab(tabId, index)\n        }\n        // tabsView两边的icon\n        const onBothSideIcons = (type: string) => {\n            const tabsView = document.getElementById(\"tabsView\");\n            if (tabsView) {\n                const offset = tabsView.scrollLeft\n                if (type === \"right\") {\n                    tabsView.scrollTo({left: offset + 500, behavior: 'smooth'});\n                }\n                if (type === \"left\") {\n                    tabsView.scrollTo({left: offset - 500, behavior: 'smooth'});\n                }\n            }\n        }\n        // 定位当前\n        const location = () => {\n            const tabsList = document.getElementsByClassName(\"tabs-transition\")[0];\n            if (tabsList && tabsList instanceof HTMLElement) {\n                (tabsList.childNodes || [])?.forEach(tab => {\n                    if (tab instanceof HTMLElement && (tab.id === `tabView_${tabIndex?.value}`)) {\n                        tab.scrollIntoView && tab.scrollIntoView({\n                            inline: \"center\",\n                            behavior: \"smooth\",\n                        });\n                    }\n                })\n            }\n        }\n\n        // 右击\n        const handleContextMenu = (e: MouseEvent, item: any, index: number) => {\n            e.preventDefault()\n            showDropdownRef.value = false\n            nextTick().then(() => {\n                showDropdownRef.value = true\n                xRef.value = e.clientX\n                yRef.value = e.clientY\n                whichIndex.value = index\n            })\n        }\n\n        const handleSelect = (key: string) => {\n            showDropdownRef.value = false\n            switch (key) {\n                case \"right\":\n                    useSystemStore().removeRightTabs(whichIndex.value as number);\n                    break;\n                case \"left\":\n                    useSystemStore().removeLeftTabs(whichIndex.value as number);\n                    break;\n                case \"other\":\n                    useSystemStore().removeOtherTabs(whichIndex.value as number);\n                    break;\n                case \"all\":\n                    useSystemStore().removeAllTabs();\n                    break;\n            }\n        }\n\n        onMounted(() => {\n            initActiveBackgroundColor();\n            initMouseEvent(tabIndex?.value);\n        })\n\n        return {\n            getTheme,\n            activeBackgroundColor,\n            activeFontColor,\n            CloseOutline, CloseCircleOutline,\n            ChevronBack, ChevronForward, LocateOutline,\n            PlaySkipForward, PlaySkipBack, CodeWorking, ArrowBackOutline, ArrowForwardOutline,\n            onClickTag: onClickTab,\n            useHexToRgba,\n            removeTab,\n            tabIndex,\n            tabsList,\n            onBothSideIcons,\n            location,\n            dropOptions,\n            showDropdownRef,\n            xRef,\n            yRef,\n            handleContextMenu,\n            handleSelect\n        }\n    }\n})\n</script>\n\n<style lang=\"less\">\n.tabsView {\n    width: 100%;\n    display: flex;\n    align-items: center;\n    padding: 0 5px 0 5px;\n    overflow: hidden;\n\n    .tabs-transition {\n        display: flex;\n\n        .tabs {\n            height: 30px;\n            position: relative;\n            border-radius: 8px 8px 0 0;\n            display: flex;\n            align-items: center;\n            justify-content: space-between;\n\n            &-title {\n                width: 100%;\n                height: 15px;\n                display: flex;\n                align-items: center;\n                padding: 0 6px 0 10px;\n\n                span {\n                    margin: 0 10px 0 5px;\n                    user-select: none;\n                    white-space: nowrap;\n                }\n            }\n\n            &-before {\n                position: absolute;\n                left: -7px;\n                bottom: 0;\n            }\n\n            &-after {\n                position: absolute;\n                right: -7px;\n                bottom: 0;\n                transform: rotate(90deg);\n            }\n        }\n\n        .tabs-active {\n            height: 30px;\n            position: relative;\n            border-radius: 8px 8px 0 0;\n            display: flex;\n            align-items: center;\n            justify-content: space-between;\n\n            &-before {\n                position: absolute;\n                left: -7px;\n                bottom: 0;\n            }\n\n            &-after {\n                position: absolute;\n                right: -7px;\n                bottom: 0;\n                transform: rotate(90deg);\n            }\n        }\n\n        .tabs-active:hover {\n            cursor: pointer;\n        }\n\n        .tabs:hover {\n            cursor: pointer;\n        }\n\n        /* 多标签组件过渡动画 */\n        .tab-move,\n        .tab-enter-active,\n        .tab-leave-active {\n            transition: all 0.3s ease;\n        }\n\n        .tab-enter-from,\n        .tab-leave-to {\n            opacity: 0;\n            transform: translateX(-30px);\n        }\n\n        .tab-leave-active {\n            position: absolute;\n        }\n    }\n}\n\n\n</style>\n"
  },
  {
    "path": "ginyi-vue3/src/layout/theme/index.vue",
    "content": "<template>\n    <n-popover placement=\"top-end\" trigger=\"click\" :arrow-point-to-center=\"true\">\n        <template #trigger>\n            <div style=\"position: fixed; bottom: 55px; right:15px; cursor: pointer\">\n                <n-card style=\"border-radius: 50%; width: 50px; height: 50px; position: relative\">\n                    <n-icon style=\"position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%)\" :size=\"30\"\n                            :component=\"SparklesOutline\"/>\n                </n-card>\n            </div>\n        </template>\n        <div style=\"width: 350px; height: 600px;\">\n            <span style=\"font-size: 20px\">主题配置</span>\n            <n-divider title-placement=\"center\">系统主题</n-divider>\n            <div style=\"display: flex; justify-content: center\" class=\"dark-switch\">\n                <n-switch v-model:value=\"darkTheme\">\n                    <template #checked>\n                        <n-icon size=\"14\" color=\"#ffd93b\" background=\"#212126\">\n                            <SunnySharp/>\n                        </n-icon>\n                    </template>\n                    <template #unchecked>\n                        <n-icon size=\"14\" color=\"#ffd93b\" background=\"#212126\">\n                            <Moon/>\n                        </n-icon>\n                    </template>\n                </n-switch>\n            </div>\n            <n-divider title-placement=\"center\">系统主题色</n-divider>\n            <div class=\"theme-box\">\n                <div class=\"theme-box-item\"\n                     v-for=\"(color, index) in themeColorList\" :key=\"index\" :style=\"{ 'background-color': color }\"\n                     @click=\"chooseColor(color)\">\n                    <n-icon size=\"24\" v-if=\"color === themeColor\" color=\"white\">\n                        <Checkmark/>\n                    </n-icon>\n                </div>\n                <n-color-picker v-model:value=\"themeColor\" :show-alpha=\"false\"/>\n            </div>\n        </div>\n    </n-popover>\n</template>\n\n<script lang=\"ts\">\nimport {Checkmark, Moon, SparklesOutline, SunnySharp} from '@vicons/ionicons5'\nimport {defineComponent} from \"vue\";\nimport {storeToRefs} from \"pinia\";\nimport {useSystemStore} from \"@/store/modules/useSystemStore\";\n\nexport default defineComponent({\n    name: \"Theme\",\n    components: {\n        Moon,\n        SunnySharp,\n        SparklesOutline,\n        Checkmark\n    },\n    setup() {\n        const {darkTheme, themeColor, themeColorList} = storeToRefs(useSystemStore())\n        const chooseColor = (color: string) => {\n            useSystemStore().setThemeColor(color)\n        }\n        return {\n            darkTheme,\n            themeColor,\n            themeColorList,\n            chooseColor,\n            Moon, SunnySharp, SparklesOutline, Checkmark\n        }\n    }\n})\n</script>\n\n<style scoped lang=\"less\">\n.dark-switch .n-switch {\n    ::v-deep(.n-switch__rail) {\n        background-color: #000e1c;\n    }\n}\n\n.theme-box {\n    width: 100%;\n    display: flex;\n    justify-content: center;\n    flex-wrap: wrap;\n\n    &-item {\n        width: 27px;\n        height: 27px;\n        margin: 4px;\n        display: flex;\n        cursor: pointer;\n    }\n}\n</style>"
  },
  {
    "path": "ginyi-vue3/src/main.ts",
    "content": "import {createApp} from 'vue'\nimport App from '@/App.vue'\nimport router from \"@/router\";\nimport naive from 'naive-ui';\nimport {initStore} from '@/store';\nimport \"@/plugins/naive-ui/common\"\nimport \"@/config/console.log\";\nimport {initDirectives} from \"@/directives\";\nimport {initIcon} from \"@/plugins/naive-ui/common\";\nimport \"@/style/index.css\"\n\nconst app = createApp(App)\ninitStore(app)\ninitDirectives(app)\ninitIcon(app)\n\napp.use(router)\napp.use(naive)\napp.mount('#app')\n"
  },
  {
    "path": "ginyi-vue3/src/plugins/naive-ui/common.tsx",
    "content": "import type {App} from \"vue\";\nimport {Component, computed, createVNode} from \"vue\";\nimport {createDiscreteApi, NIcon} from \"naive-ui\";\nimport * as Icons from \"@vicons/ionicons5\";\nimport {useSystemStore} from \"@/store/modules/useSystemStore\";\nimport {storeToRefs} from \"pinia\";\nimport {useLighten} from \"@/hooks/useColor\";\n\n/**\n * 加延时是为了让 pinia 完成初始化，时间可适当调长些\n */\nsetTimeout(() => {\n    const {getTheme, themeColor} = storeToRefs(useSystemStore())\n    const configProviderPropsRef = computed(() => ({\n        theme: getTheme.value,\n        themeOverrides: {\n            common: {\n                primaryColor: themeColor?.value,\n                primaryColorHover: useLighten(themeColor?.value as string, 6),\n                primaryColorPressed: useLighten(themeColor?.value as string, 6),\n            },\n            LoadingBar: {\n                colorLoading: themeColor?.value as string,\n            },\n        },\n    }));\n    const {message, dialog, notification, loadingBar} = createDiscreteApi(\n        [\"message\", \"dialog\", \"notification\", \"loadingBar\"],\n        {\n            configProviderProps: configProviderPropsRef,\n        }\n    );\n\n    window.$message = message;\n    window.$dialog = dialog;\n    window.$notification = notification;\n    window.$loading = loadingBar;\n}, 100)\n\n\n/**\n * 加载图标，要配合【 renderIcon 】一起使用\n * @param icon\n */\nexport const useLoadIcon = (icon: string) => {\n    return Icons[icon as keyof typeof Icons];\n}\n\n/**\n * 渲染icon，不一定要配合【 loadIcon 】，可以单独使用\n * @param icon\n */\nexport const useRenderIcon = (icon: Component) => {\n    return () => {\n        return <NIcon component={icon}/>\n    }\n}\n\nconst icon = (props: { icon: string }) => {\n    const {icon} = props;\n    return createVNode(Icons[icon as keyof typeof Icons]);\n};\n\nexport function initIcon(app: App<Element>): void {\n    app.component('Icon', icon);\n}"
  },
  {
    "path": "ginyi-vue3/src/plugins/pinia/piniaPlugin.ts",
    "content": "import {PiniaPluginContext} from \"pinia\";\nimport {storage} from \"@/hooks/useStorage\";\nimport {toRaw} from \"vue\";\n\n/**\n * 做状态持久化\n * @param key\n */\nexport const piniaPlugin = (key?: string) => {\n    const piniaKey: string = 'PiniaKey'\n    return (context: PiniaPluginContext) => {\n        const {store} = context\n        const data = storage.get(`${key ?? piniaKey}-${store.$id}`)\n        // 每次 pinia 中的状态发生改变时，都会执行 $subscribe\n        store.$subscribe(() => {\n            // store.$state 需要转换成普通对象，可以console.log看看\n            storage.set(`${key ?? piniaKey}-${store.$id}`, toRaw(store.$state))\n        })\n        // 即将 data 解构，后重新赋值给 pinia 的 state，实现持久化\n        return {\n            ...data\n        }\n    }\n}"
  },
  {
    "path": "ginyi-vue3/src/router/index.ts",
    "content": "import {createRouter, createWebHashHistory, RouteRecordRaw} from \"vue-router\";\nimport {useUserStore} from \"@/store/modules/useUserStore\";\nimport {storeToRefs} from \"pinia\";\nimport {storage} from \"@/hooks/useStorage\";\nimport {store} from \"@/store\";\nimport {setting} from \"@/config/setting\";\nimport {useRouterStore} from \"@/store/modules/useRouterStore\";\n\n\n/**\n * 系统路由\n */\nconst routes: Array<RouteRecordRaw> = [\n    {\n        path: \"/\",\n        name: \"Layout\",\n        redirect: \"/home\",\n        component: () => import(\"@/layout/index.vue\"),\n        children: [\n            {\n                path: \"home\",\n                name: \"home\",\n                meta: {title: \"首页\"},\n                component: () => import(\"@/views/home/index.vue\")\n            }\n        ]\n    },\n    {\n        path: \"/login\",\n        name: \"login\",\n        meta: {title: \"登录\"},\n        component: () => import(\"@/views/login/index.vue\")\n    }\n]\n\nconst router = createRouter({\n    history: createWebHashHistory(),\n    routes\n})\n\nrouter.beforeEach((to, from, next) => {\n    window.$loading && window.$loading.start();\n    document.title = `${setting.title} - ${to.meta.title as string}`\n    const {authorization, tokenKey} = storeToRefs(useUserStore(store));\n    const token = authorization?.value ?? storage.get(tokenKey?.value as string);\n\n    if (to.name === \"login\") {\n        if (token) {\n            window.$dialog.warning({\n                title: \"温馨提醒\",\n                content: \"当前状态【已登录】，是否退出登录回到登录页面？\",\n                positiveText: \"确定\",\n                negativeText: \"取消\",\n                onPositiveClick: () => {\n                    useUserStore(store).logout().then(() => {\n                        useRouterStore(store).$reset()\n                        next({name: to.name as string})\n                        window.$message.success(\"退出成功\")\n                    })\n                },\n                onNegativeClick: () => {\n                    next({name: from.name as string})\n                },\n                onMaskClick: () => {\n                    next({name: from.name as string})\n                },\n                onClose: () => {\n                    next({name: from.name as string})\n                },\n                onEsc: () => {\n                    next({name: from.name as string})\n                }\n            })\n        } else {\n            next()\n        }\n    } else {\n        if (token) {\n            // 注入路由\n            useRouterStore(store).addRoutes()\n            if (!router.hasRoute(to.name as string)) {\n                next({...to, replace: true});\n            } else {\n                next();\n            }\n        } else {\n            next({name: \"login\"})\n        }\n    }\n\n})\n\nrouter.afterEach(() => {\n    window.$loading && window.$loading.finish();\n})\n\n/**\n * 封装路由跳转\n * @param name 路由name\n * @param query 路由参数\n */\nexport const useCommonRouter = (name: string, query?: any) => {\n    router.push({\n        name: name,\n        query: query\n    })\n}\n\nexport default router"
  },
  {
    "path": "ginyi-vue3/src/store/index.ts",
    "content": "import type {App} from 'vue';\nimport {createPinia} from 'pinia';\nimport {piniaPlugin} from \"@/plugins/pinia/piniaPlugin\";\n\nconst store = createPinia();\nstore.use(piniaPlugin())\n\nexport const initStore = (app: App<Element>) => {\n    app.use(store);\n}\n\nexport {store};"
  },
  {
    "path": "ginyi-vue3/src/store/modules/useRouterStore.ts",
    "content": "import {defineStore} from \"pinia\";\nimport {storeKeyEnums} from \"@/enums/storeKeyEnums\";\nimport {IRouterType} from \"@/interface/modules/system\";\nimport {RouteRecordRaw} from \"vue-router\";\nimport router from \"@/router\";\n\n\nexport const useRouterStore = defineStore(storeKeyEnums.ROUTER, {\n    state: (): IRouterType => ({\n        routesList: [],\n        routesKeepAliveList: []\n    }),\n    actions: {\n        setRoutesList(data: Array<any>, prevRoute?: RouteRecordRaw) {\n            data?.forEach((menu: any) => {\n                if (prevRoute) {\n                    menu.path = prevRoute ? `${prevRoute.path}/${menu.path}` : menu.path\n                }\n                // C代表菜单，其余是目录和按钮\n                if (menu.menuType.toUpperCase() === \"C\") {\n                    menu.meta = {\n                        title: menu.menuName,\n                        keepAlive: menu.isCache === \"0\"\n                    }\n                    this.routesList?.push(menu);\n                }\n                if (menu.name && menu.isCache === \"0\") {\n                    this.routesKeepAliveList?.push(menu.name);\n                }\n                if (menu.children?.length > 0) {\n                    this.setRoutesList(menu.children, menu)\n                }\n            })\n        },\n        addRoutes() {\n            const modules = import.meta.glob('../../views/**/*.vue')\n            this.routesList?.forEach(route => {\n                const temp: RouteRecordRaw = {\n                    path: route.path,\n                    name: route.name,\n                    meta: route.meta,\n                    component: modules[`../../views/${route.component}.vue`]\n                }\n                router.addRoute(\"Layout\", temp)\n            })\n            router.addRoute(\"Layout\", {\n                path: \"/:path(.*)\",\n                name: \"404\",\n                component: () => import(\"@/views/404/index.vue\")\n            })\n        }\n    }\n})"
  },
  {
    "path": "ginyi-vue3/src/store/modules/useSystemStore.ts",
    "content": "import {defineStore} from \"pinia\";\nimport {storeKeyEnums} from \"@/enums/storeKeyEnums\";\nimport {darkTheme} from \"naive-ui\";\nimport {BuiltInGlobalTheme} from \"naive-ui/es/themes/interface\";\nimport {ISystemState, ITabType} from \"@/interface/modules/system\";\nimport {useCommonRouter} from \"@/router\";\n\nexport const useSystemStore = defineStore(storeKeyEnums.SYSTEM, {\n    state: (): ISystemState => ({\n        darkTheme: false,\n        themeColor: themeColorList[0],\n        themeColorList: themeColorList,\n        clientHeight: document.body.clientHeight,\n        clientWidth: document.body.clientWidth,\n        layoutHeaderHeight: 58,\n        layoutFooterHeight: 40,\n        collapsed: false,\n        logoHeight: 60,\n        tabsHeight: 20,\n        tabIndex: tabsList[0].id,\n        tabsList: tabsList,\n        menuList: [],\n        breadMenuList: []\n    }),\n    getters: {\n        getTheme(): BuiltInGlobalTheme | undefined {\n            return this.darkTheme ? darkTheme : undefined\n        },\n        getCollapsed(): boolean | undefined {\n            return this.collapsed\n        },\n        getMenuList(): any {\n            return this.menuList\n        }\n    },\n    actions: {\n        setThemeColor(data: string | undefined) {\n            this.themeColor = data\n        },\n        setClientHeight(data: number | undefined) {\n            this.clientHeight = data\n        },\n        setClientWidth(data: number | undefined) {\n            this.clientWidth = data\n        },\n        setCollapsed(data: boolean | undefined) {\n            this.collapsed = data\n        },\n        setTagIndex(data: number | undefined) {\n            this.tabIndex = data\n        },\n        addTab(data: ITabType) {\n            const exist = this.tabsList?.some(tab => {\n                return tab.id === data.id\n            })\n            if (!exist) {\n                this.tabsList?.push(data)\n            }\n            this.tabIndex = data.id\n\n            setTimeout(() => {\n                const currentTab = document.getElementById(`tabView_${this.tabIndex}`)\n                if (currentTab instanceof HTMLElement) {\n                    currentTab.scrollIntoView && currentTab.scrollIntoView({\n                        inline: \"center\",\n                        behavior: \"smooth\",\n                    });\n                }\n            }, 50)\n        },\n        removeTab(tabId: number, index: number) {\n            this.tabsList?.splice(index, 1)\n            if (this.tabsList && tabId === this.tabIndex) {\n                this.tabIndex = this.tabsList[0].id\n                useCommonRouter(this.tabsList[0].tabKey)\n            }\n        },\n        removeLeftTabs(index: number) {\n            this.tabsList?.splice(1, index - 1)\n            const closeSelf = this.tabsList?.some(tab => {\n                return tab.id === this.tabIndex\n            })\n            if (!closeSelf && this.tabsList) {\n                this.tabIndex = this.tabsList[0].id\n                useCommonRouter(this.tabsList[0].tabKey)\n            }\n        },\n        removeRightTabs(index: number) {\n            this.tabsList?.splice(index + 1, this.tabsList?.length - 1)\n            const closeSelf = this.tabsList?.some(tab => {\n                return tab.id === this.tabIndex\n            })\n            if (!closeSelf && this.tabsList) {\n                this.tabIndex = this.tabsList[0].id\n                useCommonRouter(this.tabsList[0].tabKey)\n            }\n        },\n        removeOtherTabs(index: number) {\n            this.tabsList = this.tabsList?.filter((tab, key) => {\n                return key === 0 || key === index\n            })\n            const closeSelf = this.tabsList?.some(tab => {\n                return tab.id === this.tabIndex\n            })\n            if (!closeSelf && this.tabsList) {\n                this.tabIndex = this.tabsList[0].id\n                useCommonRouter(this.tabsList[0].tabKey)\n            }\n        },\n        removeAllTabs() {\n            this.tabsList = this.tabsList?.filter((tab, key) => {\n                return key === 0\n            })\n            if (this.tabsList) {\n                this.tabIndex = this.tabsList[0].id\n                useCommonRouter(this.tabsList[0].tabKey)\n            }\n        },\n        setMenuList(data: any) {\n            this.menuList = data\n            const hasHome = this.menuList?.some(menu => {\n                return menu.name === \"home\"\n            })\n            const has404 = this.menuList?.some(menu => {\n                return menu.name === \"404\"\n            })\n            if (!hasHome) {\n                this.menuList?.unshift({\n                    menuId: 0,\n                    path: \"home\",\n                    name: \"home\",\n                    icon: \"Home\",\n                    menuName: \"首页\",\n                    component: \"home/index\",\n                    menuType: \"C\",\n                })\n            }\n            if (!has404) {\n                this.menuList?.push({\n                    menuId: 199999,\n                    path: \"404\",\n                    name: \"404\",\n                    icon: \"Bug\",\n                    show: false,\n                    menuName: \"404\",\n                    component: \"404/index\",\n                    menuType: \"C\",\n                })\n            }\n        },\n        setBreadMenuList(data: Array<any>) {\n            this.breadMenuList = []\n            // 延时主要是为了配合动画效果\n            setTimeout(() => {\n                this.breadMenuList = data\n            }, 0)\n        },\n        resetBreadMenuList() {\n            this.breadMenuList = [{\n                menuId: 0,\n                path: \"home\",\n                name: \"home\",\n                icon: \"Home\",\n                menuName: \"首页\",\n                component: \"home/index\",\n                menuType: \"C\",\n            }]\n        }\n    }\n})\n\nconst themeColorList: Array<string> = [\n    \"#9A53FE\",\n    \"#e88080\",\n    \"#0084f4\",\n    \"#009688\",\n    \"#536dfe\",\n    \"#ff5c93\",\n    \"#ee4f12\",\n    \"#0096c7\",\n    \"#9c27b0\",\n    \"#ff9800\",\n    \"#FF3D68\",\n    \"#00C1D4\",\n    \"#1DB85B\",\n    \"#2AB193\",\n    \"#5CC143\",\n    \"#63e2b7\",\n    \"#E61E8F\",\n    \"#E03D3D\",\n    \"#E63AAF\",\n    \"#35BA7B\",\n]\n\nconst tabsList: Array<ITabType> = [\n    {\n        id: 0,\n        tabKey: \"home\",\n        tabName: \"首页\",\n        icon: \"Home\"\n    }\n]"
  },
  {
    "path": "ginyi-vue3/src/store/modules/useUserStore.ts",
    "content": "import {defineStore} from \"pinia\";\nimport {storeKeyEnums} from \"@/enums/storeKeyEnums\";\nimport {IUser} from \"@/interface/modules/system\";\nimport {storage} from \"@/hooks/useStorage\";\nimport {userController} from \"@/api\";\nimport {useSystemStore} from \"@/store/modules/useSystemStore\";\n\nexport const useUserStore = defineStore(storeKeyEnums.USER, {\n    state: (): IUser => ({\n        username: undefined,\n        tokenKey: undefined,\n        authorization: undefined\n    }),\n    getters: {\n        getName(): string | undefined {\n            return this.username\n        },\n    },\n    actions: {\n        login(username: string | undefined, data: any): Promise<any> {\n            return new Promise((resolve, reject) => {\n                this.username = username\n                this.tokenKey = data.tokenHeader\n                this.authorization = data.token\n                storage.set(data.tokenHeader, data.token)\n                resolve(null);\n            })\n        },\n        logout(): Promise<any> {\n            return new Promise((resolve, reject) => {\n                userController.logout().then(() => {\n                    useUserStore().$reset()\n                    useSystemStore().removeAllTabs()\n                    useSystemStore().resetBreadMenuList()\n                    storage.clear()\n                    resolve(null);\n                })\n            })\n        }\n    }\n})"
  },
  {
    "path": "ginyi-vue3/src/style/index.css",
    "content": "/* 全局样式 */\n.n-card.n-card--bordered {\n    border-radius: 10px;\n}\n\n/* 滚动条的宽度 */\n::-webkit-scrollbar {\n    width: 6px;\n}\n\n/* 滚动条里面小方块 */\n::-webkit-scrollbar-thumb {\n    border-radius: 4px;\n    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);\n    background: rgba(153, 153, 153, 0.3);\n}\n\n/* 滚动条里面轨道 */\n::-webkit-scrollbar-track {\n    border-radius: 0;\n}\n\n/* 内容主体区过渡动画 */\n.fade-main-move,\n.fade-main-enter-active,\n.fade-main-leave-active {\n    transition: all 0.3s ease;\n}\n\n.fade-main-enter-from,\n.fade-main-leave-to {\n    opacity: 0;\n    transform: translateX(30px);\n}\n\n.fade-main-leave-active {\n    position: absolute;\n}\n\n/* 抽屉面板内容区 */\n.n-drawer .n-drawer-content .n-drawer-body-content-wrapper {\n    box-sizing: border-box;\n    padding: 0;\n}\n\n"
  },
  {
    "path": "ginyi-vue3/src/views/404/index.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/404.png\" alt=\"404\">\n                <img class=\"pic-404__child left\" src=\"@/assets/404/404_cloud.png\" alt=\"404\">\n                <img class=\"pic-404__child mid\" src=\"@/assets/404/404_cloud.png\" alt=\"404\">\n                <img class=\"pic-404__child right\" src=\"@/assets/404/404_cloud.png\" alt=\"404\">\n            </div>\n            <div class=\"bullshit\">\n                <div class=\"bullshit__oops\">404错误!</div>\n                <div class=\"bullshit__headline\">找不到网页！</div>\n                <div class=\"bullshit__info\">\n                    对不起，您正在寻找的页面不存在。尝试检查URL的错误，然后按浏览器上的刷新按钮或尝试在我们的应用程序中找到其他内容。\n                </div>\n                <router-link to=\"/\" class=\"bullshit__return-home\">返回首页</router-link>\n            </div>\n        </div>\n    </div>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent} from \"vue\";\n\nexport default defineComponent({\n    name: \"404\",\n    setup() {\n        return {}\n    }\n})\n</script>\n\n<style lang=\"less\" scoped>\n.wscn-http404-container {\n    transform: translate(-50%, -50%);\n    position: absolute;\n    top: 40%;\n    left: 50%;\n}\n\n.wscn-http404 {\n    position: relative;\n    width: 1200px;\n    padding: 0 50px;\n    overflow: hidden;\n    display: flex;\n    justify-content: center;\n\n    .pic-404 {\n        position: relative;\n        float: left;\n        width: 600px;\n        overflow: hidden;\n\n        &__parent {\n            width: 100%;\n        }\n\n        &__child {\n            position: absolute;\n\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\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\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\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\n    .bullshit {\n        position: relative;\n        float: left;\n        width: 300px;\n        padding: 30px 0;\n        overflow: hidden;\n\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\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\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\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\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>"
  },
  {
    "path": "ginyi-vue3/src/views/home/index.vue",
    "content": "<template>\n    <div class=\"grid-container\">\n        <n-card class=\"card item1\" :style=\"{backgroundColor: useHexToRgba(themeColor, 0.3)}\">\n            <div class=\"item1-main\">\n                <div class=\"item1-main-content\">\n                    <n-avatar round :size=\"105\" :src=\"logo\"/>\n                    <div style=\"margin-left: 20px\">\n                        <h1 style=\"font-weight: 400; margin: 5px 0\">下午好，欢迎登录！</h1>\n                        <span>做点喜欢的事情，学点不一样的知识，挺好！</span>\n                        <br>\n                        <span>努力坚持吧！希望未来咱都能笑着直面生活的全部！</span>\n                    </div>\n                </div>\n                <div class=\"item1-main-bg\"/>\n            </div>\n        </n-card>\n        <n-card class=\"card item2\" title=\"一个小目标\">\n            <n-statistic tabular-nums>\n                <n-number-animation ref=\"numberAnimationInstRef\" show-separator :from=\"0\" :to=\"100000000\"/>\n                <template #suffix>\n                    元\n                </template>\n            </n-statistic>\n        </n-card>\n        <n-card class=\"card item3\" title=\"您一共处理了\">\n            <n-statistic tabular-nums>\n                <n-number-animation ref=\"numberAnimationInstRef\" :from=\"0\" :to=\"12039\"/>\n                <template #suffix>\n                    条群消息\n                </template>\n            </n-statistic>\n        </n-card>\n        <n-card class=\"card item4\" title=\"活动剩余时间\">\n            <n-statistic tabular-nums>\n                <n-countdown ref=\"countdown\" :duration=\"86400000\" :active=\"true\"/>\n            </n-statistic>\n        </n-card>\n        <n-card class=\"card item5\" title=\"微信步数\">\n            <n-statistic tabular-nums>\n                <n-number-animation ref=\"numberAnimationInstRef\" :from=\"0\" :to=\"12039\"/>\n                <template #suffix>\n                    步\n                </template>\n            </n-statistic>\n        </n-card>\n        <n-card class=\"card item6\" title=\"好友点赞总数\">\n            <n-statistic tabular-nums>\n                <n-number-animation ref=\"numberAnimationInstRef\" :from=\"0\" :to=\"154861\"/>\n                <template #suffix>\n                    个\n                </template>\n            </n-statistic>\n        </n-card>\n        <n-card class=\"card item7\" title=\"活跃用户\">\n            <n-statistic tabular-nums>\n                <n-number-animation ref=\"numberAnimationInstRef\" :from=\"0\" :to=\"186411\"/>\n                <template #suffix>\n                    位\n                </template>\n            </n-statistic>\n        </n-card>\n        <n-card class=\"card item8\">\n            <div class=\"item8-main\">\n                <div class=\"item8-main-content\">\n                    <n-statistic tabular-nums label=\"总销售额\">\n                        <n-number-animation ref=\"numberAnimationInstRef\" show-separator :from=\"0\" :to=\"1546721546\"/>\n                        <template #suffix>\n                            元\n                        </template>\n                    </n-statistic>\n                </div>\n                <div class=\"item8-main-content\">\n                    <n-progress style=\"margin-right: 15px\" type=\"dashboard\" gap-position=\"bottom\" :percentage=\"80\"/>\n                    <n-statistic tabular-nums label=\"当月销售额占比\">\n                        <n-number-animation ref=\"numberAnimationInstRef\" show-separator :from=\"0\" :to=\"14884614\"/>\n                        <template #suffix>\n                            元\n                        </template>\n                    </n-statistic>\n                </div>\n                <div class=\"item8-main-content\">\n                    <n-progress\n                        style=\"margin: 0 8px 12px 0\"\n                        type=\"circle\"\n                        status=\"success\"\n                        :percentage=\"95\"\n                    />\n                    <n-statistic tabular-nums label=\"客户下单率\">\n                        <n-number-animation ref=\"numberAnimationInstRef\" show-separator :from=\"0\" :to=\"15984\"/>\n                        <template #suffix>\n                            （单）\n                        </template>\n                    </n-statistic>\n                </div>\n                <div class=\"item8-main-content\">\n                    <n-progress\n                        style=\"width: 120px; margin: 0 8px 12px 0\"\n                        type=\"multiple-circle\"\n                        :percentage=\"[70, 35]\"\n                    />\n                    <n-statistic tabular-nums label=\"新用户数量和占比\">\n                        <n-number-animation ref=\"numberAnimationInstRef\" show-separator :from=\"0\" :to=\"22564\"/>\n                        <template #suffix>\n                            （位）\n                        </template>\n                    </n-statistic>\n                </div>\n            </div>\n        </n-card>\n        <n-card class=\"card item9\" title=\"不知道写点什么\">\n            <n-result status=\"404\" title=\"这个位置留给你头脑风暴吧！\" description=\"生活总归带点荒谬\" />\n        </n-card>\n        <n-card class=\"card item10\" title=\"留言板\">\n            <div class=\"item11-main\">\n                <div class=\"item10-main-content\">\n                    <n-avatar round size=\"medium\" :src=\"logo\" style=\"margin-right: 15px\"/>\n                    <div style=\"width: 100%; display: flex; justify-content: space-between\">\n                        <span>做得挺好！但是客户喜欢第一版~</span>\n                        <span>2023-03-02</span>\n                    </div>\n                </div>\n                <div class=\"item10-main-content\">\n                    <n-avatar round size=\"medium\" :src=\"logo\" style=\"margin-right: 15px\"/>\n                    <div style=\"width: 100%; display: flex; justify-content: space-between\">\n                        <span>可以可以！如果这个位置再完善下是不是更好些？</span>\n                        <span>2023-03-01</span>\n                    </div>\n                </div>\n                <div class=\"item10-main-content\">\n                    <n-avatar round size=\"medium\" :src=\"logo\" style=\"margin-right: 15px\"/>\n                    <div style=\"width: 100%; display: flex; justify-content: space-between\">\n                        <span>@全体成员 2023-02-29至2023-02-31，放假3天！3月1日正常上班。</span>\n                        <span>2023-02-28</span>\n                    </div>\n                </div>\n                <div class=\"item10-main-content\">\n                    <n-avatar round size=\"medium\" :src=\"logo\" style=\"margin-right: 15px\"/>\n                    <div style=\"width: 100%; display: flex; justify-content: space-between\">\n                        <span>有个新需求！根据用户手机壳颜色自动改变APP主题色。</span>\n                        <span>2023-02-26</span>\n                    </div>\n                </div>\n            </div>\n        </n-card>\n        <n-card class=\"card item11\" title=\"好评率\">\n            <div class=\"item11-main\">\n                <div class=\"item11-main-content\">\n                    <n-avatar round size=\"medium\" :src=\"logo\" style=\"margin-right: 15px\"/>\n                    <n-progress type=\"line\" status=\"error\" :percentage=\"60\" :indicator-placement=\"'inside'\" processing/>\n                </div>\n                <div class=\"item11-main-content\">\n                    <n-avatar round size=\"medium\" :src=\"logo\" style=\"margin-right: 15px\"/>\n                    <n-progress type=\"line\" :percentage=\"78\" :indicator-placement=\"'inside'\"\n                                processing/>\n                </div>\n                <div class=\"item11-main-content\">\n                    <n-avatar round size=\"medium\" :src=\"logo\" style=\"margin-right: 15px\"/>\n                    <n-progress type=\"line\" status=\"success\" :percentage=\"98\" :indicator-placement=\"'inside'\"\n                                processing/>\n                </div>\n                <div class=\"item11-main-content\">\n                    <n-avatar round size=\"medium\" :src=\"logo\" style=\"margin-right: 15px\"/>\n                    <n-progress type=\"line\" status=\"warning\" :percentage=\"88\" :indicator-placement=\"'inside'\"\n                                processing/>\n                </div>\n            </div>\n        </n-card>\n    </div>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent} from \"vue\";\nimport {useSystemStore} from \"@/store/modules/useSystemStore\";\nimport {storeToRefs} from \"pinia\";\nimport {useHexToRgba, useLighten} from \"@/hooks/useColor\";\nimport {setting} from \"@/config/setting\";\n\nexport default defineComponent({\n    setup() {\n        const {clientHeight, layoutFooterHeight, layoutHeaderHeight, themeColor} = storeToRefs(useSystemStore())\n        const {logo} = setting;\n\n        return {\n            clientHeight, layoutFooterHeight, layoutHeaderHeight, themeColor,\n            useLighten, useHexToRgba,\n            logo\n        }\n    }\n})\n</script>\n\n<style lang=\"less\">\n\n.grid-container {\n    display: grid;\n    height: 100%;\n    width: 100%;\n\n    .item1 {\n        grid-area: aaa;\n        min-height: 180px;\n        padding: 0 !important;\n\n        .n-card__content {\n            padding: 0 !important;\n        }\n\n        &-main {\n            display: flex;\n            align-items: flex-end;\n            height: 100%;\n\n            &-content {\n                width: 600px;\n                height: 100%;\n                min-height: 150px;\n                display: flex;\n                align-items: center;\n                justify-content: center;\n            }\n\n            &-bg {\n                flex: 1;\n                height: 90%;\n                text-align: end;\n                background-size: 100% 100%;\n                background-repeat: no-repeat;\n                background-image: url(\"../../../src/assets/home-bg.svg\");\n            }\n        }\n    }\n\n    .item2 {\n        grid-area: bbb;\n        max-height: 400px;\n    }\n\n    .item3 {\n        grid-area: ccc;\n        max-height: 400px;\n    }\n\n    .item4 {\n        grid-area: ddd;\n        max-height: 400px;\n    }\n\n    .item5 {\n        grid-area: eee;\n        max-height: 400px;\n    }\n\n    .item6 {\n        grid-area: fff;\n        max-height: 400px;\n    }\n\n    .item7 {\n        grid-area: ggg;\n        max-height: 400px;\n    }\n\n    .item8 {\n        grid-area: hhh;\n        min-height: 150px;\n        &-main {\n            width: 100%;\n            height: 100%;\n            display: flex;\n            flex-direction: row;\n            align-items: center;\n\n            &-content {\n                flex: 1;\n                display: flex;\n                align-items: center;\n            }\n        }\n    }\n\n    .item9 {\n        grid-area: iii;\n        width: 100%;\n    }\n\n    .item10 {\n        grid-area: jjj;\n\n        &-main {\n            &-content {\n                display: flex;\n                align-items: center;\n                margin-bottom: 15px;\n            }\n        }\n    }\n\n    .item11 {\n        grid-area: kkk;\n\n        &-main {\n            &-content {\n                display: flex;\n                align-items: center;\n                margin-bottom: 15px;\n            }\n        }\n    }\n\n    grid-template-areas:\n                'aaa aaa aaa aaa aaa aaa'\n                'bbb ccc ddd eee fff ggg'\n                'hhh hhh hhh hhh hhh hhh'\n                'iii iii jjj jjj kkk kkk';\n    grid-gap: 10px;\n\n}\n\n</style>"
  },
  {
    "path": "ginyi-vue3/src/views/login/index.vue",
    "content": "<template>\n    <n-card class=\"login-box\" :style=\"{height: clientHeight + 'px'}\">\n        <div style=\"display: flex; justify-content: flex-end\" class=\"dark-switch\">\n            <n-switch v-model:value=\"darkTheme\">\n                <template #checked>\n                    <n-icon size=\"14\" color=\"#ffd93b\" background=\"#212126\">\n                        <SunnySharp/>\n                    </n-icon>\n                </template>\n                <template #unchecked>\n                    <n-icon size=\"14\" color=\"#ffd93b\" background=\"#212126\">\n                        <Moon/>\n                    </n-icon>\n                </template>\n            </n-switch>\n        </div>\n        <div\n            style=\"width: 400px; position: absolute; top: 40%; left: 50%; transform: translate(-50%, -50%);\n            border: 1px solid #ccc; border-radius: 8px; padding: 30px;\">\n            <n-tabs\n                class=\"card-tabs\"\n                default-value=\"signin\"\n                size=\"large\"\n                animated\n                style=\"margin: 0 -4px\"\n                pane-style=\"padding-left: 4px; padding-right: 4px; box-sizing: border-box;\">\n                <n-tab-pane name=\"signin\" tab=\"登录\">\n                    <LoginForm @doLogin=\"doLogin\" :is-success=\"isLoginSuccess\"/>\n                </n-tab-pane>\n                <n-tab-pane name=\"signup\" tab=\"注册\">\n                    <RegisterForm @doRegister=\"doRegister\"/>\n                </n-tab-pane>\n            </n-tabs>\n        </div>\n    </n-card>\n</template>\n\n<script lang=\"ts\">\nimport {useSystemStore} from \"@/store/modules/useSystemStore\";\nimport {ILoginFormType, IRegisterFormType} from \"@/interface/modules/system\";\nimport {useUserStore} from \"@/store/modules/useUserStore\";\nimport {useCommonRouter} from \"@/router\";\nimport {menuController, userController} from \"@/api\";\nimport {defineComponent, ref} from \"vue\";\nimport {storeToRefs} from \"pinia\";\nimport {useRouterStore} from \"@/store/modules/useRouterStore\";\nimport LoginForm from \"@/views/login/loginForm.vue\";\nimport {Moon, SunnySharp} from \"@vicons/ionicons5\";\nimport RegisterForm from \"@/views/login/registerForm.vue\";\n\nexport default defineComponent({\n    components: {\n        LoginForm, RegisterForm, SunnySharp, Moon\n    },\n    setup() {\n        // 系统深色主题\n        const {darkTheme, clientHeight} = storeToRefs(useSystemStore())\n        // 登录状态\n        const isLoginSuccess = ref<boolean | string>(false)\n\n        // 登录\n        const doLogin = (data: ILoginFormType) => {\n            userController.login(data).then(res => {\n                return useUserStore().login(data.username, res.data)\n            }).then(() => {\n                isLoginSuccess.value = true\n                return menuController.getRouterList()\n            }).then(res => {\n                useSystemStore().setMenuList(res.data.list)\n                useSystemStore().resetBreadMenuList()\n                useRouterStore().setRoutesList(res.data.list)\n                window.$notification.success({\n                    title: \"登录成功\",\n                    content: \"工作顺利，快乐摸鱼！\",\n                    duration: 2500,\n                })\n                useCommonRouter(\"home\")\n            }).catch(() => {\n                // 用于更新验证码\n                isLoginSuccess.value = `${false}_${new Date().valueOf()}`\n            })\n        }\n        // 注册\n        const doRegister = (data: IRegisterFormType) => {\n            console.log('注册data', data)\n        }\n        return {\n            isLoginSuccess,\n            darkTheme, clientHeight,\n            doLogin,\n            doRegister,\n            SunnySharp, Moon\n        }\n    }\n})\n\n\n</script>\n\n<style scoped lang=\"less\">\n.login-box {\n    border-radius: 0;\n    background-image: url(\"../../../src/assets/bg-light.svg\");\n    background-repeat: no-repeat;\n    background-size: 100% 100%;\n}\n\n.dark-switch .n-switch {\n    ::v-deep(.n-switch__rail) {\n        background-color: #000e1c;\n    }\n}\n</style>"
  },
  {
    "path": "ginyi-vue3/src/views/login/loginForm.vue",
    "content": "<template>\n    <n-form label-placement=\"left\" label-width=\"auto\">\n        <n-form-item-row label=\"用户名\">\n            <n-input v-model:value=\"loginForm.username\" v-focus @keyup.enter.native=\"handleLogin\" placeholder=\"用户名\" clearable>\n                <template #prefix>\n                    <n-icon :component=\"Person\"/>\n                </template>\n            </n-input>\n        </n-form-item-row>\n        <n-form-item-row label=\"密码\">\n            <n-input v-model:value=\"loginForm.password\"\n                     type=\"password\" @keyup.enter.native=\"handleLogin\"\n                     show-password-on=\"mousedown\" placeholder=\"密码\"\n                     clearable>\n                <template #prefix>\n                    <n-icon :component=\"BagOutline\"/>\n                </template>\n            </n-input>\n        </n-form-item-row>\n        <n-form-item-row label=\"验证码\">\n            <n-input-group>\n                <n-input v-model:value=\"loginForm.code\" @keyup.enter.native=\"handleLogin\" placeholder=\"验证码\" clearable>\n                    <template #prefix>\n                        <n-icon :component=\"QrCode\"/>\n                    </template>\n                </n-input>\n                <img :src=\"`data:image/png;base64,${captchaCode ?? null}`\"\n                     @click=\"() => getCaptcha()\"\n                     style=\"width: 120px; height: 34px; cursor: pointer\"\n                     alt=\"重新获取\">\n            </n-input-group>\n        </n-form-item-row>\n    </n-form>\n    <n-button type=\"primary\" block :loading=\"loading\" @click=\"handleLogin\">登 录</n-button>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, onMounted, reactive, ref, watchEffect} from \"vue\";\nimport {BagOutline, Moon, Person, QrCode, SunnySharp} from '@vicons/ionicons5';\nimport type {ILoginFormType} from \"@/interface/modules/system\";\nimport {userController} from \"@/api\";\n\nexport default defineComponent({\n    name: \"LoginForm\",\n    emits: [\"doLogin\"],\n    props: {\n        isSuccess: {\n            type: [Boolean, String],\n            default: false\n        }\n    },\n    setup(props, {emit}) {\n        // 加载中\n        const loading = ref<boolean>(false)\n        // 验证码\n        const captchaCode = ref<string | undefined>(undefined)\n        const loginForm = reactive<ILoginFormType>({\n            username: \"Ginyi\",\n            password: \"123456\",\n            code: undefined\n        })\n        // 登录\n        const handleLogin = () => {\n            loading.value = true\n            emit(\"doLogin\", loginForm)\n        }\n        // 获取验证码\n        const getCaptcha = () => {\n            userController.captcha().then(res => {\n                captchaCode.value = res.data.img\n            })\n        }\n        // 监听登录状态，失败则更新验证码\n        watchEffect(() => {\n            loading.value = false\n            if (props.isSuccess !== true) {\n                getCaptcha()\n            }\n        })\n\n        return {\n            loading,\n            loginForm,\n            captchaCode,\n            handleLogin,\n            getCaptcha,\n            BagOutline, Moon, Person, QrCode, SunnySharp\n        }\n    }\n})\n</script>\n\n<style scoped>\n\n</style>"
  },
  {
    "path": "ginyi-vue3/src/views/login/registerForm.vue",
    "content": "<template>\n    <n-form label-placement=\"left\" label-width=\"auto\">\n        <n-form-item-row label=\"用户名\">\n            <n-input placeholder=\"用户名\" v-model:value=\"registerForm.username\"\n                     @keyup.enter.native=\"handleRegister\" clearable>\n                <template #prefix>\n                    <n-icon :component=\"Person\"/>\n                </template>\n            </n-input>\n        </n-form-item-row>\n        <n-form-item-row label=\"密码\">\n            <n-input v-model:value=\"registerForm.password\"\n                     type=\"password\" @keyup.enter.native=\"handleRegister\"\n                     show-password-on=\"mousedown\"\n                     placeholder=\"密码\" clearable>\n                <template #prefix>\n                    <n-icon :component=\"BagOutline\"/>\n                </template>\n            </n-input>\n        </n-form-item-row>\n        <n-form-item-row label=\"重复密码\">\n            <n-input v-model:value=\"registerForm.password2\"\n                     type=\"password\" @keyup.enter.native=\"handleRegister\"\n                     show-password-on=\"mousedown\"\n                     placeholder=\"重复密码\" clearable>\n                <template #prefix>\n                    <n-icon :component=\"BagOutline\"/>\n                </template>\n            </n-input>\n        </n-form-item-row>\n        <n-form-item-row label=\"验证码\">\n            <n-input-group>\n                <n-input v-model:value=\"registerForm.code\"\n                         placeholder=\"验证码\" @keyup.enter.native=\"handleRegister\"\n                         clearable>\n                    <template #prefix>\n                        <n-icon :component=\"QrCode\"/>\n                    </template>\n                </n-input>\n                <img :src=\"`data:image/png;base64,${captchaCode ?? null}`\"\n                     @click=\"() => getCaptcha()\"\n                     style=\"width: 120px; height: 34px; cursor: pointer\"\n                     alt=\"重新获取\">\n            </n-input-group>\n        </n-form-item-row>\n    </n-form>\n    <n-button type=\"primary\" block @click=\"handleRegister\">注册</n-button>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, onMounted, reactive, ref} from \"vue\";\nimport {IRegisterFormType} from \"@/interface/modules/system\";\nimport {BagOutline, Moon, Person, QrCode, SunnySharp} from '@vicons/ionicons5';\nimport {userController} from \"@/api\";\n\nexport default defineComponent({\n    name: \"RegisterForm\",\n    emits: [\"doRegister\"],\n    setup(props, {emit}) {\n        const registerForm = reactive<IRegisterFormType>({\n            username: undefined,\n            password: undefined,\n            password2: undefined,\n            code: undefined\n        })\n        // 验证码\n        const captchaCode = ref<string | undefined>(undefined)\n\n        // 获取验证码\n        const getCaptcha = () => {\n            userController.captcha().then(res => {\n                captchaCode.value = res.data.img\n            })\n        }\n\n        onMounted(() => {\n            getCaptcha()\n        })\n\n        // 注册\n        const handleRegister = () => {\n            emit(\"doRegister\", registerForm)\n        }\n        return {\n            registerForm,\n            handleRegister,\n            captchaCode,\n            getCaptcha,\n            BagOutline, Moon, Person, QrCode, SunnySharp\n        }\n    }\n})\n</script>\n\n<style scoped>\n\n</style>"
  },
  {
    "path": "ginyi-vue3/src/views/pages/monitor/cache/index.vue",
    "content": "<template>\n    <div>\n        缓存监控\n    </div>\n</template>\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/monitor/cacheList/columns.tsx",
    "content": "import {DataTableColumns, NButton, NSpace} from \"naive-ui\";\nimport {eventBus} from \"@/config/eventBus\";\nimport {h} from \"vue\";\n\n/**\n * 查看详情的点击事件\n * @param row\n */\nconst handleClickDetails = (row: any) => {\n    eventBus.emit(\"handleClickDetails\", row)\n}\n\n/**\n * 删除的点击事件\n * @param row\n */\nconst handleClickDelete = (row: any) => {\n    eventBus.emit(\"handleClickDelete\", row)\n}\n\nexport const columns: DataTableColumns<any> = [\n    {\n        title: \"键名\",\n        key: \"key\",\n        ellipsis: {\n            tooltip: true\n        }\n    },\n    {\n        title: \"操作\",\n        key: \"action\",\n        width: 160,\n        render: (row) => (\n            h(NSpace, null, () => [\n                h(NButton, {\n                    type: \"primary\", size: \"small\", onClick: () => handleClickDetails(row)}, {default: () => \"查看详情\"}),\n                h(NButton, {type: \"error\", size: \"small\", onClick: () => handleClickDelete(row)}, {default: () => \"删除\"})\n            ])\n        )\n    }\n]"
  },
  {
    "path": "ginyi-vue3/src/views/pages/monitor/cacheList/index.vue",
    "content": "<template>\n    <div style=\"height: 100%\">\n        <n-grid x-gap=\"12\" :cols=\"5\" style=\"height: 100%\">\n            <n-grid-item span=\"2\">\n                <n-card title=\"缓存列表\" style=\"height: 100%\">\n                    <template #header-extra>\n                        <n-tooltip placement=\"top-start\" trigger=\"hover\" :show-arrow=\"false\">\n                            <template #trigger>\n                                <n-icon size=\"24\">\n                                    <ReloadOutline @click=\"reloadData\" class=\"load-icon\"/>\n                                </n-icon>\n                            </template>\n                            重新获取缓存数据\n                        </n-tooltip>\n                    </template>\n                    <n-data-table\n                        :columns=\"columns\"\n                        :data=\"tableDataList\"\n                        :size=\"'small'\"\n                        flex-height style=\"height: 100%\"\n                        :bordered=\"false\"\n                    />\n                </n-card>\n            </n-grid-item>\n            <n-grid-item span=\"3\">\n                <n-card title=\"数据详情\" style=\"height: 100%\" :content-style=\"{height: '100%'}\">\n                    <n-empty description=\"什么都没有哦~\" v-if=\"cache.key === undefined\"/>\n                    <div style=\"height: 100%; display: flex; flex-direction: column\" v-else>\n                        <n-form label-placement=\"left\">\n                            <n-form-item label=\"键名\">\n                                <n-input v-model:value=\"cache.key\" readonly></n-input>\n                            </n-form-item>\n                            <n-form-item label=\"TTL\">\n                                <n-input v-model:value=\"cache.expire\" readonly></n-input>\n                            </n-form-item>\n                        </n-form>\n                        <MonacoEditor v-model=\"rowData\"\n                                      :language=\"'json'\"\n                                      height=\"100%\"\n                                      @editorMounted=\"editorMounted\"/>\n                    </div>\n                </n-card>\n            </n-grid-item>\n        </n-grid>\n    </div>\n\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, onMounted, ref} from \"vue\";\nimport {columns} from \"@/views/pages/monitor/cacheList/columns\";\nimport {useCommonTable} from \"@/components/commonTable/useCommonTable\";\nimport {monitorController} from \"@/api\";\nimport CommonTable from \"@/components/commonTable/index.vue\";\nimport * as monaco from \"monaco-editor\";\nimport MonacoEditor from \"@/components/monacoEditor/index.vue\";\nimport {eventBus} from \"@/config/eventBus\";\nimport {ReloadOutline} from \"@vicons/ionicons5\";\n\nexport default defineComponent({\n    components: {\n        CommonTable,\n        MonacoEditor,\n        ReloadOutline\n    },\n    setup() {\n        const {\n            tableDataList,\n            getDataList,\n        } = useCommonTable(monitorController.getCacheList)\n        const rowData = ref<any>(undefined)\n        const cache = ref<any>({\n            key: undefined,\n            expire: undefined\n        })\n        const editorMounted = (editor: monaco.editor.IStandaloneCodeEditor) => {\n        }\n\n        const reloadData = () => {\n            getDataList()\n            window.$message.success(\"操作成功\")\n        }\n\n        onMounted(() => {\n            getDataList()\n            eventBus.on(\"handleClickDetails\", (row: any) => {\n                monitorController.getCacheValue(row).then(res => {\n                    cache.value.key = res.data.key\n                    cache.value.expire = res.data.expire\n                    rowData.value = JSON.stringify(res.data.value, null, \"\\t\")\n                }).catch(e => {\n                    getDataList()\n                })\n            })\n            eventBus.on(\"handleClickDelete\", (row: any) => {\n                window.$dialog.error({\n                    title: \"温馨提醒\",\n                    content: \"删除操作不可逆，是否继续？\",\n                    positiveText: \"确定\",\n                    negativeText: \"取消\",\n                    onPositiveClick: () => {\n                        monitorController.removeCache(row.key).then(res => {\n                            window.$message.success(res.msg)\n                            getDataList()\n                        })\n                    }\n                })\n            })\n        })\n\n        return {\n            columns,\n            tableDataList,\n            getDataList,\n            rowData,\n            editorMounted,\n            cache,\n            ReloadOutline,\n            reloadData,\n        }\n    }\n})\n</script>\n\n<style scoped lang=\"less\">\n.load-icon:hover {\n    cursor: pointer;\n}\n</style>\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/monitor/data/index.vue",
    "content": "<template>\n    <div>\n        数据监控\n    </div>\n</template>\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/monitor/online/columns.tsx",
    "content": "import {DataTableColumns} from \"naive-ui\";\n\n\nexport const columns: DataTableColumns<any> = [\n    {\n        title: \"会话编号\",\n        key: \"token\",\n        width: 200,\n        ellipsis: {\n            tooltip: true\n        }\n    },\n    {\n        title: \"登录名称\",\n        key: \"username\"\n    },\n    {\n        title: \"主机IP\",\n        key: \"ipaddr\",\n    },\n    {\n        title: \"登录地点\",\n        key: \"loginLocation\",\n    },\n    {\n        title: \"浏览器\",\n        key: \"browser\",\n    },\n    {\n        title: \"操作系统\",\n        key: \"os\",\n    },\n    {\n        title: \"登录时间\",\n        key: \"loginTime\",\n        width: 200\n    }\n]"
  },
  {
    "path": "ginyi-vue3/src/views/pages/monitor/online/index.vue",
    "content": "<template>\n    <div>\n        <CommonTable :columns=\"columns\"\n                     :pagination=\"tablePagination\"\n                     :loading=\"tableLoading\"\n                     :dataList=\"tableDataList\"\n                     :labelField=\"'title'\"\n                     :actionWidth=\"140\"\n                     :rowKey=\"(row) => row.token\"\n                     :scrollX=\"1200\"\n                     :buttonConfig=\"buttonConfig\"\n                     :showActionCol=\"true\"\n                     @onPageChange=\"onPageChange\"\n                     @onPageSizeChange=\"onPageSizeChange\"\n                     @onEvent=\"onEvent\">>\n        </CommonTable>\n    </div>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, onMounted, reactive} from \"vue\";\nimport CommonTable from \"@/components/commonTable/index.vue\";\nimport {useCommonTable} from \"@/components/commonTable/useCommonTable\";\nimport {onlineController} from \"@/api\";\nimport {columns} from \"@/views/pages/monitor/online/columns\";\nimport {IButtonType} from \"@/interface/modules/system\";\nimport {tableActionEnums} from \"@/enums/tableActionEnums\";\n\nexport default defineComponent({\n    name: \"OnlineUser\",\n    components: {CommonTable},\n    setup() {\n        const {\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            onPageChange,\n            onPageSizeChange,\n            getDataList\n        } = useCommonTable(onlineController.getOnlineUserList)\n        const buttonConfig = reactive<IButtonType>({\n            addButton: {\n                show: false,\n            },\n            editButton: {\n                show: false,\n            },\n            deleteButton: {\n                type: \"delete\",\n                title: \"强制退出\",\n                actionType: tableActionEnums.DELETE,\n                show: true,\n                colorType: \"error\",\n            },\n            batchDeleteButton: {\n                type: \"batchDelete\",\n                colorType: \"error\",\n                actionType: tableActionEnums.BATCH_DELETE,\n                show: true,\n                title: \"批量强制退出\",\n            }\n        })\n\n        const onEvent = (value: any) => {\n            switch (value.type) {\n                // 强制退出\n                case tableActionEnums.DELETE:\n                    console.log(value.row.token)\n                    window.$dialog.warning({\n                        title: \"温馨提醒\",\n                        content: \"是否确认强制该用户下线？\",\n                        positiveText: \"确定\",\n                        negativeText: \"取消\",\n                        onPositiveClick: () => {\n                            onlineController.removeUser(value.row.token).then(res => {\n                                window.$message.success(res.msg)\n                                getDataList()\n                            })\n                        }\n                    })\n                    break\n                // 批量强制退出\n                case tableActionEnums.BATCH_DELETE:\n                    window.$dialog.warning({\n                        title: \"温馨提醒\",\n                        content: \"是否确认强制将选中的用户下线？\",\n                        positiveText: \"确定\",\n                        negativeText: \"取消\",\n                        onPositiveClick: () => {\n                            onlineController.batchRemoveUser(value.data).then(res => {\n                                window.$message.success(res.msg)\n                                getDataList()\n                            })\n                        }\n                    })\n                    break\n            }\n        }\n\n        onMounted(() => {\n            getDataList()\n        })\n\n        return {\n            columns,\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            onPageChange,\n            onPageSizeChange,\n            onEvent,\n            buttonConfig\n        }\n    }\n})\n</script>\n\n<style scoped>\n\n</style>"
  },
  {
    "path": "ginyi-vue3/src/views/pages/monitor/service/columns.tsx",
    "content": "import {DataTableColumns, NTag} from \"naive-ui\";\n\nexport const columns: DataTableColumns<any> = [\n    {\n        title: \"盘符路径\",\n        key: \"dirName\",\n    },\n    {\n        title: \"盘符类型\",\n        key: \"typeName\",\n    },\n    {\n        title: \"总大小\",\n        key: \"total\",\n        render: (row) => (\n            <span>{row.total}GB</span>\n        )\n    },\n    {\n        title: \"已用大小\",\n        key: \"used\",\n        render: (row) => (\n            <span>{row.used}GB</span>\n        )\n    },\n    {\n        title: \"可用大小\",\n        key: \"free\",\n        render: (row) => (\n            <span>{row.free}GB</span>\n        )\n    },\n    {\n        title: \"已用百分比\",\n        key: \"usage\",\n        render: (row) => (\n            <span>{row.usage}%</span>\n        )\n    },\n]"
  },
  {
    "path": "ginyi-vue3/src/views/pages/monitor/service/index.vue",
    "content": "<template>\n    <n-spin :show=\"loading\">\n        <n-grid :x-gap=\"12\" :y-gap=\"12\" :cols=\"2\">\n            <n-grid-item>\n                <n-card title=\"CPU\" hoverable>\n                    <template #header-extra>\n                        <n-tooltip placement=\"top-start\" trigger=\"hover\" :show-arrow=\"false\">\n                            <template #trigger>\n                                <n-icon size=\"24\">\n                                    <ReloadOutline @click=\"reloadData\" class=\"load-icon\"/>\n                                </n-icon>\n                            </template>\n                            重新获取数据\n                        </n-tooltip>\n                    </template>\n                    <n-table :bordered=\"false\">\n                        <thead>\n                        <tr>\n                            <th>属性</th>\n                            <th>值</th>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <tr>\n                            <td>核心数</td>\n                            <td>\n                                <n-tag>{{ serverInfo?.cpu?.cpuNum }}</n-tag>\n                            </td>\n                        </tr>\n                        <tr>\n                            <td>用户使用率</td>\n                            <td>\n                                <n-tag type=\"warning\">{{ serverInfo?.cpu?.used }}%</n-tag>\n                            </td>\n                        </tr>\n                        <tr>\n                            <td>系统使用率</td>\n                            <td>\n                                <n-tag type=\"info\">{{ serverInfo?.cpu?.sys }}%</n-tag>\n                            </td>\n                        </tr>\n                        <tr>\n                            <td>当前空闲率</td>\n                            <td>\n                                <n-tag type=\"success\">{{ serverInfo?.cpu?.free }}%</n-tag>\n                            </td>\n                        </tr>\n                        </tbody>\n                    </n-table>\n                </n-card>\n            </n-grid-item>\n            <n-grid-item>\n                <n-card title=\"内存\" hoverable>\n                    <template #header-extra>\n                        <n-tooltip placement=\"top-start\" trigger=\"hover\" :show-arrow=\"false\">\n                            <template #trigger>\n                                <n-icon size=\"24\">\n                                    <ReloadOutline @click=\"reloadData\" class=\"load-icon\"/>\n                                </n-icon>\n                            </template>\n                            重新获取数据\n                        </n-tooltip>\n                    </template>\n                    <n-table :bordered=\"false\">\n                        <thead>\n                        <tr>\n                            <th>属性</th>\n                            <th>内存</th>\n                            <th>JVM</th>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <tr>\n                            <td>总内存</td>\n                            <td>\n                                <n-tag>{{ serverInfo?.memory?.total }}G</n-tag>\n                            </td>\n                            <td>\n                                <n-tag>{{ serverInfo?.jvm?.total }}M</n-tag>\n                            </td>\n                        </tr>\n                        <tr>\n                            <td>已用内存</td>\n                            <td>\n                                <n-tag type=\"warning\">{{ serverInfo?.memory?.used }}G</n-tag>\n                            </td>\n                            <td>\n                                <n-tag type=\"warning\">{{ serverInfo?.jvm?.used }}M</n-tag>\n                            </td>\n                        </tr>\n                        <tr>\n                            <td>剩余内存</td>\n                            <td>\n                                <n-tag type=\"info\">{{ serverInfo?.memory?.free }}G</n-tag>\n                            </td>\n                            <td>\n                                <n-tag type=\"info\">{{ serverInfo?.jvm?.free }}M</n-tag>\n                            </td>\n                        </tr>\n                        <tr>\n                            <td>使用率</td>\n                            <td>\n                                <n-tag type=\"success\">{{ serverInfo?.memory?.free }}%</n-tag>\n                            </td>\n                            <td>\n                                <n-tag type=\"success\">{{ serverInfo?.jvm?.usage }}%</n-tag>\n                            </td>\n                        </tr>\n                        </tbody>\n                    </n-table>\n                </n-card>\n            </n-grid-item>\n            <n-grid-item span=\"2\">\n                <n-card title=\"服务器信息\" hoverable>\n                    <template #header-extra>\n                        <n-tooltip placement=\"top-start\" trigger=\"hover\" :show-arrow=\"false\">\n                            <template #trigger>\n                                <n-icon size=\"24\">\n                                    <ReloadOutline @click=\"reloadData\" class=\"load-icon\"/>\n                                </n-icon>\n                            </template>\n                            重新获取数据\n                        </n-tooltip>\n                    </template>\n                    <n-table :bordered=\"false\">\n                        <thead>\n                        <tr>\n                            <th>服务器名称</th>\n                            <th>服务器IP</th>\n                            <th>操作系统</th>\n                            <th>系统架构</th>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <tr>\n                            <td>{{ serverInfo?.sys?.computerName }}</td>\n                            <td>{{ serverInfo?.sys?.computerIp }}</td>\n                            <td>{{ serverInfo?.sys?.osName }}</td>\n                            <td>{{ serverInfo?.sys?.osArch }}</td>\n                        </tr>\n                        </tbody>\n                    </n-table>\n                </n-card>\n            </n-grid-item>\n            <n-grid-item span=\"2\">\n                <n-card title=\"Java虚拟机信息\" hoverable>\n                    <template #header-extra>\n                        <n-tooltip placement=\"top-start\" trigger=\"hover\" :show-arrow=\"false\">\n                            <template #trigger>\n                                <n-icon size=\"24\">\n                                    <ReloadOutline @click=\"reloadData\" class=\"load-icon\"/>\n                                </n-icon>\n                            </template>\n                            重新获取数据\n                        </n-tooltip>\n                    </template>\n                    <n-descriptions label-placement=\"left\" title=\"描述\" bordered :column=\"2\"\n                                    :label-style=\"{width: '150px'}\">\n                        <n-descriptions-item label=\"Java名称\">\n                            {{ serverInfo?.jvm?.name }}\n                        </n-descriptions-item>\n                        <n-descriptions-item label=\"Java版本\">\n                            {{ serverInfo?.jvm?.version }}\n                        </n-descriptions-item>\n                        <n-descriptions-item label=\"启动时间\">\n                            {{ serverInfo?.jvm?.startTime }}\n                        </n-descriptions-item>\n                        <n-descriptions-item label=\"运行时间\">\n                            {{ serverInfo?.jvm?.runTime }}\n                        </n-descriptions-item>\n                        <n-descriptions-item label=\"安装路径\">\n                            {{ serverInfo?.jvm?.home }}\n                        </n-descriptions-item>\n                        <n-descriptions-item label=\"项目路径\">\n                            {{ serverInfo?.sys?.userDir }}\n                        </n-descriptions-item>\n                        <n-descriptions-item label=\"运行参数\">\n                            {{ serverInfo?.jvm?.inputArgs }}\n                        </n-descriptions-item>\n                    </n-descriptions>\n                </n-card>\n            </n-grid-item>\n            <n-grid-item span=\"2\">\n                <n-card title=\"磁盘状态\" hoverable>\n                    <template #header-extra>\n                        <n-tooltip placement=\"top-start\" trigger=\"hover\" :show-arrow=\"false\">\n                            <template #trigger>\n                                <n-icon size=\"24\">\n                                    <ReloadOutline @click=\"reloadData\" class=\"load-icon\"/>\n                                </n-icon>\n                            </template>\n                            重新获取数据\n                        </n-tooltip>\n                    </template>\n                    <n-data-table\n                        :columns=\"columns\"\n                        :data=\"serverInfo?.file\"\n                        :bordered=\"false\"\n                    />\n                </n-card>\n            </n-grid-item>\n        </n-grid>\n    </n-spin>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, onMounted, ref} from \"vue\";\nimport {columns} from \"@/views/pages/monitor/service/columns\";\nimport {monitorController} from \"@/api\";\nimport {ReloadOutline} from \"@vicons/ionicons5\";\n\n\nexport default defineComponent({\n    components: {\n        ReloadOutline\n    },\n    setup() {\n        const serverInfo = ref<any>(undefined)\n        const loading = ref<boolean>(true)\n\n        const reloadData = () => {\n            loading.value = true\n            getServerInfo()\n        }\n        const getServerInfo = () => {\n            monitorController.getServerInfo().then(res => {\n                serverInfo.value = res.data\n                loading.value = false\n            })\n        }\n\n        onMounted(() => {\n            getServerInfo()\n        })\n        return {\n            columns,\n            serverInfo,\n            loading,\n            ReloadOutline,\n            reloadData\n        }\n    }\n})\n</script>\n\n<style scoped lang=\"less\">\n.load-icon:hover{\n    cursor: pointer;\n}\n</style>"
  },
  {
    "path": "ginyi-vue3/src/views/pages/monitor/task/index.vue",
    "content": "<template>\n    <div>\n        定时任务\n    </div>\n</template>\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/order/index.vue",
    "content": "<template>\n    <div>\n        订单页面\n    </div>\n</template>\n\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/product/index.vue",
    "content": "<template>\n    <div>\n        商品页面\n    </div>\n</template>\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/department/columns.tsx",
    "content": "import {DataTableColumns, NSwitch, NTag} from \"naive-ui\";\nimport {useCommonColumns} from \"@/hooks/useCommonColums\";\nimport {h} from \"vue\";\nimport {eventBus} from \"@/config/eventBus\";\n\nconst {useRenderStateById} = useCommonColumns()\n/**\n * 状态的点击事件\n * @param row\n */\nconst handleSwitchClick = (row: any) => {\n    eventBus.emit(\"handleDeptStatusSwitchClick\", row)\n}\n\nexport const columns: DataTableColumns<any> = [\n    {\n        title: \"部门名称\",\n        key: \"deptName\"\n    },\n    {\n        title: \"状态\",\n        key: \"status\",\n        render: (row) => (\n            h(NSwitch, {\n                value: row.status === \"0\",\n                onClick: () => handleSwitchClick(row)\n            }, {\n                checked: () => \"正常\",\n                unchecked: () => \"禁用\"\n            })\n        )\n    },\n    {\n        title: \"排序\",\n        key: \"sort\",\n    },\n    {\n        title: \"创建时间\",\n        key: \"createTime\",\n    }\n]"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/department/deptEditForm.vue",
    "content": "<template>\n    <CommonModal v-model:show=\"modalShow\"\n                 :title=\"modalTitle\"\n                 @onSubmit=\"onSubmit\" style=\"width: 800px\">\n        <n-form\n            ref=\"modalFormRef\"\n            :model=\"modalForm\"\n            :inline=\"false\"\n            :rules=\"rules\">\n            <n-grid :cols=\"24\" :x-gap=\"24\">\n                <n-form-item-gi :span=\"24\" label=\"上级部门\" path=\"parentId\">\n                    <n-tree-select v-model:value=\"modalForm.parentId\"\n                                   label-field=\"deptName\"\n                                   key-field=\"deptId\"\n                                   :default-value=\"0\"\n                                   :options=\"deptDict\"\n                                   :show-path=\"true\"\n                                   placeholder=\"不选择时，默认顶级部门\"\n                                   clearable\n                                   default-expand-all/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"部门名称\" path=\"deptName\">\n                    <n-input v-model:value=\"modalForm.deptName\" clearable placeholder=\"请输入部门名称\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"显示顺序\" path=\"sort\">\n                    <n-input-number v-model:value=\"modalForm.sort\" clearable placeholder=\"请输入显示顺序\" style=\"width: 100%\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"部门负责人\" path=\"deptName\">\n                    <n-input v-model:value=\"modalForm.leader\" clearable placeholder=\"请输入部门负责人\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"负责人邮箱\" path=\"email\">\n                    <n-input v-model:value=\"modalForm.email\" clearable placeholder=\"请输入负责人邮箱\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"负责人联系电话\" path=\"phone\">\n                    <n-input v-model:value=\"modalForm.phone\" clearable placeholder=\"请输入负责人联系电话\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"状态\" path=\"status\">\n                    <n-radio-group v-model:value=\"modalForm.status\" name=\"radioGroup2\" size=\"small\">\n                        <n-radio-button v-for=\"(item, index) in statusDict\" :key=\"index\" :value=\"item.value\">\n                            {{ item.label }}\n                        </n-radio-button>\n                    </n-radio-group>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"24\" label=\"备注\" path=\"remark\">\n                    <n-input v-model:value=\"modalForm.remark\" clearable type=\"textarea\" placeholder=\"请输入备注\"/>\n                </n-form-item-gi>\n            </n-grid>\n        </n-form>\n    </CommonModal>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, ref, watch} from \"vue\";\nimport CommonModal from \"@/components/commonModal/index.vue\";\nimport {useCommonModal} from \"@/components/commonModal/useCommonModal\";\nimport {deptController} from \"@/api\";\nimport {useStaticDict} from \"@/dictionary/useStaticDict\";\nimport {useDynamicDict} from \"@/dictionary/useDynamicDict\";\n\nexport default defineComponent({\n    name: \"deptEditForm\",\n    components: {\n        CommonModal\n    },\n    setup() {\n        const {\n            modalShow,\n            modalTitle,\n            modalForm,\n            modalFormRef,\n            modalLoading,\n            onAdd,\n            onEdit,\n            onSubmit,\n            onDeleteById,\n            onDeleteByIds\n        } = useCommonModal(\"部门\", {status: \"0\"},\n            deptController.add,\n            deptController.edit,\n            deptController.deleteById,\n            deptController.deleteByIds,\n            deptController.getDeptDetailsById\n        )\n        const {statusDict} = useStaticDict()\n        const {deptDict} = useDynamicDict()\n\n\n        const rules = {\n            deptName: {type: \"string\", required: true, message: \"请输入部门名称\", trigger: [\"input\", \"blur\"]},\n            sort: {type: \"number\", required: true, message: \"请输入排序\", trigger: [\"input\", \"blur\"]},\n            status: {type: \"string\", required: true, message: \"请选择状态\", trigger: [\"blur\", \"change\"]},\n            email: {type: \"string\", required: true, message: \"请输入部门负责人邮箱\", trigger: [\"input\", \"blur\"]},\n            phone: {type: \"string\", required: true, message: \"请输入部门负责人联系电话\", trigger: [\"input\", \"blur\"]},\n        }\n\n        const addChildDept = (value: any) => {\n            onAdd()\n            modalForm.value.parentId = value.deptId\n        }\n\n        return {\n            modalShow,\n            modalTitle,\n            modalForm,\n            modalFormRef,\n            modalLoading,\n            rules,\n            onAdd,\n            onEdit,\n            onSubmit,\n            onDeleteById,\n            onDeleteByIds,\n            statusDict,\n            deptDict,\n            addChildDept,\n        }\n    }\n})\n</script>\n\n<style scoped>\n\n</style>"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/department/deptQueryForm.vue",
    "content": "<template>\n    <CommonForm\n        :submitButtonText=\"'查询'\"\n        @onSubmit=\"query\"\n        @onReset=\"reset\">\n        <n-form\n            ref=\"formRef\"\n            :model=\"formValue\"\n            :inline=\"true\"\n            :label-width=\"80\">\n            <n-grid\n                x-gap=\"12\"\n                :cols=\"3\">\n                <n-grid-item>\n                    <n-form-item label=\"部门名称\" path=\"roleName\">\n                        <n-input v-model:value=\"formValue.deptName\" placeholder=\"请输入部门名称\" clearable v-focus/>\n                    </n-form-item>\n                </n-grid-item>\n                <n-grid-item>\n                    <n-form-item label=\"状态\" path=\"status\">\n                        <n-select v-model:value=\"formValue.status\" clearable :options=\"statusDict\"/>\n                    </n-form-item>\n                </n-grid-item>\n                <n-grid-item>\n                    <n-form-item label=\"起止时间\" path=\"time\">\n                        <n-date-picker v-model:formatted-value=\"formValue.time\"\n                                       clearable\n                                       value-format=\"yyyy-MM-dd HH:mm:ss\"\n                                       update-value-on-close type=\"datetimerange\" :actions=\"['confirm', 'clear']\"/>\n                    </n-form-item>\n                </n-grid-item>\n            </n-grid>\n        </n-form>\n    </CommonForm>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent} from \"vue\";\nimport CommonForm from \"@/components/commonForm/index.vue\";\nimport {useCommonForm} from \"@/components/commonForm/useCommonForm\";\nimport {useStaticDict} from \"@/dictionary/useStaticDict\";\n\nexport default defineComponent({\n    name: \"DeptQueryValue\",\n    components: {\n        CommonForm\n    },\n    emits: [\"onQuery\", \"onReset\"],\n    setup(props, context) {\n        const {\n            formRef,\n            formValue,\n            onReset,\n            onQuery\n        } = useCommonForm({})\n\n        const {statusDict} = useStaticDict()\n\n        const query = () => {\n            onQuery().then(res => {\n                context.emit(\"onQuery\", res)\n            })\n        }\n\n        const reset = () => {\n            onReset().then(res => {\n                context.emit(\"onReset\", res)\n            })\n        }\n\n        return {\n            formRef,\n            formValue,\n            query,\n            reset,\n            statusDict\n        }\n    }\n})\n</script>"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/department/index.vue",
    "content": "<template>\n    <div>\n        <CommonTable :columns=\"columns\"\n                     :pagination=\"tablePagination\"\n                     :loading=\"tableLoading\"\n                     :dataList=\"tableDataList\"\n                     :labelField=\"'title'\"\n                     :actionWidth=\"220\"\n                     :rowKey=\"(row) => row.deptId\"\n                     :scrollX=\"1000\"\n                     :actionColData=\"actionCol\"\n                     @onPageChange=\"onPageChange\"\n                     @onPageSizeChange=\"onPageSizeChange\"\n                     @onEvent=\"onEvent\">\n            <template #query>\n                <DeptQueryForm @onQuery=\"onQuery\" @onReset=\"onReset\"/>\n            </template>\n        </CommonTable>\n        <DeptEditForm ref=\"deptEditFormRef\"/>\n    </div>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, onMounted, onUnmounted, ref, watch} from \"vue\";\nimport {useCommonTable} from \"@/components/commonTable/useCommonTable\";\nimport {deptController} from \"@/api\";\nimport {columns} from \"@/views/pages/system/department/columns\";\nimport CommonTable from \"@/components/commonTable/index.vue\";\nimport DeptQueryForm from \"@/views/pages/system/department/deptQueryForm.vue\";\nimport DeptEditForm from \"@/views/pages/system/department/deptEditForm.vue\";\nimport {eventBus} from \"@/config/eventBus\";\nimport {tableActionEnums} from \"@/enums/tableActionEnums\";\n\nenum actionEnums {\n    // 新增子项\n    ADD_CHILD = \"add_child\",\n}\n\nexport default defineComponent({\n    components: {\n        DeptQueryForm, CommonTable, DeptEditForm\n    },\n    setup() {\n        const {\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            onPageChange,\n            onPageSizeChange,\n            getDataList\n        } = useCommonTable(deptController.list)\n        const deptEditFormRef = ref(null)\n        // 操作列\n        const actionCol = ref<Array<any>>([\n            {\n                title: \"新增子项\",\n                colorType: \"success\",\n                actionType: actionEnums.ADD_CHILD,\n            }\n        ])\n\n        // @ts-ignore\n        watch(() => deptEditFormRef?.value?.modalLoading, () => getDataList(), {deep: true})\n\n        const onEvent = (value: any) => {\n            switch (value.type) {\n                case tableActionEnums.ADD:\n                    // @ts-ignore\n                    deptEditFormRef?.value?.onAdd()\n                    break\n                case tableActionEnums.EDIT:\n                    // @ts-ignore\n                    deptEditFormRef?.value?.onEdit(value.row)\n                    break\n                case tableActionEnums.DELETE:\n                    // @ts-ignore\n                    deptEditFormRef?.value?.onDeleteById(value.row.deptId)\n                    break\n                case tableActionEnums.BATCH_DELETE:\n                    // @ts-ignore\n                    deptEditFormRef?.value?.onDeleteByIds(value.data)\n                    break\n                case actionEnums.ADD_CHILD:\n                    // @ts-ignore\n                    deptEditFormRef?.value?.addChildDept(value.row)\n                    break\n            }\n        }\n\n        const onQuery = (value: any) => {\n            getDataList(value)\n        }\n\n        const onReset = (value: any) => {\n            getDataList(value)\n        }\n\n        onMounted(() => {\n            getDataList()\n            eventBus.on(\"handleDeptStatusSwitchClick\", (row: any) => {\n                deptController.updateStatus({\n                    deptId: row.deptId,\n                    status: [0, \"0\"].includes(row.status) ? \"1\" : \"0\"\n                }).then(res => {\n                    window.$message.success(res.msg)\n                    getDataList()\n                })\n            })\n        })\n\n        onUnmounted(() => {\n            eventBus.off(\"handleDeptStatusSwitchClick\",() => {})\n        })\n\n        return {\n            columns,\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            onPageChange,\n            onPageSizeChange,\n            onEvent,\n            onQuery,\n            onReset,\n            deptEditFormRef,\n            actionCol\n        }\n    }\n})\n</script>\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/dict/index.vue",
    "content": "<template>\n    <div>\n        字典管理\n    </div>\n</template>\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/log/login/columns.tsx",
    "content": "import {DataTableColumns, NTag} from \"naive-ui\";\nimport {useCommonColumns} from \"@/hooks/useCommonColums\";\n\nconst {useRenderSuccessById} = useCommonColumns()\n\n\nexport const columns: DataTableColumns<any> = [\n    {\n        title: \"用户名称\",\n        key: \"userName\",\n    },\n    {\n        title: \"登录地址\",\n        key: \"ipaddr\"\n    },\n    {\n        title: \"登录地点\",\n        key: \"loginLocation\",\n    },\n    {\n        title: \"浏览器\",\n        key: \"browser\",\n    },\n    {\n        title: \"操作系统\",\n        key: \"os\",\n    },\n    {\n        title: \"登录状态\",\n        key: \"status\",\n        render: (row) => (\n            <NTag type={row.status === \"0\" ? \"success\" : \"error\"}>\n                {useRenderSuccessById(row.status)}\n            </NTag>\n        )\n    },\n    {\n        title: \"操作信息\",\n        key: \"msg\",\n        ellipsis: {\n            tooltip: true\n        }\n    },\n    {\n        title: \"登录日期\",\n        key: \"createTime\",\n        width: 200\n    }\n]"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/log/login/index.vue",
    "content": "<template>\n    <div>\n        <CommonTable :columns=\"columns\"\n                     :pagination=\"tablePagination\"\n                     :loading=\"tableLoading\"\n                     :dataList=\"tableDataList\"\n                     :labelField=\"'title'\"\n                     :actionWidth=\"140\"\n                     :rowKey=\"(row) => row.id\"\n                     :scrollX=\"1200\"\n                     :buttonConfig=\"buttonConfig\"\n                     :showActionCol=\"false\"\n                     @onPageChange=\"onPageChange\"\n                     @onPageSizeChange=\"onPageSizeChange\"\n                     @onEvent=\"onEvent\">\n        </CommonTable>\n    </div>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, onMounted, reactive} from \"vue\";\nimport CommonTable from \"@/components/commonTable/index.vue\";\nimport {useCommonTable} from \"@/components/commonTable/useCommonTable\";\nimport {logController} from \"@/api\";\nimport {IButtonType} from \"@/interface/modules/system\";\nimport {columns} from \"@/views/pages/system/log/login/columns\";\n\nexport default defineComponent({\n    name: \"LogLogin\",\n    components: {\n        CommonTable\n    },\n    setup() {\n        const {\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            onPageChange,\n            onPageSizeChange,\n            getDataList\n        } = useCommonTable(logController.getLoginLogList)\n        const buttonConfig = reactive<IButtonType>({\n            addButton: {\n                type: \"add\",\n                title: \"新增数据\",\n                actionType: 0,\n                show: false,\n                colorType: \"primary\",\n            },\n            editButton: {\n                type: \"edit\",\n                title: \"编辑\",\n                actionType: 1,\n                show: false,\n                colorType: \"primary\",\n            },\n            deleteButton: {\n                type: \"delete\",\n                title: \"强制退出\",\n                actionType: 2,\n                show: false,\n                colorType: \"error\",\n            },\n            batchDeleteButton: {\n                type: \"batchDelete\",\n                colorType: \"error\",\n                actionType: 3,\n                show: false,\n                title: \"批量强制退出\",\n            }\n        })\n\n        const onEvent = (value: any) => {\n\n        }\n\n        onMounted(() => {\n            getDataList()\n        })\n\n        return {\n            columns,\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            onPageChange,\n            onPageSizeChange,\n            buttonConfig,\n            onEvent\n        }\n    }\n})\n\n</script>\n\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/log/operation/columns.tsx",
    "content": "import {DataTableColumns, NTag} from \"naive-ui\";\nimport {useCommonColumns} from \"@/hooks/useCommonColums\";\nimport {useColumns} from \"@/views/pages/system/log/operation/useColumns\";\n\nconst {useRenderSuccessById} = useCommonColumns()\nconst {useRenderOperationType} = useColumns()\n\n\nexport const columns: DataTableColumns<any> = [\n    {\n        title: \"操作人员\",\n        key: \"operationName\",\n    },\n    {\n        title: \"操作地址\",\n        key: \"operationIp\"\n    },\n    {\n        title: \"操作地点\",\n        key: \"operationLocation\",\n    },\n    {\n        title: \"系统模块\",\n        key: \"title\",\n    },\n    {\n        title: \"操作类型\",\n        key: \"businessType\",\n        render: (row) => (\n            <NTag type={\n                row.businessType.toString() === \"1\" ? \"info\"\n                    : row.businessType.toString() === \"2\" ? \"warning\"\n                        : row.businessType.toString() === \"3\" ? \"error\" : \"default\"}>\n                {useRenderOperationType(row.businessType.toString())}\n            </NTag>\n        )\n    },\n    {\n        title: \"请求方式\",\n        key: \"requestMethod\",\n    },\n    {\n        title: \"操作状态\",\n        key: \"status\",\n        render: (row) => (\n            <NTag type={row.status.toString() === \"0\" ? \"success\" : \"error\"}>\n                {useRenderSuccessById(row.status)}\n            </NTag>\n        )\n    },\n    {\n        title: \"操作时间\",\n        key: \"createTime\",\n        width: 200\n    }\n]"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/log/operation/index.vue",
    "content": "<template>\n    <div>\n        <CommonTable :columns=\"columns\"\n                     :pagination=\"tablePagination\"\n                     :loading=\"tableLoading\"\n                     :dataList=\"tableDataList\"\n                     :labelField=\"'title'\"\n                     :actionWidth=\"140\"\n                     :actionColData=\"actionCol\"\n                     :rowKey=\"(row) => row.id\"\n                     :scrollX=\"1200\"\n                     :buttonConfig=\"buttonConfig\"\n                     @onPageChange=\"onPageChange\"\n                     @onPageSizeChange=\"onPageSizeChange\"\n                     @onEvent=\"onEvent\">\n        </CommonTable>\n        <n-drawer v-model:show=\"show\" width=\"50%\">\n            <n-drawer-content title=\"数据详情\" closable>\n                <MonacoEditor v-model=\"rowData\"\n                              :language=\"language\"\n                              width=\"100%\"\n                              height=\"100%\"\n                              @editorMounted=\"editorMounted\"/>\n            </n-drawer-content>\n        </n-drawer>\n    </div>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, onMounted, reactive, ref} from \"vue\";\nimport CommonTable from \"@/components/commonTable/index.vue\";\nimport {useCommonTable} from \"@/components/commonTable/useCommonTable\";\nimport {logController} from \"@/api\";\nimport {IButtonConfig, IButtonType} from \"@/interface/modules/system\";\nimport {columns} from \"@/views/pages/system/log/operation/columns\";\nimport MonacoEditor from \"@/components/monacoEditor/index.vue\";\nimport * as monaco from \"monaco-editor\";\n\nexport default defineComponent({\n    name: \"LogLogin\",\n    components: {\n        CommonTable, MonacoEditor\n    },\n    setup() {\n        const show = ref<boolean>(false)\n        const rowData = ref<any>(undefined)\n        const language = ref<any>(\"json\")\n        const editorMounted = (editor: monaco.editor.IStandaloneCodeEditor) => {\n        }\n        const {\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            onPageChange,\n            onPageSizeChange,\n            getDataList\n        } = useCommonTable(logController.getOperationLogList)\n        const buttonConfig = reactive<IButtonType>({\n            addButton: {\n                type: \"add\",\n                title: \"新增数据\",\n                actionType: 0,\n                show: false,\n                colorType: \"primary\",\n            },\n            editButton: {\n                type: \"edit\",\n                title: \"编辑\",\n                actionType: 1,\n                show: false,\n                colorType: \"primary\",\n            },\n            deleteButton: {\n                type: \"delete\",\n                title: \"强制退出\",\n                actionType: 2,\n                show: false,\n                colorType: \"error\",\n            },\n            batchDeleteButton: {\n                type: \"batchDelete\",\n                colorType: \"error\",\n                actionType: 3,\n                show: false,\n                title: \"批量强制退出\",\n            }\n        })\n        // 操作列\n        const actionCol = ref<Array<IButtonConfig>>([\n            {\n                type: \"details\",\n                title: \"查看详情\",\n                colorType: \"primary\",\n                show: true,\n                actionType: 4,\n            }\n        ])\n\n        const onEvent = (value: any) => {\n            show.value = true\n            value.row.operationParam = JSON.parse(value.row.operationParam ?? \"{}\")\n            value.row.jsonResult = JSON.parse(value.row.jsonResult ?? \"{}\")\n            rowData.value = JSON.stringify(value.row, null, \"\\t\")\n        }\n\n        onMounted(() => {\n            getDataList()\n        })\n\n        return {\n            columns,\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            onPageChange,\n            onPageSizeChange,\n            buttonConfig,\n            actionCol,\n            onEvent,\n            show,\n            rowData,\n            language,\n            editorMounted\n        }\n    }\n})\n\n</script>\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/log/operation/useColumns.ts",
    "content": "import {useStaticDict} from \"@/dictionary/useStaticDict\";\n\nexport const useColumns = () => {\n    const {operationType} = useStaticDict()\n\n    const useRenderOperationType = (operation: string) => {\n        const temp = operationType.value.filter(type => {\n            return type.value === operation\n        })\n        return temp.length !== 0 ? temp[0].label : undefined;\n    }\n\n\n    return {\n        useRenderOperationType\n    }\n}"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/menu/columns.tsx",
    "content": "import {DataTableColumns, NSwitch, NTag} from \"naive-ui\";\nimport {useColumns} from \"@/views/pages/system/menu/useColumns\";\nimport {useCommonColumns} from \"@/hooks/useCommonColums\";\nimport {h} from \"vue\";\nimport {eventBus} from \"@/config/eventBus\";\n\nconst {useRenderStateById} = useCommonColumns()\nconst {useRenderMenuType} = useColumns()\n/**\n * 状态的点击事件\n * @param row\n */\nconst handleSwitchClick = (row: any) => {\n    eventBus.emit(\"handleMenuStatusSwitchClick\", row)\n}\n\nexport const columns: DataTableColumns<any> = [\n    {\n        title: \"菜单名称\",\n        key: \"menuName\",\n        ellipsis: {\n            tooltip: true\n        }\n    },\n    {\n        title: \"图标\",\n        key: \"icon\",\n        ellipsis: {\n            tooltip: true\n        }\n    },\n    {\n        title: \"状态\",\n        key: \"status\",\n        width: 150,\n        render: (row) => (\n            h(NSwitch, {\n                value: row.status === \"0\",\n                onClick: () => handleSwitchClick(row)\n            }, {\n                checked: () => \"正常\",\n                unchecked: () => \"禁用\"\n            })\n        )\n    },\n    {\n        title: \"菜单类型\",\n        key: \"menuType\",\n        render: (row) => (\n            <NTag type={row.menuType === \"M\" ? \"primary\" : row.menuType === \"C\" ? \"success\" : \"warning\"}>\n                {useRenderMenuType(row.menuType)}\n            </NTag>\n        )\n    },\n    {\n        title: \"路由名称\",\n        key: \"name\",\n        align: \"center\",\n        render: (row) => (\n            <span>\n                {[undefined, null, \"\"].includes(row.name) ? \"-\" : row.name}\n            </span>\n        )\n    },\n    {\n        title: \"权限标识\",\n        key: \"perms\",\n        width: 200,\n        ellipsis: {\n            tooltip: true\n        },\n        align: \"center\",\n        render: (row) => (\n            <span>\n                {[undefined, null, \"\"].includes(row.perms) ? \"-\" : row.perms}\n            </span>\n        )\n    },\n    {\n        title: \"组件路径\",\n        key: \"component\",\n        width: 300,\n        render: (row) => (\n            <span>\n                {[undefined, null, \"\"].includes(row.component) ? \"-\" : row.component}\n            </span>\n        )\n    },\n    {\n        title: \"排序\",\n        key: \"sort\",\n    },\n    {\n        title: \"创建时间\",\n        key: \"createTime\",\n    }\n]"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/menu/index.vue",
    "content": "<template>\n    <div>\n        <CommonTable :columns=\"columns\"\n                     :pagination=\"tablePagination\"\n                     :loading=\"tableLoading\"\n                     :dataList=\"tableDataList\"\n                     :labelField=\"'title'\"\n                     :actionWidth=\"220\"\n                     :rowKey=\"(row) => row.menuId\"\n                     :scrollX=\"2000\"\n                     :actionColData=\"actionCol\"\n                     @onPageChange=\"onPageChange\"\n                     @onPageSizeChange=\"onPageSizeChange\"\n                     @onEvent=\"onEvent\">\n            <template #query>\n                <MenuQueryForm @onQuery=\"onQuery\" @onReset=\"onReset\"/>\n            </template>\n        </CommonTable>\n        <MenuEditForm ref=\"menuEditFormRef\"/>\n    </div>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, onMounted, onUnmounted, ref, watch} from \"vue\";\nimport CommonTable from \"@/components/commonTable/index.vue\";\nimport {useCommonTable} from \"@/components/commonTable/useCommonTable\";\nimport {menuController} from \"@/api\";\nimport {columns} from \"@/views/pages/system/menu/columns\";\nimport MenuQueryForm from \"@/views/pages/system/menu/menuQueryForm.vue\";\nimport MenuEditForm from \"@/views/pages/system/menu/menuEditForm.vue\";\nimport {eventBus} from \"@/config/eventBus\";\nimport {tableActionEnums} from \"@/enums/tableActionEnums\";\n\nenum actionEnums {\n    // 新增子项\n    ADD_CHILD = \"add_child\",\n}\n\nexport default defineComponent({\n    components: {\n        CommonTable, MenuQueryForm, MenuEditForm\n    },\n    setup() {\n        const {\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            onPageChange,\n            onPageSizeChange,\n            getDataList\n        } = useCommonTable(menuController.list)\n        const menuEditFormRef = ref(null)\n        // 操作列\n        const actionCol = ref<Array<any>>([\n            {\n                title: \"新增子项\",\n                colorType: \"success\",\n                actionType: actionEnums.ADD_CHILD,\n            }\n        ])\n\n        // @ts-ignore\n        watch(() => menuEditFormRef?.value?.modalLoading, () => getDataList(), {deep: true})\n\n        const onEvent = (value: any) => {\n            switch (value.type) {\n                case tableActionEnums.ADD:\n                    // @ts-ignore\n                    menuEditFormRef?.value?.onAdd()\n                    break\n                case tableActionEnums.EDIT:\n                    // @ts-ignore\n                    menuEditFormRef?.value?.onEdit(value.row)\n                    break\n                case tableActionEnums.DELETE:\n                    // @ts-ignore\n                    menuEditFormRef?.value?.onDeleteById(value.row.menuId)\n                    break\n                case tableActionEnums.BATCH_DELETE:\n                    // @ts-ignore\n                    menuEditFormRef?.value?.onDeleteByIds(value.data)\n                    break\n                case actionEnums.ADD_CHILD:\n                    if(value.row.menuType === \"F\"){\n                        window.$message.error(\"按钮无法新增子项！\")\n                        return\n                    }\n                    // @ts-ignore\n                    menuEditFormRef?.value?.addChildMenu(value.row)\n                    break\n            }\n        }\n\n        const onQuery = (value: any) => {\n            getDataList(value)\n        }\n\n        const onReset = (value: any) => {\n            getDataList(value)\n        }\n\n        onMounted(() => {\n            getDataList()\n            eventBus.on(\"handleMenuStatusSwitchClick\", (row: any) => {\n                menuController.updateStatus({\n                    menuId: row.menuId,\n                    status: [0, \"0\"].includes(row.status) ? \"1\" : \"0\"\n                }).then(res => {\n                    window.$message.success(res.msg)\n                    getDataList()\n                })\n            })\n        })\n\n        onUnmounted(() => {\n            eventBus.off(\"handleMenuStatusSwitchClick\",() => {})\n        })\n\n        return {\n            columns,\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            onPageChange,\n            onPageSizeChange,\n            actionCol,\n            onEvent,\n            onQuery,\n            onReset,\n            menuEditFormRef\n        }\n    }\n})\n</script>\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/menu/menuEditForm.vue",
    "content": "<template>\n    <CommonModal v-model:show=\"modalShow\"\n                 :title=\"modalTitle\"\n                 @onSubmit=\"onSubmit\" style=\"width: 800px\">\n        <n-form\n            ref=\"modalFormRef\"\n            :model=\"modalForm\"\n            :inline=\"false\"\n            :rules=\"rules\">\n            <n-grid :cols=\"24\" :x-gap=\"24\">\n                <n-form-item-gi :span=\"24\" label=\"上级菜单\" path=\"parentId\">\n                    <n-tree-select v-model:value=\"modalForm.parentId\"\n                                   label-field=\"menuName\"\n                                   key-field=\"menuId\"\n                                   :default-value=\"0\"\n                                   :options=\"menuDict\"\n                                   :show-path=\"true\"\n                                   placeholder=\"不选择时，默认顶级菜单\"\n                                   clearable\n                                   default-expand-all/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"菜单名称\" path=\"menuName\">\n                    <n-input v-model:value=\"modalForm.menuName\" clearable placeholder=\"请输入菜单名称\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"显示顺序\" path=\"sort\">\n                    <n-input-number v-model:value=\"modalForm.sort\" clearable placeholder=\"请输入显示顺序\" style=\"width: 100%\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"菜单图标\" :path=\"['M', 'C'].includes(modalForm.menuType) ? 'icon' : ''\">\n                    <n-input v-model:value=\"modalForm.icon\" clearable placeholder=\"请输入菜单图标\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"路由名称\" :path=\"['M', 'C'].includes(modalForm.menuType) ? 'name' : ''\">\n                    <n-input v-model:value=\"modalForm.name\" clearable placeholder=\"请输入路由名称\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"路由组件\" :path=\"modalForm.menuType === 'C' ? 'component' : ''\">\n                    <n-input v-model:value=\"modalForm.component\" clearable placeholder=\"请输入路由组件\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"权限字符\" :path=\"modalForm.menuType === 'F' ? 'perms' : ''\">\n                    <n-input v-model:value=\"modalForm.perms\" clearable placeholder=\"请输入权限字符\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"6\" label=\"菜单状态\" path=\"status\">\n                    <n-radio-group v-model:value=\"modalForm.status\" name=\"radioGroup2\" size=\"small\">\n                        <n-radio-button v-for=\"(item, index) in statusDict\" :key=\"index\" :value=\"item.value\">\n                            {{ item.label }}\n                        </n-radio-button>\n                    </n-radio-group>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"6\" label=\"是否显示\" path=\"visible\">\n                    <n-radio-group v-model:value=\"modalForm.visible\" name=\"radioGroup2\" size=\"small\">\n                        <n-radio-button v-for=\"(item, index) in whetherDict\" :key=\"index\" :value=\"item.value\">\n                            {{ item.label }}\n                        </n-radio-button>\n                    </n-radio-group>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"6\" label=\"是否缓存\" path=\"isCache\">\n                    <n-radio-group v-model:value=\"modalForm.isCache\" name=\"radioGroup2\" size=\"small\">\n                        <n-radio-button v-for=\"(item, index) in whetherDict\" :key=\"index\" :value=\"item.value\">\n                            {{ item.label }}\n                        </n-radio-button>\n                    </n-radio-group>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"6\" label=\"菜单类型\" path=\"menuType\">\n                    <n-radio-group v-model:value=\"modalForm.menuType\" name=\"radioGroup2\" size=\"small\">\n                        <n-radio-button v-for=\"(item, index) in menuTypeDict\" :key=\"index\" :value=\"item.value\">\n                            {{ item.label }}\n                        </n-radio-button>\n                    </n-radio-group>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"24\" label=\"备注\" path=\"remark\">\n                    <n-input v-model:value=\"modalForm.remark\" clearable type=\"textarea\" placeholder=\"请输入备注\"/>\n                </n-form-item-gi>\n            </n-grid>\n        </n-form>\n    </CommonModal>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, watch} from \"vue\";\nimport CommonModal from \"@/components/commonModal/index.vue\";\nimport {useCommonModal} from \"@/components/commonModal/useCommonModal\";\nimport {menuController} from \"@/api\";\nimport {useStaticDict} from \"@/dictionary/useStaticDict\";\nimport {useDynamicDict} from \"@/dictionary/useDynamicDict\";\nimport {useRemoveEmptyChildrenField} from \"@/hooks/useTree\";\n\nexport default defineComponent({\n    name: \"deptEditForm\",\n    components: {\n        CommonModal\n    },\n    setup() {\n        const {\n            modalShow,\n            modalTitle,\n            modalForm,\n            modalFormRef,\n            modalLoading,\n            onAdd,\n            onEdit,\n            onSubmit,\n            onDeleteById,\n            onDeleteByIds\n        } = useCommonModal(\"菜单\", {\n                status: \"0\",\n                visible: \"0\",\n                isCache: \"0\",\n                menuType: \"M\"\n            },\n            menuController.add,\n            menuController.edit,\n            menuController.deleteById,\n            menuController.deleteByIds,\n            menuController.getMenuDetailsById\n        )\n        const {statusDict, whetherDict, menuTypeDict} = useStaticDict()\n        const {menuDict} = useDynamicDict()\n\n\n        const rules = {\n            menuName: {type: \"string\", required: true, message: \"请输入菜单名称\", trigger: [\"input\", \"blur\"]},\n            sort: {type: \"number\", required: true, message: \"请输入排序\", trigger: [\"input\", \"blur\"]},\n            icon: {type: \"string\", required: true, message: \"请输入菜单图标\", trigger: [\"input\", \"blur\"]},\n            component: {type: \"string\", required: true, message: \"请输入路由组件\", trigger: [\"input\", \"blur\"]},\n            name: {type: \"string\", required: true, message: \"请输入路由名称\", trigger: [\"input\", \"blur\"]},\n            perms: {type: \"string\", required: true, message: \"请输入权限字符\", trigger: [\"input\", \"blur\"]},\n            status: {type: \"string\", required: true, message: \"请选择状态\", trigger: [\"blur\", \"change\"]},\n            isCache: {type: \"string\", required: true, message: \"请选择是否缓存\", trigger: [\"blur\", \"change\"]},\n            visible: {type: \"string\", required: true, message: \"请选择是否显示\", trigger: [\"blur\", \"change\"]},\n            menuType: {type: \"string\", required: true, message: \"请选择菜单类型\", trigger: [\"blur\", \"change\"]},\n        }\n\n        const addChildMenu = (value: any) => {\n            onAdd()\n            modalForm.value.parentId = value.menuId\n        }\n\n        watch(() => modalForm.value, () => modalForm.value.path = modalForm.value.name, {deep: true})\n\n        return {\n            modalShow,\n            modalTitle,\n            modalForm,\n            modalFormRef,\n            modalLoading,\n            rules,\n            onAdd,\n            onEdit,\n            onSubmit,\n            onDeleteById,\n            onDeleteByIds,\n            addChildMenu,\n            menuDict,\n            statusDict,\n            whetherDict,\n            menuTypeDict\n        }\n    }\n})\n</script>\n\n<style scoped>\n\n</style>"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/menu/menuQueryForm.vue",
    "content": "<template>\n    <CommonForm\n        :submitButtonText=\"'查询'\"\n        @onSubmit=\"query\"\n        @onReset=\"reset\">\n        <n-form\n            ref=\"formRef\"\n            :model=\"formValue\"\n            :inline=\"true\"\n            :label-width=\"80\">\n            <n-grid\n                x-gap=\"12\"\n                :cols=\"3\">\n                <n-grid-item>\n                    <n-form-item label=\"菜单名称\" path=\"menuName\">\n                        <n-input v-model:value=\"formValue.menuName\" placeholder=\"请输入菜单名称\" clearable v-focus/>\n                    </n-form-item>\n                </n-grid-item>\n                <n-grid-item>\n                    <n-form-item label=\"状态\" path=\"status\">\n                        <n-select v-model:value=\"formValue.status\" clearable :options=\"statusDict\" />\n                    </n-form-item>\n                </n-grid-item>\n                <n-grid-item>\n                    <n-form-item label=\"起止时间\" path=\"time\">\n                        <n-date-picker v-model:formatted-value=\"formValue.time\"\n                                       clearable\n                                       value-format=\"yyyy-MM-dd HH:mm:ss\"\n                                       update-value-on-close type=\"datetimerange\" :actions=\"['confirm', 'clear']\"/>\n                    </n-form-item>\n                </n-grid-item>\n            </n-grid>\n        </n-form>\n    </CommonForm>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent} from \"vue\";\nimport CommonForm from \"@/components/commonForm/index.vue\";\nimport {useCommonForm} from \"@/components/commonForm/useCommonForm\";\nimport {useStaticDict} from \"@/dictionary/useStaticDict\";\n\nexport default defineComponent({\n    name: \"DeptQueryValue\",\n    components: {\n        CommonForm\n    },\n    emits: [\"onQuery\", \"onReset\"],\n    setup(props, context) {\n        const {\n            formRef,\n            formValue,\n            onReset,\n            onQuery\n        } = useCommonForm({})\n\n        const {statusDict} = useStaticDict()\n\n        const query = () => {\n            onQuery().then(res => {\n                context.emit(\"onQuery\", res)\n            })\n        }\n\n        const reset = () => {\n            onReset().then(res => {\n                context.emit(\"onReset\", res)\n            })\n        }\n\n        return {\n            formRef,\n            formValue,\n            query,\n            reset,\n            statusDict\n        }\n    }\n})\n</script>"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/menu/useColumns.ts",
    "content": "import {useStaticDict} from \"@/dictionary/useStaticDict\";\n\nexport const useColumns = () => {\n    const {menuTypeDict} = useStaticDict()\n\n    const useRenderMenuType = (menuType: string) => {\n        const temp = menuTypeDict.value.filter(type => {\n            return type.value === menuType\n        })\n        return temp.length !== 0 ? temp[0].label : undefined;\n    }\n\n\n    return {\n        useRenderMenuType\n    }\n}"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/notice/index.vue",
    "content": "<template>\n    <div>\n        通知公告\n    </div>\n</template>\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/params/index.vue",
    "content": "<template>\n    <div>\n        参数管理\n    </div>\n</template>\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/position/columns.tsx",
    "content": "import {DataTableColumns, NSwitch, NTag} from \"naive-ui\";\nimport {useCommonColumns} from \"@/hooks/useCommonColums\";\nimport {h} from \"vue\";\nimport {eventBus} from \"@/config/eventBus\";\n\nconst {useRenderStateById} = useCommonColumns()\n/**\n * 状态的点击事件\n * @param row\n */\nconst handleSwitchClick = (row: any) => {\n    eventBus.emit(\"handlePostStatusSwitchClick\", row)\n}\nexport const columns: DataTableColumns<any> = [\n    {\n        title: \"岗位名称\",\n        key: \"postName\"\n    },\n    {\n        title: \"岗位编码\",\n        key: \"postCode\"\n    },\n    {\n        title: \"状态\",\n        key: \"status\",\n        render: (row) => (\n            h(NSwitch, {\n                value: row.status === \"0\",\n                onClick: () => handleSwitchClick(row)\n            }, {\n                checked: () => \"正常\",\n                unchecked: () => \"禁用\"\n            })\n        )\n    },\n    {\n        title: \"排序\",\n        key: \"sort\",\n    },\n    {\n        title: \"创建时间\",\n        key: \"createTime\",\n        width: 200\n    }\n]"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/position/index.vue",
    "content": "<template>\n    <div>\n        <CommonTable :columns=\"columns\"\n                     :pagination=\"tablePagination\"\n                     :loading=\"tableLoading\"\n                     :dataList=\"tableDataList\"\n                     :labelField=\"'title'\"\n                     :actionWidth=\"140\"\n                     :rowKey=\"(row) => row.postId\"\n                     :scrollX=\"1000\"\n                     @onPageChange=\"onPageChange\"\n                     @onPageSizeChange=\"onPageSizeChange\"\n                     @onEvent=\"onEvent\">\n            <template #query>\n                <PostQueryForm @onQuery=\"onQuery\" @onReset=\"onReset\"/>\n            </template>\n        </CommonTable>\n        <PostEditForm ref=\"postEditFormRef\"/>\n    </div>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, onMounted, onUnmounted, ref, watch} from \"vue\";\nimport CommonTable from \"@/components/commonTable/index.vue\";\nimport {useCommonTable} from \"@/components/commonTable/useCommonTable\";\nimport {menuController, postController} from \"@/api\";\nimport {columns} from \"@/views/pages/system/position/columns\";\nimport PostQueryForm from \"@/views/pages/system/position/postQueryForm.vue\";\nimport PostEditForm from \"@/views/pages/system/position/postEditForm.vue\";\nimport {eventBus} from \"@/config/eventBus\";\nimport {tableActionEnums} from \"@/enums/tableActionEnums\";\n\nexport default defineComponent({\n    components: {\n        CommonTable, PostQueryForm, PostEditForm\n    },\n    setup() {\n        const {\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            onPageChange,\n            onPageSizeChange,\n            getDataList\n        } = useCommonTable(postController.list)\n        const postEditFormRef = ref(null)\n\n        // @ts-ignore\n        watch(() => postEditFormRef?.value?.modalLoading, () => getDataList(), {deep: true})\n\n\n        const onEvent = (value: any) => {\n            switch (value.type) {\n                case tableActionEnums.ADD:\n                    // @ts-ignore\n                    postEditFormRef?.value?.onAdd()\n                    break\n                case tableActionEnums.EDIT:\n                    // @ts-ignore\n                    postEditFormRef?.value?.onEdit(value.row, value.row.roleId)\n                    break\n                case tableActionEnums.DELETE:\n                    // @ts-ignore\n                    postEditFormRef?.value?.onDeleteById(value.row.roleId)\n                    break\n                case tableActionEnums.BATCH_DELETE:\n                    // @ts-ignore\n                    postEditFormRef?.value?.onDeleteByIds(value.data)\n                    break\n            }\n        }\n\n        const onQuery = (value: any) => {\n            getDataList(value)\n        }\n\n        const onReset = (value: any) => {\n            getDataList(value)\n        }\n\n        onMounted(() => {\n            getDataList()\n            eventBus.on(\"handlePostStatusSwitchClick\", (row: any) => {\n                postController.updateStatus({\n                    postId: row.postId,\n                    status: [0, \"0\"].includes(row.status) ? \"1\" : \"0\"\n                }).then(res => {\n                    window.$message.success(res.msg)\n                    getDataList()\n                })\n            })\n        })\n\n        onUnmounted(() => {\n            eventBus.off(\"handlePostStatusSwitchClick\",() => {})\n        })\n\n        return {\n            columns,\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            onPageChange,\n            onPageSizeChange,\n            onEvent,\n            onQuery,\n            onReset,\n            postEditFormRef\n        }\n    }\n})\n</script>\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/position/postEditForm.vue",
    "content": "<template>\n    <CommonModal v-model:show=\"modalShow\" :title=\"modalTitle\" @onSubmit=\"onSubmit\" style=\"width: 800px\">\n        <n-form\n            ref=\"modalFormRef\"\n            :model=\"modalForm\"\n            :inline=\"false\"\n            :rules=\"rules\">\n            <n-grid :cols=\"24\" :x-gap=\"24\">\n                <n-form-item-gi :span=\"12\" label=\"岗位名称\" path=\"postName\">\n                    <n-input v-model:value=\"modalForm.postName\" clearable placeholder=\"请输入岗位名称\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"岗位编码\" path=\"postCode\">\n                    <n-input v-model:value=\"modalForm.postCode\" clearable placeholder=\"请输入岗位编码\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"角色顺序\" path=\"sort\">\n                    <n-input-number v-model:value=\"modalForm.sort\" clearable placeholder=\"请输入角色顺序\" style=\"width: 100%\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"状态\" path=\"status\">\n                    <n-radio-group v-model:value=\"modalForm.status\" name=\"radioGroup2\" size=\"small\">\n                        <n-radio-button v-for=\"(item, index) in statusDict\" :key=\"index\" :value=\"item.value\">\n                            {{ item.label }}\n                        </n-radio-button>\n                    </n-radio-group>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"24\" label=\"备注\" path=\"remark\">\n                    <n-input v-model:value=\"modalForm.remark\" clearable type=\"textarea\" placeholder=\"请输入备注\"/>\n                </n-form-item-gi>\n            </n-grid>\n        </n-form>\n    </CommonModal>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent} from \"vue\";\nimport CommonModal from \"@/components/commonModal/index.vue\";\nimport {useCommonModal} from \"@/components/commonModal/useCommonModal\";\nimport {postController} from \"@/api\";\nimport {useStaticDict} from \"@/dictionary/useStaticDict\";\n\nexport default defineComponent({\n    name: \"UserEditForm\",\n    components: {\n        CommonModal\n    },\n    setup() {\n        const {\n            modalShow,\n            modalTitle,\n            modalForm,\n            modalFormRef,\n            modalLoading,\n            onAdd,\n            onEdit,\n            onSubmit,\n            onDeleteById,\n            onDeleteByIds\n        } = useCommonModal(\"岗位\", {status: \"0\"},\n            postController.add,\n            postController.edit,\n            postController.deleteById,\n            postController.deleteByIds,\n            postController.getPostDetailsById\n        )\n        const {statusDict} = useStaticDict()\n\n        const rules = {\n            postName: {type: \"string\", required: true, message: \"请输入岗位名称\", trigger: [\"input\", \"blur\"]},\n            postCode: {type: \"string\", required: true, message: \"请输入岗位编码\", trigger: [\"input\", \"blur\"]},\n            sort: {type: \"number\", required: true, message: \"请输入排序\", trigger: [\"input\", \"blur\"]},\n            status: {type: \"string\", required: true, message: \"请选择状态\", trigger: [\"blur\", \"change\"]},\n        }\n\n        return {\n            modalShow,\n            modalTitle,\n            modalForm,\n            modalFormRef,\n            modalLoading,\n            rules,\n            onAdd,\n            onEdit,\n            onSubmit,\n            onDeleteById,\n            onDeleteByIds,\n            statusDict,\n        }\n    }\n})\n</script>\n\n<style scoped>\n\n</style>"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/position/postQueryForm.vue",
    "content": "<template>\n    <CommonForm\n        :submitButtonText=\"'查询'\"\n        @onSubmit=\"query\"\n        @onReset=\"reset\">\n        <n-form\n            ref=\"formRef\"\n            :model=\"formValue\"\n            :inline=\"true\"\n            :label-width=\"80\">\n            <n-grid\n                x-gap=\"12\"\n                :cols=\"4\">\n                <n-grid-item>\n                    <n-form-item label=\"岗位名称\" path=\"postName\">\n                        <n-input v-model:value=\"formValue.postName\" placeholder=\"请输入岗位名称\" clearable v-focus/>\n                    </n-form-item>\n                </n-grid-item>\n                <n-grid-item>\n                    <n-form-item label=\"岗位编码\" path=\"postCode\">\n                        <n-input v-model:value=\"formValue.postCode\" placeholder=\"请输入岗位编码\" clearable/>\n                    </n-form-item>\n                </n-grid-item>\n                <n-grid-item>\n                    <n-form-item label=\"状态\" path=\"status\">\n                        <n-select v-model:value=\"formValue.status\" clearable :options=\"statusDict\"/>\n                    </n-form-item>\n                </n-grid-item>\n                <n-grid-item>\n                    <n-form-item label=\"起止时间\" path=\"time\">\n                        <n-date-picker v-model:formatted-value=\"formValue.time\"\n                                       clearable\n                                       value-format=\"yyyy-MM-dd HH:mm:ss\"\n                                       update-value-on-close type=\"datetimerange\" :actions=\"['confirm', 'clear']\"/>\n                    </n-form-item>\n                </n-grid-item>\n            </n-grid>\n        </n-form>\n    </CommonForm>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent} from \"vue\";\nimport CommonForm from \"@/components/commonForm/index.vue\";\nimport {useCommonForm} from \"@/components/commonForm/useCommonForm\";\nimport {useStaticDict} from \"@/dictionary/useStaticDict\";\n\nexport default defineComponent({\n    name: \"RoleQueryValue\",\n    components: {\n        CommonForm\n    },\n    emits: [\"onQuery\", \"onReset\"],\n    setup(props, context) {\n        const {\n            formRef,\n            formValue,\n            onReset,\n            onQuery\n        } = useCommonForm({})\n\n        const {statusDict} = useStaticDict()\n\n        const query = () => {\n            onQuery().then(res => {\n                context.emit(\"onQuery\", res)\n            })\n        }\n\n        const reset = () => {\n            onReset().then(res => {\n                context.emit(\"onReset\", res)\n            })\n        }\n\n        return {\n            formRef,\n            formValue,\n            query,\n            reset,\n            statusDict\n        }\n    }\n})\n</script>"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/role/columns.tsx",
    "content": "import {DataTableColumns, NSwitch, NTag} from \"naive-ui\";\nimport {useCommonColumns} from \"@/hooks/useCommonColums\";\nimport {h} from \"vue\";\nimport {eventBus} from \"@/config/eventBus\";\n\nconst {useRenderStateById} = useCommonColumns()\n/**\n * 状态的点击事件\n * @param row\n */\nconst handleSwitchClick = (row: any) => {\n    eventBus.emit(\"handleRoleStatusSwitchClick\", row)\n}\n\nexport const columns: DataTableColumns<any> = [\n    {\n        title: \"角色名称\",\n        key: \"roleName\"\n    },\n    {\n        title: \"角色权限字符串\",\n        key: \"roleKey\"\n    },\n    {\n        title: \"状态\",\n        key: \"status\",\n        render: (row) => (\n            h(NSwitch, {\n                value: row.status === \"0\",\n                onClick: () => handleSwitchClick(row)\n            }, {\n                checked: () => \"正常\",\n                unchecked: () => \"禁用\"\n            })\n        )\n    },\n    {\n        title: \"排序\",\n        key: \"sort\",\n        width: 80,\n    },\n    {\n        title: \"创建时间\",\n        key: \"createTime\",\n        width: 200\n    }\n]"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/role/index.vue",
    "content": "<template>\n    <div>\n        <CommonTable :columns=\"columns\"\n                     :pagination=\"tablePagination\"\n                     :loading=\"tableLoading\"\n                     :dataList=\"tableDataList\"\n                     :labelField=\"'title'\"\n                     :actionWidth=\"140\"\n                     :rowKey=\"(row) => row.roleId\"\n                     :scrollX=\"1000\"\n                     @onPageChange=\"onPageChange\"\n                     @onPageSizeChange=\"onPageSizeChange\"\n                     @onEvent=\"onEvent\">>\n            <template #query>\n                <RoleQueryForm @onQuery=\"onQuery\" @onReset=\"onReset\"/>\n            </template>\n        </CommonTable>\n        <RoleEditForm ref=\"roleEditFormRef\"/>\n    </div>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, onMounted, onUnmounted, ref, watch} from \"vue\";\nimport CommonTable from \"@/components/commonTable/index.vue\";\nimport {columns} from \"@/views/pages/system/role/columns\";\nimport {useCommonTable} from \"@/components/commonTable/useCommonTable\";\nimport {roleController} from \"@/api\";\nimport RoleQueryForm from \"@/views/pages/system/role/roleQueryForm.vue\";\nimport RoleEditForm from \"@/views/pages/system/role/roleEditForm.vue\";\nimport {eventBus} from \"@/config/eventBus\";\nimport {tableActionEnums} from \"@/enums/tableActionEnums\";\n\nexport default defineComponent({\n    components: {\n        CommonTable, RoleQueryForm, RoleEditForm\n    },\n    setup() {\n        const {\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            onPageChange,\n            onPageSizeChange,\n            getDataList\n        } = useCommonTable(roleController.list)\n        const roleEditFormRef = ref(null)\n\n        // @ts-ignore\n        watch(() => roleEditFormRef?.value?.modalLoading, () => getDataList(), {deep: true})\n\n\n        const onEvent = (value: any) => {\n            switch (value.type) {\n                case tableActionEnums.ADD:\n                    // @ts-ignore\n                    roleEditFormRef?.value?.onAdd()\n                    break\n                case tableActionEnums.EDIT:\n                    // @ts-ignore\n                    roleEditFormRef?.value?.onEdit(value.row, value.row.roleId)\n                    break\n                case tableActionEnums.DELETE:\n                    // @ts-ignore\n                    roleEditFormRef?.value?.onDeleteById(value.row.roleId)\n                    break\n                case tableActionEnums.BATCH_DELETE:\n                    // @ts-ignore\n                    roleEditFormRef?.value?.onDeleteByIds(value.data)\n                    break\n            }\n        }\n\n        const onQuery = (value: any) => {\n            getDataList(value)\n        }\n\n        const onReset = (value: any) => {\n            getDataList(value)\n        }\n\n        onMounted(() => {\n            getDataList()\n            eventBus.on(\"handleRoleStatusSwitchClick\", (row: any) => {\n                roleController.updateStatus({\n                    roleId: row.roleId,\n                    status: [0, \"0\"].includes(row.status) ? \"1\" : \"0\"\n                }).then(res => {\n                    window.$message.success(res.msg)\n                    getDataList()\n                })\n            })\n        })\n\n        onUnmounted(() => {\n            eventBus.off(\"handleRoleStatusSwitchClick\",() => {})\n        })\n\n        return {\n            columns,\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            onPageChange,\n            onPageSizeChange,\n            onEvent,\n            onQuery,\n            onReset,\n            roleEditFormRef\n        }\n    }\n})\n\n</script>\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/role/roleEditForm.vue",
    "content": "<template>\n    <CommonModal v-model:show=\"modalShow\" :title=\"modalTitle\" @onSubmit=\"onSubmit\" style=\"width: 800px\">\n        <n-form\n            ref=\"modalFormRef\"\n            :model=\"modalForm\"\n            :inline=\"false\"\n            :rules=\"rules\">\n            <n-grid :cols=\"24\" :x-gap=\"24\">\n                <n-form-item-gi :span=\"12\" label=\"角色名称\" path=\"roleName\">\n                    <n-input v-model:value=\"modalForm.roleName\" clearable placeholder=\"请输入角色名称\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"角色权限字符串\" path=\"roleKey\">\n                    <n-input v-model:value=\"modalForm.roleKey\" clearable placeholder=\"请输入角色权限字符串\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"角色顺序\" path=\"sort\">\n                    <n-input-number v-model:value=\"modalForm.sort\" clearable placeholder=\"请输入角色顺序\" style=\"width: 100%\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"状态\" path=\"status\">\n                    <n-radio-group v-model:value=\"modalForm.status\" name=\"radioGroup2\" size=\"small\">\n                        <n-radio-button v-for=\"(item, index) in statusDict\" :key=\"index\" :value=\"item.value\">\n                            {{ item.label }}\n                        </n-radio-button>\n                    </n-radio-group>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"24\" label=\"角色权限\" path=\"permissions\">\n                    <n-transfer ref=\"transfer\"\n                                v-model:value=\"modalForm.permissions\"\n                                :render-source-list=\"renderSourceList\"\n                                :source-filterable=\"true\"\n                                :target-filterable=\"true\"\n                                :show-selected=\"true\"\n                                :options=\"options\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"24\" label=\"备注\" path=\"remark\">\n                    <n-input v-model:value=\"modalForm.remark\" clearable type=\"textarea\" placeholder=\"请输入备注\"/>\n                </n-form-item-gi>\n            </n-grid>\n        </n-form>\n    </CommonModal>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, h, onMounted, ref} from \"vue\";\nimport CommonModal from \"@/components/commonModal/index.vue\";\nimport {useCommonModal} from \"@/components/commonModal/useCommonModal\";\nimport {menuController, roleController} from \"@/api\";\nimport {useStaticDict} from \"@/dictionary/useStaticDict\";\nimport {NTree, TransferRenderSourceList} from \"naive-ui\";\nimport {useFieldClone, useTreeToArray} from \"@/hooks/useTree\";\n\nexport default defineComponent({\n    name: \"UserEditForm\",\n    components: {\n        CommonModal\n    },\n    setup() {\n        const {\n            modalShow,\n            modalTitle,\n            modalForm,\n            modalFormRef,\n            modalLoading,\n            onAdd,\n            onEdit,\n            onSubmit,\n            onDeleteById,\n            onDeleteByIds\n        } = useCommonModal(\"角色\", {status: \"0\"},\n            roleController.add,\n            roleController.edit,\n            roleController.deleteById,\n            roleController.deleteByIds,\n            roleController.getRoleDetailsById\n        )\n\n        const menuList = ref<Array<any>>([])\n        const {statusDict} = useStaticDict()\n\n        const rules = {\n            roleName: {type: \"string\", required: true, message: \"请输入角色名称\", trigger: [\"input\", \"blur\"]},\n            roleKey: {type: \"string\", required: true, message: \"请输入角色权限字符串\", trigger: [\"input\", \"blur\"]},\n            sort: {type: \"number\", required: true, message: \"请输入排序\", trigger: [\"input\", \"blur\"]},\n            status: {type: \"string\", required: true, message: \"请选择状态\", trigger: [\"blur\", \"change\"]},\n            permissions: {type: \"array\", required: true, message: \"请选择权限\", trigger: [\"blur\", \"change\"]},\n        }\n\n        const options = ref<Array<any>>([])\n        const renderSourceList: TransferRenderSourceList = ({onCheck, pattern}) => {\n            return h(NTree, {\n                style: \"margin: 0 4px;\",\n                keyField: \"menuId\",\n                labelField: \"menuName\",\n                checkable: true,\n                cascade: false,\n                selectable: false,\n                blockLine: true,\n                checkOnClick: true,\n                defaultExpandAll: false,\n                data: menuList.value,\n                checkedKeys: modalForm.value.permissions,\n                pattern,\n                onUpdateCheckedKeys: (checkedKeys: Array<string | number>) => {\n                    onCheck(checkedKeys)\n                }\n            })\n        }\n\n        const getMenuList = () => {\n            menuController.list({}).then(res => {\n                const temp = useFieldClone(res.data.list, \"menuId\", \"value\")\n                menuList.value = useFieldClone(temp, \"menuName\", \"label\")\n                options.value = useTreeToArray(menuList.value, [])\n            })\n        }\n\n        onMounted(() => {\n            getMenuList()\n        })\n\n\n        return {\n            modalShow,\n            modalTitle,\n            modalForm,\n            modalFormRef,\n            modalLoading,\n            rules,\n            onAdd,\n            onEdit,\n            onSubmit,\n            onDeleteById,\n            onDeleteByIds,\n            statusDict,\n            menuList,\n            renderSourceList,\n            options\n        }\n    }\n})\n</script>\n\n<style scoped>\n\n</style>"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/role/roleQueryForm.vue",
    "content": "<template>\n    <CommonForm\n        :submitButtonText=\"'查询'\"\n        @onSubmit=\"query\"\n        @onReset=\"reset\">\n        <n-form\n            ref=\"formRef\"\n            :model=\"formValue\"\n            :inline=\"true\"\n            :label-width=\"80\">\n            <n-grid\n                x-gap=\"12\"\n                :cols=\"4\">\n                <n-grid-item>\n                    <n-form-item label=\"角色名称\" path=\"roleName\">\n                        <n-input v-model:value=\"formValue.roleName\" placeholder=\"请输入角色名称\" clearable v-focus/>\n                    </n-form-item>\n                </n-grid-item>\n                <n-grid-item>\n                    <n-form-item label=\"角色权限字符串\" path=\"roleKey\">\n                        <n-input v-model:value=\"formValue.roleKey\" placeholder=\"请输入角色权限字符串\" clearable/>\n                    </n-form-item>\n                </n-grid-item>\n                <n-grid-item>\n                    <n-form-item label=\"状态\" path=\"status\">\n                        <n-select v-model:value=\"formValue.status\" clearable :options=\"statusDict\"/>\n                    </n-form-item>\n                </n-grid-item>\n                <n-grid-item>\n                    <n-form-item label=\"起止时间\" path=\"time\">\n                        <n-date-picker v-model:formatted-value=\"formValue.time\"\n                                       clearable\n                                       value-format=\"yyyy-MM-dd HH:mm:ss\"\n                                       update-value-on-close type=\"datetimerange\" :actions=\"['confirm', 'clear']\"/>\n                    </n-form-item>\n                </n-grid-item>\n            </n-grid>\n        </n-form>\n    </CommonForm>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent} from \"vue\";\nimport CommonForm from \"@/components/commonForm/index.vue\";\nimport {useCommonForm} from \"@/components/commonForm/useCommonForm\";\nimport {useStaticDict} from \"@/dictionary/useStaticDict\";\n\nexport default defineComponent({\n    name: \"RoleQueryValue\",\n    components: {\n        CommonForm\n    },\n    emits: [\"onQuery\", \"onReset\"],\n    setup(props, context) {\n        const {\n            formRef,\n            formValue,\n            onReset,\n            onQuery\n        } = useCommonForm({})\n\n        const {statusDict} = useStaticDict()\n\n        const query = () => {\n            onQuery().then(res => {\n                context.emit(\"onQuery\", res)\n            })\n        }\n\n        const reset = () => {\n            onReset().then(res => {\n                context.emit(\"onReset\", res)\n            })\n        }\n\n        return {\n            formRef,\n            formValue,\n            query,\n            reset,\n            statusDict\n        }\n    }\n})\n</script>"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/user/columns.tsx",
    "content": "import {DataTableColumns, NImage, NSpace, NSwitch, NTag} from \"naive-ui\";\nimport {useColumns} from \"@/views/pages/system/user/useColumns\";\nimport {setting} from \"@/config/setting\";\nimport {h} from \"vue\";\nimport {eventBus} from \"@/config/eventBus\";\n\nconst {logo} = setting\n\nconst {\n    useRenderSexById,\n    useRenderDeptNameById,\n    useRenderRoleNameByIds,\n    useRenderPostNameByIds\n} = useColumns()\n\n/**\n * 状态的点击事件\n * @param row\n */\nconst handleSwitchClick = (row: any) => {\n    eventBus.emit(\"handleUserStatusSwitchClick\", row)\n}\n\nexport const columns: DataTableColumns<any> = [\n    {\n        title: \"用户昵称\",\n        key: \"nickName\"\n    },\n    {\n        title: \"用户账号\",\n        key: \"userName\"\n    },\n    {\n        title: \"头像\",\n        key: \"avatar\",\n        width: 80,\n        render: (row) => (\n            <NImage width=\"30\" style=\"border-radius: 50%;\" src={logo}/>\n        )\n    },\n    {\n        title: \"性别\",\n        key: \"sex\",\n        width: 80,\n        render: (row) => (\n            <span>{useRenderSexById(row.sex)}</span>\n        )\n    },\n    {\n        title: \"状态\",\n        key: \"status\",\n        width: 150,\n        render: (row) => (\n            h(NSwitch, {\n                value: row.status === \"0\",\n                onClick: () => handleSwitchClick(row)\n            }, {\n                checked: () => \"正常\",\n                unchecked: () => \"禁用\"\n            })\n        )\n    },\n    {\n        title: \"部门\",\n        key: \"deptId\",\n        width: 300,\n        ellipsis: {\n            tooltip: true\n        },\n        render: (row) => (\n            <span>{useRenderDeptNameById(row.deptId)}</span>\n        )\n    },\n    {\n        title: \"岗位\",\n        key: \"postIds\",\n        width: 200,\n        render: (row) => (\n            <NSpace>\n                {useRenderPostNameByIds(row.postIds)?.map(post => <NTag key={post}>{post}</NTag>)}\n            </NSpace>\n        )\n    },\n    {\n        title: \"角色\",\n        key: \"roleIds\",\n        width: 200,\n        ellipsis: {\n            tooltip: true\n        },\n        render: (row) => (\n            <NSpace>\n                {useRenderRoleNameByIds(row.roleIds)?.map(role => <NTag key={role}>{role}</NTag>)}\n            </NSpace>\n        )\n    },\n    {\n        title: \"手机号码\",\n        key: \"phoneNumber\"\n    },\n    {\n        title: \"创建时间\",\n        key: \"createTime\",\n        width: 200\n    },\n    {\n        title: \"更新时间\",\n        key: \"updateTime\",\n        width: 200\n    }\n]"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/user/index.vue",
    "content": "<template>\n    <div>\n        <CommonTable :columns=\"columns\"\n                     :dataList=\"tableDataList\"\n                     :labelField=\"'title'\"\n                     :rowKey=\"(row) => row.userId\"\n                     :actionColData=\"actionCol\"\n                     :actionWidth=\"220\"\n                     :pagination=\"tablePagination\"\n                     :loading=\"tableLoading\"\n                     :scrollX=\"2200\"\n                     @onPageChange=\"onPageChange\"\n                     @onPageSizeChange=\"onPageSizeChange\"\n                     @onEvent=\"onEvent\">\n            <template #query>\n                <UserQueryForm @onQuery=\"onQuery\" @onReset=\"onReset\"/>\n            </template>\n        </CommonTable>\n        <UserEditForm ref=\"userEditFormRef\"/>\n    </div>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent, onMounted, onUnmounted, ref, watch} from \"vue\";\nimport CommonTable from \"@/components/commonTable/index.vue\"\nimport {columns} from \"@/views/pages/system/user/columns\";\nimport {userController} from \"@/api\";\nimport UserEditForm from \"@/views/pages/system/user/userEditForm.vue\";\nimport UserQueryForm from \"@/views/pages/system/user/userQueryForm.vue\";\nimport {useCommonTable} from \"@/components/commonTable/useCommonTable\";\nimport {eventBus} from \"@/config/eventBus\";\nimport {tableActionEnums} from \"@/enums/tableActionEnums\";\n\nexport default defineComponent({\n    components: {\n        UserEditForm, CommonTable, UserQueryForm\n    },\n    setup() {\n\n        const {\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            onPageChange,\n            onPageSizeChange,\n            getDataList\n        } = useCommonTable(userController.getUserList)\n        const userEditFormRef = ref(null)\n        const userId = ref<number | undefined>(undefined)\n        // 操作列\n        const actionCol = ref<Array<any>>([\n            {\n                title: \"重置密码\",\n                colorType: \"success\",\n                actionType: 4,\n            }\n        ])\n\n        // @ts-ignore\n        watch(() => userEditFormRef?.value?.modalLoading, () => getDataList(), {deep: true})\n\n        const onEvent = (value: any) => {\n            switch (value.type) {\n                case tableActionEnums.ADD:\n                    // @ts-ignore\n                    userEditFormRef?.value?.onAdd()\n                    break\n                case tableActionEnums.EDIT:\n                    // @ts-ignore\n                    userEditFormRef?.value?.onEdit(value.row)\n                    break\n                case tableActionEnums.DELETE:\n                    // @ts-ignore\n                    userEditFormRef?.value?.onDeleteById(value.row.userId)\n                    break\n                case tableActionEnums.BATCH_DELETE:\n                    // @ts-ignore\n                    userEditFormRef?.value?.onDeleteByIds(value.data)\n                    break\n                // 重置密码\n                case 4:\n                    window.$message.warning(\"您点击了重置密码\")\n                    break\n            }\n        }\n\n        const onQuery = (value: any) => {\n            getDataList(value)\n        }\n\n        const onReset = (value: any) => {\n            getDataList(value)\n        }\n\n        onMounted(() => {\n            getDataList()\n            eventBus.on(\"handleUserStatusSwitchClick\", (row: any) => {\n                userController.updateStatus({\n                    userId: row.userId,\n                    status: [0, \"0\"].includes(row.status) ? \"1\" : \"0\"\n                }).then(res => {\n                    window.$message.success(res.msg)\n                    getDataList()\n                })\n            })\n        })\n\n        onUnmounted(() => {\n            eventBus.off(\"handleUserStatusSwitchClick\",() => {})\n        })\n\n        return {\n            columns,\n            tableDataList,\n            tablePagination,\n            tableLoading,\n            actionCol,\n            onPageChange,\n            onPageSizeChange,\n            onEvent,\n            userEditFormRef,\n            userId,\n            onQuery,\n            onReset\n        }\n    }\n})\n</script>\n"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/user/useColumns.ts",
    "content": "import {useStaticDict} from \"@/dictionary/useStaticDict\";\nimport {useDynamicDict} from \"@/dictionary/useDynamicDict\";\nimport {useArraySeparator, useFindParentNodes, useTreeToArray} from \"@/hooks/useTree\";\n\nexport const useColumns = () => {\n    const {statusDict, sexDict} = useStaticDict()\n    const {deptDict, roleDict, postDict} = useDynamicDict()\n\n    const useRenderSexById = (sexId: string) => {\n        const temp = sexDict.value.filter(sex => {\n            return sex.value === sexId\n        })\n        return temp.length !== 0 ? temp[0].label : undefined;\n    }\n\n    const useRenderDeptNameById = (deptId: string | number) => {\n        const dataList = useFindParentNodes(\"deptId\", deptId, \"children\", deptDict.value)\n        return useArraySeparator(dataList as Array<any>, \"deptName\")\n    }\n\n    const useRenderRoleNameByIds = (roleIds: Array<string | number>) => {\n        const temp = roleDict.value.filter(role => {\n            return roleIds.includes(role.roleId)\n        })\n        if (temp.length !== 0) {\n            return temp.map(role => role.roleName)\n        }\n    }\n\n    const useRenderPostNameByIds = (postIds: Array<string | number>) => {\n        const temp = postDict.value.filter(post => {\n            return postIds.includes(post.postId)\n        })\n        if (temp.length !== 0) {\n            return temp.map(post => post.postName)\n        }\n    }\n\n\n    return {\n        useRenderSexById,\n        useRenderDeptNameById,\n        useRenderRoleNameByIds,\n        useRenderPostNameByIds\n    }\n}"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/user/userEditForm.vue",
    "content": "<template>\n    <CommonModal v-model:show=\"modalShow\" :title=\"modalTitle\" @onSubmit=\"onSubmit\" style=\"width: 800px\">\n        <n-form\n            ref=\"modalFormRef\"\n            :model=\"modalForm\"\n            :inline=\"false\"\n            :rules=\"rules\">\n            <n-grid :cols=\"24\" :x-gap=\"24\">\n                <n-form-item-gi :span=\"12\" label=\"用户账号\" path=\"userName\">\n                    <n-input v-model:value=\"modalForm.userName\" clearable placeholder=\"请输入用户账号\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"用户昵称\" path=\"nickName\">\n                    <n-input v-model:value=\"modalForm.nickName\" clearable placeholder=\"请输入用户昵称\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"手机号码\" path=\"phoneNumber\">\n                    <n-input v-model:value=\"modalForm.phoneNumber\" clearable placeholder=\"请输入手机号码\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"头像\" path=\"avatar\">\n                    <n-input v-model:value=\"modalForm.avatar\" clearable placeholder=\"请上传头像\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"邮箱\" path=\"email\">\n                    <n-input v-model:value=\"modalForm.email\" clearable placeholder=\"请输入邮箱\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"岗位\" path=\"postIds\">\n                    <n-select v-model:value=\"modalForm.postIds\"\n                              label-field=\"postName\"\n                              value-field=\"postId\"\n                              clearable\n                              filterable\n                              :multiple=\"true\"\n                              :options=\"postDict\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"归属部门\" path=\"deptId\">\n                    <n-cascader value-field=\"deptId\"\n                                label-field=\"deptName\"\n                                v-model:value=\"modalForm.deptId\"\n                                :expand-trigger=\"'click'\"\n                                :options=\"deptDict\"\n                                :check-strategy=\"'all'\"\n                                :show-path=\"true\"\n                                :filterable=\"true\"\n                    />\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"性别\" path=\"sex\">\n                    <n-radio-group v-model:value=\"modalForm.sex\" size=\"small\" name=\"radioGroup1\">\n                        <n-radio-button v-for=\"(item, index) in sexDict\" :key=\"index\" :value=\"item.value\">\n                            {{ item.label }}\n                        </n-radio-button>\n                    </n-radio-group>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"用户角色\" path=\"roleIds\">\n                    <n-select v-model:value=\"modalForm.roleIds\"\n                              label-field=\"roleName\"\n                              value-field=\"roleId\"\n                              clearable\n                              filterable\n                              :multiple=\"true\"\n                              :options=\"roleDict\"/>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"12\" label=\"状态\" path=\"status\">\n                    <n-radio-group v-model:value=\"modalForm.status\" name=\"radioGroup2\" size=\"small\">\n                        <n-radio-button v-for=\"(item, index) in statusDict\" :key=\"index\" :value=\"item.value\">\n                            {{ item.label }}\n                        </n-radio-button>\n                    </n-radio-group>\n                </n-form-item-gi>\n                <n-form-item-gi :span=\"24\" label=\"备注\" path=\"remark\">\n                    <n-input v-model:value=\"modalForm.remark\" clearable type=\"textarea\" placeholder=\"请输入备注\"/>\n                </n-form-item-gi>\n            </n-grid>\n        </n-form>\n    </CommonModal>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent} from \"vue\";\nimport CommonModal from \"@/components/commonModal/index.vue\";\nimport {useCommonModal} from \"@/components/commonModal/useCommonModal\";\nimport {userController} from \"@/api\";\nimport {useDynamicDict} from \"@/dictionary/useDynamicDict\";\nimport {useStaticDict} from \"@/dictionary/useStaticDict\";\n\nexport default defineComponent({\n    name: \"UserEditForm\",\n    components: {\n        CommonModal\n    },\n    setup() {\n        const {\n            modalShow,\n            modalTitle,\n            modalForm,\n            modalFormRef,\n            modalLoading,\n            onAdd,\n            onEdit,\n            onSubmit,\n            onDeleteById,\n            onDeleteByIds\n        } = useCommonModal(\"用户\", {\n                sex: \"0\",\n                status: \"0\"\n            },\n            userController.add,\n            userController.edit,\n            userController.deleteById,\n            userController.deleteByIds\n        )\n\n        const {roleDict, deptDict, postDict} = useDynamicDict()\n        const {statusDict, sexDict} = useStaticDict()\n\n        const rules = {\n            deptId: {type: \"number\", required: true, message: \"请选择归属部门\", trigger: ['blur', 'change']},\n            status: {type: \"string\", required: true, message: \"请选择状态\", trigger: [\"blur\", \"change\"]},\n            postIds: {type: \"array\", required: true, message: \"请选择岗位\", trigger: [\"blur\", \"change\"]},\n            roleIds: {type: \"array\", required: true, message: \"请选择角色\", trigger: [\"blur\", \"change\"]},\n            userName: {type: \"string\", required: true, message: \"请输入用户名\", trigger: [\"input\", \"blur\"]},\n            nickName: {type: \"string\", required: true, message: \"请输入用户昵称\", trigger: [\"input\", \"blur\"]},\n        }\n\n\n        return {\n            modalShow,\n            modalTitle,\n            modalForm,\n            modalFormRef,\n            modalLoading,\n            rules,\n            onAdd,\n            onEdit,\n            onSubmit,\n            onDeleteById,\n            onDeleteByIds,\n            statusDict,\n            sexDict,\n            roleDict,\n            deptDict,\n            postDict\n        }\n    }\n})\n</script>\n\n<style scoped>\n\n</style>"
  },
  {
    "path": "ginyi-vue3/src/views/pages/system/user/userQueryForm.vue",
    "content": "<template>\n    <CommonForm\n        :submitButtonText=\"'查询'\"\n        @onSubmit=\"query\"\n        @onReset=\"reset\">\n        <n-form\n            ref=\"formRef\"\n            :model=\"formValue\"\n            :inline=\"true\"\n            :label-width=\"80\">\n            <n-grid\n                x-gap=\"12\"\n                :cols=\"5\">\n                <n-grid-item>\n                    <n-form-item label=\"用户账号\" path=\"userName\">\n                        <n-input v-model:value=\"formValue.userName\" placeholder=\"请输入用户账号\" clearable v-focus/>\n                    </n-form-item>\n                </n-grid-item>\n                <n-grid-item>\n                    <n-form-item label=\"用户昵称\" path=\"user.nickName\">\n                        <n-input v-model:value=\"formValue.nickName\" placeholder=\"请输入昵称\" clearable/>\n                    </n-form-item>\n                </n-grid-item>\n                <n-grid-item>\n                    <n-form-item label=\"状态\" path=\"status\">\n                        <n-select v-model:value=\"formValue.status\" clearable :options=\"statusDict\"/>\n                    </n-form-item>\n                </n-grid-item>\n                <n-grid-item>\n                    <n-form-item label=\"手机号码\" path=\"phoneNumber\">\n                        <n-input v-model:value=\"formValue.phoneNumber\" placeholder=\"请输入手机号码\" clearable/>\n                    </n-form-item>\n                </n-grid-item>\n                <n-grid-item>\n                    <n-form-item label=\"起止时间\" path=\"time\">\n                        <n-date-picker v-model:formatted-value=\"formValue.time\"\n                                       clearable\n                                       value-format=\"yyyy-MM-dd HH:mm:ss\"\n                                       update-value-on-close type=\"datetimerange\" :actions=\"['confirm', 'clear']\"/>\n                    </n-form-item>\n                </n-grid-item>\n            </n-grid>\n        </n-form>\n    </CommonForm>\n</template>\n\n<script lang=\"ts\">\nimport {defineComponent} from \"vue\";\nimport CommonForm from \"@/components/commonForm/index.vue\";\nimport {useCommonForm} from \"@/components/commonForm/useCommonForm\";\nimport {useStaticDict} from \"@/dictionary/useStaticDict\";\n\nexport default defineComponent({\n    name: \"UserQueryValue\",\n    components: {\n        CommonForm\n    },\n    emits: [\"onQuery\", \"onReset\"],\n    setup(props, context) {\n        const {\n            formRef,\n            formValue,\n            onReset,\n            onQuery\n        } = useCommonForm({})\n\n        const {statusDict} = useStaticDict()\n\n        const query = () => {\n            onQuery().then(res => {\n                context.emit(\"onQuery\", res)\n            })\n        }\n\n        const reset = () => {\n            onReset().then(res => {\n                context.emit(\"onReset\", res)\n            })\n        }\n\n        return {\n            formRef, formValue,\n            query,\n            reset,\n            statusDict\n        }\n    }\n})\n</script>"
  },
  {
    "path": "ginyi-vue3/src/views/pages/systools/code/index.vue",
    "content": "<template>\n    <div>\n        代码生成\n    </div>\n</template>\n"
  },
  {
    "path": "ginyi-vue3/src/vite-env.d.ts",
    "content": "import {MessageApi} from \"naive-ui/es/message\";\nimport {NotificationApi} from \"naive-ui/es/notification\";\nimport {DialogApi} from \"naive-ui/es/dialog\";\nimport {LoadingBarApi} from \"naive-ui/es/loading-bar\";\n\ndeclare global {\n\n    interface Window {\n        $message: MessageApi;\n        $dialog: DialogApi;\n        $notification: NotificationApi;\n        $loading: LoadingBarApi;\n    }\n\n    declare module '*.svg'\n    declare module '*.png'\n    declare module '*.jpg'\n    declare module '*.jpeg'\n    declare module '*.gif'\n    declare module '*.bmp'\n    declare module '*.tiff'\n}\n\n\n"
  },
  {
    "path": "ginyi-vue3/tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"target\": \"ESNext\",\n        \"useDefineForClassFields\": true,\n        \"module\": \"ESNext\",\n        \"moduleResolution\": \"Node\",\n        \"strict\": true,\n        \"jsx\": \"preserve\",\n        \"resolveJsonModule\": true,\n        \"isolatedModules\": true,\n        \"esModuleInterop\": true,\n        \"lib\": [\"ESNext\", \"DOM\"],\n        \"skipLibCheck\": true,\n        \"noEmit\": true,\n        \"baseUrl\": \".\",\n        \"paths\": {\n            \"@/*\": [\n                \"src/*\"\n            ]\n        },\n        \"types\": [\"vite/client\"]\n    },\n    \"include\": [\"src/**/*.ts\", \"src/**/*.d.ts\", \"src/**/*.tsx\", \"src/**/*.vue\"],\n    \"references\": [\n        {\"path\": \"./tsconfig.node.json\"}\n    ]\n}\n"
  },
  {
    "path": "ginyi-vue3/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "ginyi-vue3/vite.config.ts",
    "content": "import {defineConfig} from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport {resolve} from 'path'\nimport vueJsxPlugin from \"@vitejs/plugin-vue-jsx\";\n\n\nexport default defineConfig({\n    envDir: \".\",\n    plugins: [vue(), vueJsxPlugin()],\n    server: {\n        host: '0.0.0.0',    // 主机地址\n        open: true,         // 自动打开浏览器\n        port: 3800          // 服务端口\n    },\n    // 路径别名\n    resolve: {\n        alias: {\n            '@': resolve(__dirname, './src')\n        }\n    },\n    optimizeDeps: {\n        include: [\n            `monaco-editor/esm/vs/language/json/json.worker`,\n            `monaco-editor/esm/vs/language/css/css.worker`,\n            `monaco-editor/esm/vs/language/html/html.worker`,\n            `monaco-editor/esm/vs/language/typescript/ts.worker`,\n            `monaco-editor/esm/vs/editor/editor.worker`\n        ],\n    }\n})"
  },
  {
    "path": "ginyi-vue3/web-types.json",
    "content": "{\n    \"$schema\": \"https://json.schemastore.org/web-types\",\n    \"framework\": \"vue\",\n    \"name\": \"\",\n    \"version\": \"\",\n    \"contributions\": {\n        \"html\": {\n            \"js-types-syntax\": \"typescript\",\n            \"attributes\": [\n                {\n                    \"name\": \"v-focus\",\n                    \"description\": \"自动获取输入框焦点\\n1：naive-ui：v-focus \\n2：原生控件：v-focus.native\"\n                },\n                {\n                    \"name\": \"v-loading\",\n                    \"description\": \"加载中\\ntrue开启\\nfalse关闭\"\n                },\n                {\n                     \"name\": \"v-drag\",\n                     \"description\": \"使元素可被拖拽\"\n                }\n            ]\n        }\n    }\n}\n"
  }
]