Repository: elunez/eladmin Branch: master Commit: be2f1370f3b9 Files: 305 Total size: 879.7 KB Directory structure: gitextract_rbfjroal/ ├── .gitignore ├── LICENSE ├── README.md ├── eladmin-common/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── me/ │ │ └── zhengjie/ │ │ ├── annotation/ │ │ │ ├── DataPermission.java │ │ │ ├── Limit.java │ │ │ ├── Query.java │ │ │ └── rest/ │ │ │ ├── AnonymousAccess.java │ │ │ ├── AnonymousDeleteMapping.java │ │ │ ├── AnonymousGetMapping.java │ │ │ ├── AnonymousPatchMapping.java │ │ │ ├── AnonymousPostMapping.java │ │ │ └── AnonymousPutMapping.java │ │ ├── aspect/ │ │ │ ├── LimitAspect.java │ │ │ └── LimitType.java │ │ ├── base/ │ │ │ ├── BaseDTO.java │ │ │ ├── BaseEntity.java │ │ │ └── BaseMapper.java │ │ ├── config/ │ │ │ ├── AsyncExecutor.java │ │ │ ├── AuditorConfig.java │ │ │ ├── AuthorityConfig.java │ │ │ ├── CustomP6SpyLogger.java │ │ │ ├── RedisConfiguration.java │ │ │ ├── RedissonConfiguration.java │ │ │ ├── RemoveDruidAdConfig.java │ │ │ ├── properties/ │ │ │ │ ├── FileProperties.java │ │ │ │ └── RsaProperties.java │ │ │ └── webConfig/ │ │ │ ├── ConfigurerAdapter.java │ │ │ ├── MultipartConfig.java │ │ │ ├── QueryCustomizer.java │ │ │ ├── SwaggerConfig.java │ │ │ ├── SwaggerDataConfig.java │ │ │ └── WebSocketConfig.java │ │ ├── exception/ │ │ │ ├── BadRequestException.java │ │ │ ├── EntityExistException.java │ │ │ ├── EntityNotFoundException.java │ │ │ └── handler/ │ │ │ ├── ApiError.java │ │ │ └── GlobalExceptionHandler.java │ │ └── utils/ │ │ ├── AnonTagUtils.java │ │ ├── BigDecimalUtils.java │ │ ├── CacheKey.java │ │ ├── CloseUtil.java │ │ ├── DateUtil.java │ │ ├── ElConstant.java │ │ ├── EncryptUtils.java │ │ ├── FileUtil.java │ │ ├── PageResult.java │ │ ├── PageUtil.java │ │ ├── QueryHelp.java │ │ ├── RedisUtils.java │ │ ├── RequestHolder.java │ │ ├── RsaUtils.java │ │ ├── SecurityUtils.java │ │ ├── SpringBeanHolder.java │ │ ├── StringUtils.java │ │ ├── ThrowableUtil.java │ │ ├── ValidationUtil.java │ │ └── enums/ │ │ ├── CodeBiEnum.java │ │ ├── CodeEnum.java │ │ ├── DataScopeEnum.java │ │ └── RequestMethodEnum.java │ └── test/ │ └── java/ │ └── me/ │ └── zhengjie/ │ └── utils/ │ ├── DateUtilsTest.java │ ├── EncryptUtilsTest.java │ ├── FileUtilTest.java │ └── StringUtilsTest.java ├── eladmin-generator/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── me/ │ │ └── zhengjie/ │ │ ├── domain/ │ │ │ ├── ColumnInfo.java │ │ │ ├── GenConfig.java │ │ │ └── vo/ │ │ │ └── TableInfo.java │ │ ├── repository/ │ │ │ ├── ColumnInfoRepository.java │ │ │ └── GenConfigRepository.java │ │ ├── rest/ │ │ │ ├── GenConfigController.java │ │ │ └── GeneratorController.java │ │ ├── service/ │ │ │ ├── GenConfigService.java │ │ │ ├── GeneratorService.java │ │ │ └── impl/ │ │ │ ├── GenConfigServiceImpl.java │ │ │ └── GeneratorServiceImpl.java │ │ └── utils/ │ │ ├── ColUtil.java │ │ └── GenUtil.java │ └── resources/ │ ├── gen.properties │ └── template/ │ ├── admin/ │ │ ├── Controller.ftl │ │ ├── Dto.ftl │ │ ├── Entity.ftl │ │ ├── Mapper.ftl │ │ ├── QueryCriteria.ftl │ │ ├── Repository.ftl │ │ ├── Service.ftl │ │ └── ServiceImpl.ftl │ └── front/ │ ├── api.ftl │ └── index.ftl ├── eladmin-logging/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── me/ │ └── zhengjie/ │ ├── annotation/ │ │ └── Log.java │ ├── aspect/ │ │ └── LogAspect.java │ ├── domain/ │ │ └── SysLog.java │ ├── repository/ │ │ └── LogRepository.java │ ├── rest/ │ │ └── SysLogController.java │ └── service/ │ ├── SysLogService.java │ ├── dto/ │ │ ├── SysLogErrorDto.java │ │ ├── SysLogQueryCriteria.java │ │ └── SysLogSmallDto.java │ ├── impl/ │ │ └── SysLogServiceImpl.java │ └── mapstruct/ │ ├── LogErrorMapper.java │ └── LogSmallMapper.java ├── eladmin-system/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── me/ │ │ │ └── zhengjie/ │ │ │ ├── AppRun.java │ │ │ ├── modules/ │ │ │ │ ├── maint/ │ │ │ │ │ ├── domain/ │ │ │ │ │ │ ├── App.java │ │ │ │ │ │ ├── Database.java │ │ │ │ │ │ ├── Deploy.java │ │ │ │ │ │ ├── DeployHistory.java │ │ │ │ │ │ ├── ServerDeploy.java │ │ │ │ │ │ └── enums/ │ │ │ │ │ │ └── DataTypeEnum.java │ │ │ │ │ ├── repository/ │ │ │ │ │ │ ├── AppRepository.java │ │ │ │ │ │ ├── DatabaseRepository.java │ │ │ │ │ │ ├── DeployHistoryRepository.java │ │ │ │ │ │ ├── DeployRepository.java │ │ │ │ │ │ └── ServerDeployRepository.java │ │ │ │ │ ├── rest/ │ │ │ │ │ │ ├── AppController.java │ │ │ │ │ │ ├── DatabaseController.java │ │ │ │ │ │ ├── DeployController.java │ │ │ │ │ │ ├── DeployHistoryController.java │ │ │ │ │ │ └── ServerDeployController.java │ │ │ │ │ ├── service/ │ │ │ │ │ │ ├── AppService.java │ │ │ │ │ │ ├── DatabaseService.java │ │ │ │ │ │ ├── DeployHistoryService.java │ │ │ │ │ │ ├── DeployService.java │ │ │ │ │ │ ├── ServerDeployService.java │ │ │ │ │ │ ├── dto/ │ │ │ │ │ │ │ ├── AppDto.java │ │ │ │ │ │ │ ├── AppQueryCriteria.java │ │ │ │ │ │ │ ├── DatabaseDto.java │ │ │ │ │ │ │ ├── DatabaseQueryCriteria.java │ │ │ │ │ │ │ ├── DeployDto.java │ │ │ │ │ │ │ ├── DeployHistoryDto.java │ │ │ │ │ │ │ ├── DeployHistoryQueryCriteria.java │ │ │ │ │ │ │ ├── DeployQueryCriteria.java │ │ │ │ │ │ │ ├── ServerDeployDto.java │ │ │ │ │ │ │ └── ServerDeployQueryCriteria.java │ │ │ │ │ │ ├── impl/ │ │ │ │ │ │ │ ├── AppServiceImpl.java │ │ │ │ │ │ │ ├── DatabaseServiceImpl.java │ │ │ │ │ │ │ ├── DeployHistoryServiceImpl.java │ │ │ │ │ │ │ ├── DeployServiceImpl.java │ │ │ │ │ │ │ └── ServerDeployServiceImpl.java │ │ │ │ │ │ └── mapstruct/ │ │ │ │ │ │ ├── AppMapper.java │ │ │ │ │ │ ├── DatabaseMapper.java │ │ │ │ │ │ ├── DeployHistoryMapper.java │ │ │ │ │ │ ├── DeployMapper.java │ │ │ │ │ │ └── ServerDeployMapper.java │ │ │ │ │ ├── util/ │ │ │ │ │ │ ├── ExecuteShellUtil.java │ │ │ │ │ │ ├── ScpClientUtil.java │ │ │ │ │ │ └── SqlUtils.java │ │ │ │ │ └── websocket/ │ │ │ │ │ ├── MsgType.java │ │ │ │ │ ├── SocketMsg.java │ │ │ │ │ └── WebSocketServer.java │ │ │ │ ├── quartz/ │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── JobRunner.java │ │ │ │ │ │ └── QuartzConfig.java │ │ │ │ │ ├── domain/ │ │ │ │ │ │ ├── QuartzJob.java │ │ │ │ │ │ └── QuartzLog.java │ │ │ │ │ ├── repository/ │ │ │ │ │ │ ├── QuartzJobRepository.java │ │ │ │ │ │ └── QuartzLogRepository.java │ │ │ │ │ ├── rest/ │ │ │ │ │ │ └── QuartzJobController.java │ │ │ │ │ ├── service/ │ │ │ │ │ │ ├── QuartzJobService.java │ │ │ │ │ │ ├── dto/ │ │ │ │ │ │ │ └── JobQueryCriteria.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ └── QuartzJobServiceImpl.java │ │ │ │ │ ├── task/ │ │ │ │ │ │ └── TestTask.java │ │ │ │ │ └── utils/ │ │ │ │ │ ├── ExecutionJob.java │ │ │ │ │ ├── QuartzManage.java │ │ │ │ │ └── QuartzRunnable.java │ │ │ │ ├── security/ │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── CaptchaConfig.java │ │ │ │ │ │ ├── LoginProperties.java │ │ │ │ │ │ ├── SecurityProperties.java │ │ │ │ │ │ ├── SpringSecurityConfig.java │ │ │ │ │ │ └── enums/ │ │ │ │ │ │ └── LoginCodeEnum.java │ │ │ │ │ ├── rest/ │ │ │ │ │ │ ├── AuthController.java │ │ │ │ │ │ └── OnlineController.java │ │ │ │ │ ├── security/ │ │ │ │ │ │ ├── JwtAccessDeniedHandler.java │ │ │ │ │ │ ├── JwtAuthenticationEntryPoint.java │ │ │ │ │ │ ├── TokenConfigurer.java │ │ │ │ │ │ ├── TokenFilter.java │ │ │ │ │ │ └── TokenProvider.java │ │ │ │ │ └── service/ │ │ │ │ │ ├── OnlineUserService.java │ │ │ │ │ ├── UserCacheManager.java │ │ │ │ │ ├── UserDetailsServiceImpl.java │ │ │ │ │ └── dto/ │ │ │ │ │ ├── AuthUserDto.java │ │ │ │ │ ├── AuthorityDto.java │ │ │ │ │ ├── JwtUserDto.java │ │ │ │ │ └── OnlineUserDto.java │ │ │ │ └── system/ │ │ │ │ ├── domain/ │ │ │ │ │ ├── Dept.java │ │ │ │ │ ├── Dict.java │ │ │ │ │ ├── DictDetail.java │ │ │ │ │ ├── Job.java │ │ │ │ │ ├── Menu.java │ │ │ │ │ ├── Role.java │ │ │ │ │ ├── User.java │ │ │ │ │ └── vo/ │ │ │ │ │ ├── MenuMetaVo.java │ │ │ │ │ ├── MenuVo.java │ │ │ │ │ └── UserPassVo.java │ │ │ │ ├── repository/ │ │ │ │ │ ├── DeptRepository.java │ │ │ │ │ ├── DictDetailRepository.java │ │ │ │ │ ├── DictRepository.java │ │ │ │ │ ├── JobRepository.java │ │ │ │ │ ├── MenuRepository.java │ │ │ │ │ ├── RoleRepository.java │ │ │ │ │ └── UserRepository.java │ │ │ │ ├── rest/ │ │ │ │ │ ├── DeptController.java │ │ │ │ │ ├── DictController.java │ │ │ │ │ ├── DictDetailController.java │ │ │ │ │ ├── JobController.java │ │ │ │ │ ├── LimitController.java │ │ │ │ │ ├── MenuController.java │ │ │ │ │ ├── MonitorController.java │ │ │ │ │ ├── RoleController.java │ │ │ │ │ ├── UserController.java │ │ │ │ │ └── VerifyController.java │ │ │ │ └── service/ │ │ │ │ ├── DataService.java │ │ │ │ ├── DeptService.java │ │ │ │ ├── DictDetailService.java │ │ │ │ ├── DictService.java │ │ │ │ ├── JobService.java │ │ │ │ ├── MenuService.java │ │ │ │ ├── MonitorService.java │ │ │ │ ├── RoleService.java │ │ │ │ ├── UserService.java │ │ │ │ ├── VerifyService.java │ │ │ │ ├── dto/ │ │ │ │ │ ├── DeptDto.java │ │ │ │ │ ├── DeptQueryCriteria.java │ │ │ │ │ ├── DeptSmallDto.java │ │ │ │ │ ├── DictDetailDto.java │ │ │ │ │ ├── DictDetailQueryCriteria.java │ │ │ │ │ ├── DictDto.java │ │ │ │ │ ├── DictQueryCriteria.java │ │ │ │ │ ├── DictSmallDto.java │ │ │ │ │ ├── JobDto.java │ │ │ │ │ ├── JobQueryCriteria.java │ │ │ │ │ ├── JobSmallDto.java │ │ │ │ │ ├── MenuDto.java │ │ │ │ │ ├── MenuQueryCriteria.java │ │ │ │ │ ├── RoleDto.java │ │ │ │ │ ├── RoleQueryCriteria.java │ │ │ │ │ ├── RoleSmallDto.java │ │ │ │ │ ├── UserDto.java │ │ │ │ │ └── UserQueryCriteria.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── DataServiceImpl.java │ │ │ │ │ ├── DeptServiceImpl.java │ │ │ │ │ ├── DictDetailServiceImpl.java │ │ │ │ │ ├── DictServiceImpl.java │ │ │ │ │ ├── JobServiceImpl.java │ │ │ │ │ ├── MenuServiceImpl.java │ │ │ │ │ ├── MonitorServiceImpl.java │ │ │ │ │ ├── RoleServiceImpl.java │ │ │ │ │ ├── UserServiceImpl.java │ │ │ │ │ └── VerifyServiceImpl.java │ │ │ │ └── mapstruct/ │ │ │ │ ├── DeptMapper.java │ │ │ │ ├── DeptSmallMapper.java │ │ │ │ ├── DictDetailMapper.java │ │ │ │ ├── DictMapper.java │ │ │ │ ├── DictSmallMapper.java │ │ │ │ ├── JobMapper.java │ │ │ │ ├── JobSmallMapper.java │ │ │ │ ├── MenuMapper.java │ │ │ │ ├── RoleMapper.java │ │ │ │ ├── RoleSmallMapper.java │ │ │ │ └── UserMapper.java │ │ │ └── sysrunner/ │ │ │ └── SystemRunner.java │ │ └── resources/ │ │ ├── banner.txt │ │ ├── config/ │ │ │ ├── application-dev.yml │ │ │ ├── application-prod.yml │ │ │ ├── application-quartz.yml │ │ │ └── application.yml │ │ ├── logback.xml │ │ ├── spy.properties │ │ └── template/ │ │ ├── email.ftl │ │ └── taskAlarm.ftl │ └── test/ │ └── java/ │ └── me/ │ └── zhengjie/ │ └── EladminSystemApplicationTests.java ├── eladmin-tools/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── me/ │ └── zhengjie/ │ ├── config/ │ │ └── AmzS3Config.java │ ├── domain/ │ │ ├── AlipayConfig.java │ │ ├── EmailConfig.java │ │ ├── LocalStorage.java │ │ ├── S3Storage.java │ │ ├── enums/ │ │ │ └── AliPayStatusEnum.java │ │ └── vo/ │ │ ├── EmailVo.java │ │ └── TradeVo.java │ ├── repository/ │ │ ├── AliPayRepository.java │ │ ├── EmailRepository.java │ │ ├── LocalStorageRepository.java │ │ └── S3StorageRepository.java │ ├── rest/ │ │ ├── AliPayController.java │ │ ├── EmailController.java │ │ ├── LocalStorageController.java │ │ └── S3StorageController.java │ ├── service/ │ │ ├── AliPayService.java │ │ ├── EmailService.java │ │ ├── LocalStorageService.java │ │ ├── S3StorageService.java │ │ ├── dto/ │ │ │ ├── LocalStorageDto.java │ │ │ ├── LocalStorageQueryCriteria.java │ │ │ └── S3StorageQueryCriteria.java │ │ ├── impl/ │ │ │ ├── AliPayServiceImpl.java │ │ │ ├── EmailServiceImpl.java │ │ │ ├── LocalStorageServiceImpl.java │ │ │ └── S3StorageServiceImpl.java │ │ └── mapstruct/ │ │ └── LocalStorageMapper.java │ └── utils/ │ └── AlipayUtils.java ├── pom.xml └── sql/ ├── eladmin.sql └── quartz.sql ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ ### IDEA ### .idea/* *.iml */target/* */*.iml /.gradle/ /application.pid ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2019-2023 Zheng Jie Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================

ELADMIN 后台管理系统

[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/elunez/eladmin/blob/master/LICENSE) [![star](https://gitee.com/elunez/eladmin/badge/star.svg?theme=white)](https://gitee.com/elunez/eladmin) [![GitHub stars](https://img.shields.io/github/stars/elunez/eladmin.svg?style=social&label=Stars)](https://github.com/elunez/eladmin) [![GitHub forks](https://img.shields.io/github/forks/elunez/eladmin.svg?style=social&label=Fork)](https://github.com/elunez/eladmin)
#### 项目简介 一个基于 Spring Boot 2.7.18 、 Spring Boot Jpa、 JWT、Spring Security、Redis、Vue的前后端分离的后台管理系统 现已发布基于 mybatis-plus 版本,项目地址:[https://github.com/elunez/eladmin-mp](https://github.com/elunez/eladmin-mp)、[https://gitee.com/elunez/eladmin-mp](https://gitee.com/elunez/eladmin-mp)。 **开发文档:** [https://eladmin.vip](https://eladmin.vip) **体验地址:** [https://eladmin.vip/demo](https://eladmin.vip/demo) **账号密码:** `admin / 123456` #### 项目源码 | | 后端源码 | 前端源码 | |--- |--- | --- | | github | https://github.com/elunez/eladmin | https://github.com/elunez/eladmin-web | | 码云 | https://gitee.com/elunez/eladmin | https://gitee.com/elunez/eladmin-web | #### VPS推荐 使用优惠码: `BWHCGLUKKB`,可获得 6.81% 的折扣 [查看介绍](https://bwhstock.in/) #### 主要特性 - 使用最新技术栈,社区资源丰富。 - 高效率开发,代码生成器可一键生成前后端代码 - 支持数据字典,可方便地对一些状态进行管理 - 支持接口限流,避免恶意请求导致服务层压力过大 - 支持接口级别的功能权限与数据权限,可自定义操作 - 自定义权限注解与匿名接口注解,可快速对接口拦截与放行 - 对一些常用地前端组件封装:表格数据请求、数据字典等 - 前后端统一异常拦截处理,统一输出异常,避免繁琐的判断 - 支持在线用户管理与服务器性能监控,支持限制单用户登录 - 支持运维管理,可方便地对远程服务器的应用进行部署与管理 #### 系统功能 - 用户管理:提供用户的相关配置,新增用户后,默认密码为123456 - 角色管理:对权限与菜单进行分配,可根据部门设置角色的数据权限 - 菜单管理:已实现菜单动态路由,后端可配置化,支持多级菜单 - 部门管理:可配置系统组织架构,树形表格展示 - 岗位管理:配置各个部门的职位 - 字典管理:可维护常用一些固定的数据,如:状态,性别等 - 系统日志:记录用户操作日志与异常日志,方便开发人员定位排错 - SQL监控:采用druid 监控数据库访问性能,默认用户名admin,密码123456 - 定时任务:整合Quartz做定时任务,加入任务日志,任务运行情况一目了然 - 代码生成:高灵活度生成前后端代码,减少大量重复的工作任务 - 邮件工具:配合富文本,发送html格式的邮件 - 亚马逊S3云存储:支持市面上大多数对象存储,兼容亚马逊S3协议,如七牛云,阿里云等 - 支付宝支付:整合了支付宝支付并且提供了测试账号,可自行测试 - 服务监控:监控服务器的负载情况 - 运维管理:一键部署你的应用 #### 项目结构 项目采用按功能分模块的开发方式,结构如下 - `eladmin-common` 为系统的公共模块,各种工具类,公共配置存在该模块 - `eladmin-system` 为系统核心模块也是项目入口模块,也是最终需要打包部署的模块 - `eladmin-logging` 为系统的日志模块,其他模块如果需要记录日志需要引入该模块 - `eladmin-tools` 为第三方工具模块,包含:邮件、亚马逊S3云存储、本地存储、支付宝 - `eladmin-generator` 为系统的代码生成模块,支持生成前后端CRUD代码 #### 详细结构 ``` - eladmin-common 公共模块 - annotation 为系统自定义注解 - aspect 自定义注解的切面 - base 提供了Entity、DTO基类和mapstruct的通用mapper - config 项目通用配置 - Web配置跨域与静态资源映射、Swagger配置,文件上传临时路径配置 - Redis配置,Redission配置, 异步线程池配置 - 权限拦截配置、AuthorityConfig、Druid 删除广告配置 - exception 项目统一异常的处理 - utils 系统通用工具类,列举一些常用的工具类 - BigDecimaUtils 金额计算工具类 - RequestHolder 请求工具类 - SecurityUtils 安全工具类 - StringUtils 字符串工具类 - SpringBeanHolder Spring Bean工具类 - RedisUtils Redis工具类 - EncryptUtils 加密工具类 - FileUtil 文件工具类 - eladmin-system 系统核心模块(系统启动入口) - sysrunner 程序启动后处理数据 - modules 系统相关模块(登录授权、系统监控、定时任务、系统模块、运维模块) - eladmin-logging 系统日志模块 - eladmin-tools 系统第三方工具模块 - email 邮件工具 - amazon 亚马逊S3云存储工具 - alipay 支付宝支付工具 - local-storage 本地存储工具 - eladmin-generator 系统代码生成模块 ``` #### 特别鸣谢 - 感谢 [PanJiaChen](https://github.com/PanJiaChen/vue-element-admin) 大佬提供的前端模板 - 感谢 [Moxun](https://github.com/moxun1639) 大佬提供的前端 Curd 通用组件 - 感谢 [zhy6599](https://gitee.com/zhy6599) 大佬提供的后端运维管理相关功能 - 感谢 [j.yao.SUSE](https://github.com/everhopingandwaiting) 大佬提供的匿名接口与Redis限流等功能 - 感谢 [d15801543974](https://github.com/d15801543974) 大佬提供的基于注解的通用查询方式 #### 项目捐赠 项目的发展离不开你的支持,请作者喝杯咖啡吧☕ [Donate](https://eladmin.vip/pages/030101/) #### 反馈交流 - QQ交流群:891137268 、947578238、659622532 ================================================ FILE: eladmin-common/pom.xml ================================================ eladmin me.zhengjie 2.7 4.0.0 5.8.35 eladmin-common 公共模块 cn.hutool hutool-all ${hutool.version} ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/annotation/DataPermission.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** *

* 用于判断是否过滤数据权限 * 1、如果没有用到 @OneToOne 这种关联关系,只需要填写 fieldName [参考:DeptQueryCriteria.class] * 2、如果用到了 @OneToOne ,fieldName 和 joinName 都需要填写,拿UserQueryCriteria.class举例: * 应该是 @DataPermission(joinName = "dept", fieldName = "id") *

* @author Zheng Jie * @website ... * @date 2020-05-07 **/ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DataPermission { /** * Entity 中的字段名称 */ String fieldName() default ""; /** * Entity 中与部门关联的字段名称 */ String joinName() default ""; } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/annotation/Limit.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.annotation; import me.zhengjie.aspect.LimitType; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author jacky */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Limit { // 资源名称,用于描述接口功能 String name() default ""; // 资源 key String key() default ""; // key prefix String prefix() default ""; // 时间的,单位秒 int period(); // 限制访问次数 int count(); // 限制类型 LimitType limitType() default LimitType.CUSTOMER; } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/annotation/Query.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author Zheng Jie * @date 2019-6-4 13:52:30 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Query { // Dong ZhaoYang 2017/8/7 基本对象的属性名 String propName() default ""; // Dong ZhaoYang 2017/8/7 查询方式 Type type() default Type.EQUAL; /** * 连接查询的属性名,如User类中的dept */ String joinName() default ""; /** * 默认左连接 */ Join join() default Join.LEFT; /** * 多字段模糊搜索,仅支持String类型字段,多个用逗号隔开, 如@Query(blurry = "email,username") */ String blurry() default ""; enum Type { // jie 2019/6/4 相等 EQUAL // Dong ZhaoYang 2017/8/7 大于等于 , GREATER_THAN // Dong ZhaoYang 2017/8/7 小于等于 , LESS_THAN // Dong ZhaoYang 2017/8/7 中模糊查询 , INNER_LIKE // Dong ZhaoYang 2017/8/7 左模糊查询 , LEFT_LIKE // Dong ZhaoYang 2017/8/7 右模糊查询 , RIGHT_LIKE // Dong ZhaoYang 2017/8/7 小于 , LESS_THAN_NQ // jie 2019/6/4 包含 , IN // 不包含 , NOT_IN // 不等于 ,NOT_EQUAL // between ,BETWEEN // 不为空 ,NOT_NULL // 为空 ,IS_NULL, // Aborn Jiang 2022/06/01, 对应SQL: SELECT * FROM table WHERE FIND_IN_SET('querytag', table.tags); FIND_IN_SET } /** * @author Zheng Jie * 适用于简单连接查询,复杂的请自定义该注解,或者使用sql查询 */ enum Join { /** jie 2019-6-4 13:18:30 */ LEFT, RIGHT, INNER } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousAccess.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.annotation.rest; import java.lang.annotation.*; /** * @author jacky * 用于标记匿名访问方法 */ @Inherited @Documented @Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface AnonymousAccess { } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousDeleteMapping.java ================================================ /* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.annotation.rest; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Annotation for mapping HTTP {@code DELETE} requests onto specific handler * methods. * 支持匿名访问 DeleteMapping * * @author liaojinlong * @see AnonymousGetMapping * @see AnonymousPostMapping * @see AnonymousPutMapping * @see AnonymousPatchMapping * @see RequestMapping */ @AnonymousAccess @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @RequestMapping(method = RequestMethod.DELETE) public @interface AnonymousDeleteMapping { /** * Alias for {@link RequestMapping#name}. */ @AliasFor(annotation = RequestMapping.class) String name() default ""; /** * Alias for {@link RequestMapping#value}. */ @AliasFor(annotation = RequestMapping.class) String[] value() default {}; /** * Alias for {@link RequestMapping#path}. */ @AliasFor(annotation = RequestMapping.class) String[] path() default {}; /** * Alias for {@link RequestMapping#params}. */ @AliasFor(annotation = RequestMapping.class) String[] params() default {}; /** * Alias for {@link RequestMapping#headers}. */ @AliasFor(annotation = RequestMapping.class) String[] headers() default {}; /** * Alias for {@link RequestMapping#consumes}. */ @AliasFor(annotation = RequestMapping.class) String[] consumes() default {}; /** * Alias for {@link RequestMapping#produces}. */ @AliasFor(annotation = RequestMapping.class) String[] produces() default {}; } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousGetMapping.java ================================================ /* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.annotation.rest; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Annotation for mapping HTTP {@code GET} requests onto specific handler * methods. *

* 支持匿名访问 GetMapping * * @author liaojinlong * @see RequestMapping */ @AnonymousAccess @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @RequestMapping(method = RequestMethod.GET) public @interface AnonymousGetMapping { /** * Alias for {@link RequestMapping#name}. */ @AliasFor(annotation = RequestMapping.class) String name() default ""; /** * Alias for {@link RequestMapping#value}. */ @AliasFor(annotation = RequestMapping.class) String[] value() default {}; /** * Alias for {@link RequestMapping#path}. */ @AliasFor(annotation = RequestMapping.class) String[] path() default {}; /** * Alias for {@link RequestMapping#params}. */ @AliasFor(annotation = RequestMapping.class) String[] params() default {}; /** * Alias for {@link RequestMapping#headers}. */ @AliasFor(annotation = RequestMapping.class) String[] headers() default {}; /** * Alias for {@link RequestMapping#consumes}. * * @since 4.3.5 */ @AliasFor(annotation = RequestMapping.class) String[] consumes() default {}; /** * Alias for {@link RequestMapping#produces}. */ @AliasFor(annotation = RequestMapping.class) String[] produces() default {}; } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPatchMapping.java ================================================ /* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.annotation.rest; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Annotation for mapping HTTP {@code PATCH} requests onto specific handler * methods. * * 支持匿名访问 PatchMapping * * @author liaojinlong * @see AnonymousGetMapping * @see AnonymousPostMapping * @see AnonymousPutMapping * @see AnonymousDeleteMapping * @see RequestMapping */ @AnonymousAccess @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @RequestMapping(method = RequestMethod.PATCH) public @interface AnonymousPatchMapping { /** * Alias for {@link RequestMapping#name}. */ @AliasFor(annotation = RequestMapping.class) String name() default ""; /** * Alias for {@link RequestMapping#value}. */ @AliasFor(annotation = RequestMapping.class) String[] value() default {}; /** * Alias for {@link RequestMapping#path}. */ @AliasFor(annotation = RequestMapping.class) String[] path() default {}; /** * Alias for {@link RequestMapping#params}. */ @AliasFor(annotation = RequestMapping.class) String[] params() default {}; /** * Alias for {@link RequestMapping#headers}. */ @AliasFor(annotation = RequestMapping.class) String[] headers() default {}; /** * Alias for {@link RequestMapping#consumes}. */ @AliasFor(annotation = RequestMapping.class) String[] consumes() default {}; /** * Alias for {@link RequestMapping#produces}. */ @AliasFor(annotation = RequestMapping.class) String[] produces() default {}; } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPostMapping.java ================================================ /* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.annotation.rest; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Annotation for mapping HTTP {@code POST} requests onto specific handler * methods. * 支持匿名访问 PostMapping * * @author liaojinlong * @see AnonymousGetMapping * @see AnonymousPostMapping * @see AnonymousPutMapping * @see AnonymousDeleteMapping * @see RequestMapping */ @AnonymousAccess @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @RequestMapping(method = RequestMethod.POST) public @interface AnonymousPostMapping { /** * Alias for {@link RequestMapping#name}. */ @AliasFor(annotation = RequestMapping.class) String name() default ""; /** * Alias for {@link RequestMapping#value}. */ @AliasFor(annotation = RequestMapping.class) String[] value() default {}; /** * Alias for {@link RequestMapping#path}. */ @AliasFor(annotation = RequestMapping.class) String[] path() default {}; /** * Alias for {@link RequestMapping#params}. */ @AliasFor(annotation = RequestMapping.class) String[] params() default {}; /** * Alias for {@link RequestMapping#headers}. */ @AliasFor(annotation = RequestMapping.class) String[] headers() default {}; /** * Alias for {@link RequestMapping#consumes}. */ @AliasFor(annotation = RequestMapping.class) String[] consumes() default {}; /** * Alias for {@link RequestMapping#produces}. */ @AliasFor(annotation = RequestMapping.class) String[] produces() default {}; } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPutMapping.java ================================================ /* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.annotation.rest; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Annotation for mapping HTTP {@code PUT} requests onto specific handler * methods. * * 支持匿名访问 PutMapping * * @author liaojinlong * @see AnonymousGetMapping * @see AnonymousPostMapping * @see AnonymousPutMapping * @see AnonymousDeleteMapping * @see RequestMapping */ @AnonymousAccess @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @RequestMapping(method = RequestMethod.PUT) public @interface AnonymousPutMapping { /** * Alias for {@link RequestMapping#name}. */ @AliasFor(annotation = RequestMapping.class) String name() default ""; /** * Alias for {@link RequestMapping#value}. */ @AliasFor(annotation = RequestMapping.class) String[] value() default {}; /** * Alias for {@link RequestMapping#path}. */ @AliasFor(annotation = RequestMapping.class) String[] path() default {}; /** * Alias for {@link RequestMapping#params}. */ @AliasFor(annotation = RequestMapping.class) String[] params() default {}; /** * Alias for {@link RequestMapping#headers}. */ @AliasFor(annotation = RequestMapping.class) String[] headers() default {}; /** * Alias for {@link RequestMapping#consumes}. */ @AliasFor(annotation = RequestMapping.class) String[] consumes() default {}; /** * Alias for {@link RequestMapping#produces}. */ @AliasFor(annotation = RequestMapping.class) String[] produces() default {}; } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/aspect/LimitAspect.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.aspect; import cn.hutool.core.util.ObjUtil; import com.google.common.collect.ImmutableList; import me.zhengjie.annotation.Limit; import me.zhengjie.exception.BadRequestException; import me.zhengjie.utils.RequestHolder; import me.zhengjie.utils.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.data.redis.core.script.RedisScript; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; /** * @author / */ @Aspect @Component public class LimitAspect { private final RedisTemplate redisTemplate; private static final Logger logger = LoggerFactory.getLogger(LimitAspect.class); public LimitAspect(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } @Pointcut("@annotation(me.zhengjie.annotation.Limit)") public void pointcut() { } @Around("pointcut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { HttpServletRequest request = RequestHolder.getHttpServletRequest(); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method signatureMethod = signature.getMethod(); Limit limit = signatureMethod.getAnnotation(Limit.class); LimitType limitType = limit.limitType(); String key = limit.key(); if (StringUtils.isEmpty(key)) { if (limitType == LimitType.IP) { key = StringUtils.getIp(request); } else { key = signatureMethod.getName(); } } ImmutableList keys = ImmutableList.of(StringUtils.join(limit.prefix(), "_", key, "_", request.getRequestURI().replace("/","_"))); String luaScript = buildLuaScript(); RedisScript redisScript = new DefaultRedisScript<>(luaScript, Long.class); Long count = redisTemplate.execute(redisScript, keys, limit.count(), limit.period()); if (ObjUtil.isNotNull(count) && count.intValue() <= limit.count()) { logger.info("第{}次访问key为 {},描述为 [{}] 的接口", count, keys, limit.name()); return joinPoint.proceed(); } else { throw new BadRequestException("访问次数受限制"); } } /** * 限流脚本 */ private String buildLuaScript() { return "local c" + "\nc = redis.call('get',KEYS[1])" + "\nif c and tonumber(c) > tonumber(ARGV[1]) then" + "\nreturn c;" + "\nend" + "\nc = redis.call('incr',KEYS[1])" + "\nif tonumber(c) == 1 then" + "\nredis.call('expire',KEYS[1],ARGV[2])" + "\nend" + "\nreturn c;"; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/aspect/LimitType.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.aspect; /** * 限流枚举 * @author / */ public enum LimitType { // 默认 CUSTOMER, // by ip addr IP } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/base/BaseDTO.java ================================================ package me.zhengjie.base; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import org.apache.commons.lang3.builder.ToStringBuilder; import java.io.Serializable; import java.lang.reflect.Field; import java.sql.Timestamp; /** * @author Zheng Jie * @date 2019年10月24日20:48:53 */ @Getter @Setter public class BaseDTO implements Serializable { @ApiModelProperty(value = "创建人") private String createBy; @ApiModelProperty(value = "修改人") private String updateBy; @ApiModelProperty(value = "创建时间: yyyy-MM-dd HH:mm:ss", hidden = true) @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") private Timestamp createTime; @ApiModelProperty(value = "更新时间: yyyy-MM-dd HH:mm:ss", hidden = true) @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") private Timestamp updateTime; @Override public String toString() { ToStringBuilder builder = new ToStringBuilder(this); Field[] fields = this.getClass().getDeclaredFields(); try { for (Field f : fields) { f.setAccessible(true); builder.append(f.getName(), f.get(this)).append("\n"); } } catch (Exception e) { builder.append("toString builder encounter an error"); } return builder.toString(); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/base/BaseEntity.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.base; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.LastModifiedBy; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; import javax.persistence.EntityListeners; import javax.persistence.MappedSuperclass; import java.io.Serializable; import java.lang.reflect.Field; import java.sql.Timestamp; /** * 通用字段, is_del 根据需求自行添加 * @author Zheng Jie * @date 2019年10月24日20:46:32 */ @Getter @Setter @MappedSuperclass @EntityListeners(AuditingEntityListener.class) public class BaseEntity implements Serializable { @CreatedBy @Column(name = "create_by", updatable = false) @ApiModelProperty(value = "创建人", hidden = true) private String createBy; @LastModifiedBy @Column(name = "update_by") @ApiModelProperty(value = "更新人", hidden = true) private String updateBy; @CreationTimestamp @Column(name = "create_time", updatable = false) @ApiModelProperty(value = "创建时间: yyyy-MM-dd HH:mm:ss", hidden = true) @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") private Timestamp createTime; @UpdateTimestamp @Column(name = "update_time") @ApiModelProperty(value = "更新时间: yyyy-MM-dd HH:mm:ss", hidden = true) @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") private Timestamp updateTime; /* 分组校验 */ public @interface Create {} /* 分组校验 */ public @interface Update {} @Override public String toString() { ToStringBuilder builder = new ToStringBuilder(this); Field[] fields = this.getClass().getDeclaredFields(); try { for (Field f : fields) { f.setAccessible(true); builder.append(f.getName(), f.get(this)).append("\n"); } } catch (Exception e) { builder.append("toString builder encounter an error"); } return builder.toString(); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/base/BaseMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.base; import java.util.List; /** * @author Zheng Jie * @date 2018-11-23 */ public interface BaseMapper { /** * DTO转Entity * @param dto / * @return / */ E toEntity(D dto); /** * Entity转DTO * @param entity / * @return / */ D toDto(E entity); /** * DTO集合转Entity集合 * @param dtoList / * @return / */ List toEntity(List dtoList); /** * Entity集合转DTO集合 * @param entityList / * @return / */ List toDto(List entityList); } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/config/AsyncExecutor.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; /** * 创建自定义的线程池 * @author Zheng Jie * @description * @date 2023-06-08 **/ @EnableAsync @Configuration public class AsyncExecutor implements AsyncConfigurer { public static int corePoolSize; public static int maxPoolSize; public static int keepAliveSeconds; public static int queueCapacity; @Value("${task.pool.core-pool-size}") public void setCorePoolSize(int corePoolSize) { AsyncExecutor.corePoolSize = corePoolSize; } @Value("${task.pool.max-pool-size}") public void setMaxPoolSize(int maxPoolSize) { AsyncExecutor.maxPoolSize = maxPoolSize; } @Value("${task.pool.keep-alive-seconds}") public void setKeepAliveSeconds(int keepAliveSeconds) { AsyncExecutor.keepAliveSeconds = keepAliveSeconds; } @Value("${task.pool.queue-capacity}") public void setQueueCapacity(int queueCapacity) { AsyncExecutor.queueCapacity = queueCapacity; } /** * 定义自定义异步处理线程池,线程名的标号自增计数器 */ private static AtomicInteger atomicInteger = new AtomicInteger(1); /** * 自定义线程池,用法 @Async * @return Executor */ @Override public Executor getAsyncExecutor() { // 自定义工厂 ThreadFactory factory = r -> new Thread(r, "el-async-" + atomicInteger.getAndIncrement()); // 自定义线程池 return new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveSeconds, TimeUnit.SECONDS, new ArrayBlockingQueue<>(queueCapacity), factory, new ThreadPoolExecutor.CallerRunsPolicy()); } /** * 自定义线程池,用法,注入到类中使用 * private ThreadPoolTaskExecutor taskExecutor; * @return ThreadPoolTaskExecutor */ @Bean("taskAsync") public ThreadPoolTaskExecutor taskAsync() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(2); executor.setMaxPoolSize(4); executor.setQueueCapacity(20); executor.setKeepAliveSeconds(60); executor.setThreadNamePrefix("el-task-"); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); return executor; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/config/AuditorConfig.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.config; import me.zhengjie.utils.SecurityUtils; import org.springframework.data.domain.AuditorAware; import org.springframework.stereotype.Component; import java.util.Optional; /** * @description : 设置审计 * @author : Dong ZhaoYang * @date : 2019/10/28 */ @Component("auditorAware") public class AuditorConfig implements AuditorAware { /** * 返回操作员标志信息 * * @return / */ @Override public Optional getCurrentAuditor() { try { // 这里应根据实际业务情况获取具体信息 return Optional.of(SecurityUtils.getCurrentUsername()); }catch (Exception ignored){} // 用户定时任务,或者无Token调用的情况 return Optional.of("System"); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/config/AuthorityConfig.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.config; import me.zhengjie.utils.SecurityUtils; import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Service; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * @author Zheng Jie */ @Service(value = "el") public class AuthorityConfig { /** * 判断接口是否有权限 * @param permissions 权限 * @return / */ public Boolean check(String ...permissions){ // 获取当前用户的所有权限 List elPermissions = SecurityUtils.getCurrentUser().getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()); // 判断当前用户的所有权限是否包含接口上定义的权限 return elPermissions.contains("admin") || Arrays.stream(permissions).anyMatch(elPermissions::contains); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/config/CustomP6SpyLogger.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.config; import cn.hutool.core.util.StrUtil; import com.p6spy.engine.spy.appender.MessageFormattingStrategy; import lombok.extern.slf4j.Slf4j; /** * @author Zheng Jie * @description 自定义 p6spy sql输出格式 * @date 2024-12-26 **/ @Slf4j public class CustomP6SpyLogger implements MessageFormattingStrategy { // 重置颜色 private static final String RESET = "\u001B[0m"; // 红色 private static final String RED = "\u001B[31m"; // 绿色 private static final String GREEN = "\u001B[32m"; /** * 格式化 sql * @param connectionId 连接id * @param now 当前时间 * @param elapsed 执行时长 * @param category sql分类 * @param prepared 预编译sql * @param sql 执行sql * @param url 数据库连接url * @return 格式化后的sql */ @Override public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql, String url) { // 去掉换行和多余空格 if(StrUtil.isNotBlank(sql)){ sql = sql.replaceAll("\\s+", " ").trim(); } // 格式化并加上颜色 return String.format( "%s[Time: %dms]%s - %s%s%s;", GREEN, elapsed, RESET, RED, sql, RESET ); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/config/RedisConfiguration.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.config; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONFactory; import com.alibaba.fastjson2.JSONWriter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.MurmurHash3; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.cache.Cache; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.CacheErrorHandler; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.SimpleCacheErrorHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.SerializationException; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.HashMap; import java.util.Map; /** * @author Zheng Jie * @date 2025-01-13 */ @Slf4j @Configuration @EnableCaching @AutoConfigureBefore(RedisAutoConfiguration.class) public class RedisConfiguration extends CachingConfigurerSupport { // 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全) private static final String[] WHITELIST_STR = {"me.zhengjie" }; /** * 设置 redis 数据默认过期时间,默认2小时 * 设置@cacheable 序列化方式 */ @Bean public RedisCacheConfiguration redisCacheConfiguration(){ FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig(); configuration = configuration.serializeValuesWith(RedisSerializationContext. SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofHours(2)); return configuration; } @Bean(name = "redisTemplate") public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate template = new RedisTemplate<>(); // 指定 key 和 value 的序列化方案 FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); // value值的序列化采用fastJsonRedisSerializer template.setValueSerializer(fastJsonRedisSerializer); template.setHashValueSerializer(fastJsonRedisSerializer); // 设置fastJson的序列化白名单 for (String pack : WHITELIST_STR) { JSONFactory.getDefaultObjectReaderProvider().addAutoTypeAccept(pack); } // key的序列化采用StringRedisSerializer template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setConnectionFactory(redisConnectionFactory); return template; } /** * 缓存管理器 * @param redisConnectionFactory / * @return 缓存管理器 */ @Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheConfiguration config = redisCacheConfiguration(); return RedisCacheManager.builder(redisConnectionFactory) .cacheDefaults(config) .build(); } /** * 自定义缓存key生成策略 */ @Bean public KeyGenerator keyGenerator() { return (target, method, params) -> { Map container = new HashMap<>(8); Class targetClassClass = target.getClass(); // 类地址 container.put("class",targetClassClass.toGenericString()); // 方法名称 container.put("methodName",method.getName()); // 包名称 container.put("package",targetClassClass.getPackage()); // 参数列表 for (int i = 0; i < params.length; i++) { container.put(String.valueOf(i),params[i]); } // 转为JSON字符串 String jsonString = JSON.toJSONString(container); // 使用 MurmurHash 生成 hash return Integer.toHexString(MurmurHash3.hash32x86(jsonString.getBytes())); }; } @Bean @SuppressWarnings({"unchecked","all"}) public CacheErrorHandler errorHandler() { return new SimpleCacheErrorHandler() { @Override public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) { // 处理缓存读取错误 log.error("Cache Get Error: {}",exception.getMessage()); } @Override public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) { // 处理缓存写入错误 log.error("Cache Put Error: {}",exception.getMessage()); } @Override public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) { // 处理缓存删除错误 log.error("Cache Evict Error: {}",exception.getMessage()); } @Override public void handleCacheClearError(RuntimeException exception, Cache cache) { // 处理缓存清除错误 log.error("Cache Clear Error: {}",exception.getMessage()); } }; } /** * Value 序列化 * * @param * @author / */ static class FastJsonRedisSerializer implements RedisSerializer { private final Class clazz; FastJsonRedisSerializer(Class clazz) { super(); this.clazz = clazz; } @Override public byte[] serialize(T t) throws SerializationException { if (t == null) { return new byte[0]; } return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(StandardCharsets.UTF_8); } @Override public T deserialize(byte[] bytes) throws SerializationException { if (bytes == null || bytes.length == 0) { return null; } String str = new String(bytes, StandardCharsets.UTF_8); return JSON.parseObject(str, clazz); } } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/config/RedissonConfiguration.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.config; import cn.hutool.core.util.StrUtil; import lombok.Data; import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Data @Configuration @AutoConfigureBefore(RedisAutoConfiguration.class) public class RedissonConfiguration { @Value("${spring.redis.host}") private String redisHost; @Value("${spring.redis.port}") private int redisPort; @Value("${spring.redis.database}") private int redisDatabase; @Value("${spring.redis.password:}") private String redisPassword; @Value("${spring.redis.timeout:5000}") private int timeout; @Value("${spring.redis.lettuce.pool.max-active:64}") private int connectionPoolSize; @Value("${spring.redis.lettuce.pool.min-idle:16}") private int connectionMinimumIdleSize; @Bean public RedissonClient redissonClient() { Config config = new Config(); config.useSingleServer() .setAddress("redis://" + redisHost + ":" + redisPort) .setDatabase(redisDatabase) .setTimeout(timeout) .setConnectionPoolSize(connectionPoolSize) .setConnectionMinimumIdleSize(connectionMinimumIdleSize); if(StrUtil.isNotBlank(redisPassword)){ config.useSingleServer().setPassword(redisPassword); } return Redisson.create(config); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/config/RemoveDruidAdConfig.java ================================================ package me.zhengjie.config; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; import com.alibaba.druid.util.Utils; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author Zheng Jie * @description * @date 2025-01-11 **/ @Configuration @SuppressWarnings({"unchecked","all"}) @ConditionalOnWebApplication @AutoConfigureAfter(DruidDataSourceAutoConfigure.class) @ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled", havingValue = "true", matchIfMissing = true) public class RemoveDruidAdConfig { /** * 方法名: removeDruidAdFilterRegistrationBean * 方法描述 除去页面底部的广告 * @param properties com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties * @return org.springframework.boot.web.servlet.FilterRegistrationBean */ @Bean public FilterRegistrationBean removeDruidAdFilterRegistrationBean(DruidStatProperties properties) { // 获取web监控页面的参数 DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); // 提取common.js的配置路径 String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); final String filePath = "support/http/resources/js/common.js"; //创建filter进行过滤 Filter filter = new Filter() { @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; if (httpRequest.getRequestURI().endsWith("js/common.js")) { // 获取common.js String text = Utils.readFromResource(filePath); // 正则替换banner, 除去底部的广告信息 text = text.replaceAll("
", ""); text = text.replaceAll("powered by.*?shrek.wang", ""); httpResponse.setContentType("application/javascript"); httpResponse.setCharacterEncoding("UTF-8"); httpResponse.getWriter().write(text); } else { chain.doFilter(request, response); } } @Override public void destroy() {} }; FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(filter); registrationBean.addUrlPatterns(commonJsPattern); return registrationBean; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/config/properties/FileProperties.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.config.properties; import lombok.Data; import me.zhengjie.utils.ElConstant; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; /** * @author Zheng Jie */ @Data @Configuration @ConfigurationProperties(prefix = "file") public class FileProperties { /** 文件大小限制 */ private Long maxSize; /** 头像大小限制 */ private Long avatarMaxSize; private ElPath mac; private ElPath linux; private ElPath windows; public ElPath getPath(){ String os = System.getProperty("os.name"); if(os.toLowerCase().startsWith(ElConstant.WIN)) { return windows; } else if(os.toLowerCase().startsWith(ElConstant.MAC)){ return mac; } return linux; } @Data public static class ElPath{ private String path; private String avatar; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/config/properties/RsaProperties.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.config.properties; import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * @author Zheng Jie * @website https://eladmin.vip * @description * @date 2020-05-18 **/ @Data @Component public class RsaProperties { public static String privateKey; @Value("${rsa.private_key}") public void setPrivateKey(String privateKey) { RsaProperties.privateKey = privateKey; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/config/webConfig/ConfigurerAdapter.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.config.webConfig; import com.alibaba.fastjson2.JSONWriter; import com.alibaba.fastjson2.support.config.FastJsonConfig; import com.alibaba.fastjson2.support.spring.http.converter.FastJsonHttpMessageConverter; import me.zhengjie.config.properties.FileProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; /** * WebMvcConfigurer * * @author Zheng Jie * @date 2018-11-30 */ @Configuration @EnableWebMvc public class ConfigurerAdapter implements WebMvcConfigurer { /** 文件配置 */ private final FileProperties properties; public ConfigurerAdapter(FileProperties properties) { this.properties = properties; } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOriginPattern("*"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { FileProperties.ElPath path = properties.getPath(); String avatarUtl = "file:" + path.getAvatar().replace("\\","/"); String pathUtl = "file:" + path.getPath().replace("\\","/"); registry.addResourceHandler("/avatar/**").addResourceLocations(avatarUtl).setCachePeriod(0); registry.addResourceHandler("/file/**").addResourceLocations(pathUtl).setCachePeriod(0); registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/").setCachePeriod(0); } @Override public void configureMessageConverters(List> converters) { // 配置 FastJsonHttpMessageConverter FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); List supportMediaTypeList = new ArrayList<>(); supportMediaTypeList.add(MediaType.APPLICATION_JSON); FastJsonConfig config = new FastJsonConfig(); config.setDateFormat("yyyy-MM-dd HH:mm:ss"); // 开启引用检测 config.setWriterFeatures(JSONWriter.Feature.ReferenceDetection); converter.setFastJsonConfig(config); converter.setSupportedMediaTypes(supportMediaTypeList); converter.setDefaultCharset(StandardCharsets.UTF_8); converters.add(converter); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/config/webConfig/MultipartConfig.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.config.webConfig; import org.springframework.boot.web.servlet.MultipartConfigFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.MultipartConfigElement; import java.io.File; /** * @date 2018-12-28 * @author ... */ @Configuration public class MultipartConfig { /** * 文件上传临时路径 */ @Bean MultipartConfigElement multipartConfigElement() { MultipartConfigFactory factory = new MultipartConfigFactory(); String location = System.getProperty("user.home") + "/.eladmin/file/tmp"; File tmpFile = new File(location); if (!tmpFile.exists()) { if (!tmpFile.mkdirs()) { System.out.println("create was not successful."); } } factory.setLocation(location); return factory.createMultipartConfig(); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/config/webConfig/QueryCustomizer.java ================================================ /* * Copyright 2019-2023 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.config.webConfig; import org.apache.catalina.connector.Connector; import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; import org.springframework.context.annotation.Configuration; /** * @author bearBoy80 */ @Configuration(proxyBeanMethods = false) public class QueryCustomizer implements TomcatConnectorCustomizer { @Override public void customize(Connector connector) { connector.setProperty("relaxedQueryChars", "[]{}"); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/config/webConfig/SwaggerConfig.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.config.webConfig; import lombok.RequiredArgsConstructor; import me.zhengjie.utils.AnonTagUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.ApiKey; import springfox.documentation.service.AuthorizationScope; import springfox.documentation.service.SecurityReference; import springfox.documentation.service.SecurityScheme; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * api页面 /doc.html * @author Zheng Jie * @date 2018-11-23 */ @Configuration @EnableSwagger2 @RequiredArgsConstructor public class SwaggerConfig { @Value("${jwt.header}") private String tokenHeader; @Value("${swagger.enabled}") private Boolean enabled; @Value("${server.servlet.context-path:}") private String apiPath; private final ApplicationContext applicationContext; @Bean @SuppressWarnings({"unchecked","all"}) public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .enable(enabled) .pathMapping("/") .apiInfo(apiInfo()) .select() .paths(PathSelectors.regex("^(?!/error).*")) .paths(PathSelectors.any()) .build() //添加登陆认证 .securitySchemes(securitySchemes()) .securityContexts(securityContexts()); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .description("一个简单且易上手的 Spring boot 后台管理框架") .title("ELADMIN 接口文档") .version("2.7") .build(); } private List securitySchemes() { //设置请求头信息 List securitySchemes = new ArrayList<>(); ApiKey apiKey = new ApiKey(tokenHeader, tokenHeader, "header"); securitySchemes.add(apiKey); return securitySchemes; } private List securityContexts() { //设置需要登录认证的路径 List securityContexts = new ArrayList<>(); securityContexts.add(getContextByPath()); return securityContexts; } private SecurityContext getContextByPath() { Set urls = AnonTagUtils.getAllAnonymousUrl(applicationContext); urls = urls.stream().filter(url -> !url.equals("/")).collect(Collectors.toSet()); String regExp = "^(?!" + apiPath + String.join("|" + apiPath, urls) + ").*$"; return SecurityContext.builder() .securityReferences(defaultAuth()) .operationSelector(o->o.requestMappingPattern() // 排除不需要认证的接口 .matches(regExp)) .build(); } private List defaultAuth() { List securityReferences = new ArrayList<>(); AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; authorizationScopes[0] = authorizationScope; securityReferences.add(new SecurityReference(tokenHeader, authorizationScopes)); return securityReferences; } /** * 解决Springfox与SpringBoot集成后,WebMvcRequestHandlerProvider和WebFluxRequestHandlerProvider冲突问题 * @return / */ @Bean @SuppressWarnings({"all"}) public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List filteredMappings = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(filteredMappings); } private List getHandlerMappings(Object bean) { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); if (field != null) { field.setAccessible(true); try { return (List) field.get(bean); } catch (IllegalAccessException e) { throw new IllegalStateException("Failed to access handlerMappings field", e); } } return Collections.emptyList(); } }; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/config/webConfig/SwaggerDataConfig.java ================================================ package me.zhengjie.config.webConfig; import cn.hutool.core.collection.CollUtil; import com.fasterxml.classmate.TypeResolver; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.data.domain.Pageable; import springfox.documentation.schema.AlternateTypeRule; import springfox.documentation.schema.AlternateTypeRuleConvention; import java.util.List; import static springfox.documentation.schema.AlternateTypeRules.newRule; /** * 将Pageable转换展示在swagger中 */ @Configuration public class SwaggerDataConfig { @Bean public AlternateTypeRuleConvention pageableConvention(final TypeResolver resolver) { return new AlternateTypeRuleConvention() { @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } @Override public List rules() { return CollUtil.newArrayList(newRule(resolver.resolve(Pageable.class), resolver.resolve(Page.class))); } }; } @ApiModel @Data private static class Page { @ApiModelProperty("页码 (0..N)") private Integer page; @ApiModelProperty("每页显示的数目") private Integer size; @ApiModelProperty("以下列格式排序标准:property[,asc | desc]。 默认排序顺序为升序。 支持多种排序条件:如:id,asc") private List sort; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/config/webConfig/WebSocketConfig.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.config.webConfig; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * @author ZhangHouYing * @date 2019-08-24 15:44 */ @Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/exception/BadRequestException.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.exception; import lombok.Getter; import org.springframework.http.HttpStatus; import static org.springframework.http.HttpStatus.BAD_REQUEST; /** * @author Zheng Jie * @date 2018-11-23 * 统一异常处理 */ @Getter public class BadRequestException extends RuntimeException{ private Integer status = BAD_REQUEST.value(); public BadRequestException(String msg){ super(msg); } public BadRequestException(HttpStatus status,String msg){ super(msg); this.status = status.value(); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/exception/EntityExistException.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.exception; import org.springframework.util.StringUtils; /** * @author Zheng Jie * @date 2018-11-23 */ public class EntityExistException extends RuntimeException { public EntityExistException(Class clazz, String field, String val) { super(EntityExistException.generateMessage(clazz.getSimpleName(), field, val)); } private static String generateMessage(String entity, String field, String val) { return StringUtils.capitalize(entity) + " with " + field + " "+ val + " existed"; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/exception/EntityNotFoundException.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.exception; import org.springframework.util.StringUtils; /** * @author Zheng Jie * @date 2018-11-23 */ public class EntityNotFoundException extends RuntimeException { public EntityNotFoundException(Class clazz, String field, String val) { super(EntityNotFoundException.generateMessage(clazz.getSimpleName(), field, val)); } private static String generateMessage(String entity, String field, String val) { return StringUtils.capitalize(entity) + " with " + field + " "+ val + " does not exist"; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/exception/handler/ApiError.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.exception.handler; import lombok.Data; /** * @author Zheng Jie * @date 2018-11-23 */ @Data public class ApiError { private Integer status = 400; private Long timestamp; private String message; private ApiError() { timestamp = System.currentTimeMillis(); } public static ApiError error(String message){ ApiError apiError = new ApiError(); apiError.setMessage(message); return apiError; } public static ApiError error(Integer status, String message){ ApiError apiError = new ApiError(); apiError.setStatus(status); apiError.setMessage(message); return apiError; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/exception/handler/GlobalExceptionHandler.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.exception.handler; import lombok.extern.slf4j.Slf4j; import me.zhengjie.exception.BadRequestException; import me.zhengjie.exception.EntityExistException; import me.zhengjie.exception.EntityNotFoundException; import me.zhengjie.utils.ThrowableUtil; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.validation.FieldError; import org.springframework.validation.ObjectError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import static org.springframework.http.HttpStatus.*; /** * @author Zheng Jie * @date 2018-11-23 */ @Slf4j @RestControllerAdvice public class GlobalExceptionHandler { /** * 处理所有不可知的异常 */ @ExceptionHandler(Throwable.class) public ResponseEntity handleException(Throwable e){ // 打印堆栈信息 log.error(ThrowableUtil.getStackTrace(e)); return buildResponseEntity(ApiError.error(e.getMessage())); } /** * BadCredentialsException */ @ExceptionHandler(BadCredentialsException.class) public ResponseEntity badCredentialsException(BadCredentialsException e){ // 打印堆栈信息 String message = "坏的凭证".equals(e.getMessage()) ? "用户名或密码不正确" : e.getMessage(); log.error(message); return buildResponseEntity(ApiError.error(message)); } /** * 处理自定义异常 */ @ExceptionHandler(value = BadRequestException.class) public ResponseEntity badRequestException(BadRequestException e) { // 打印堆栈信息 log.error(ThrowableUtil.getStackTrace(e)); return buildResponseEntity(ApiError.error(e.getStatus(),e.getMessage())); } /** * 处理 EntityExist */ @ExceptionHandler(value = EntityExistException.class) public ResponseEntity entityExistException(EntityExistException e) { // 打印堆栈信息 log.error(ThrowableUtil.getStackTrace(e)); return buildResponseEntity(ApiError.error(e.getMessage())); } /** * 处理 EntityNotFound */ @ExceptionHandler(value = EntityNotFoundException.class) public ResponseEntity entityNotFoundException(EntityNotFoundException e) { // 打印堆栈信息 log.error(ThrowableUtil.getStackTrace(e)); return buildResponseEntity(ApiError.error(NOT_FOUND.value(),e.getMessage())); } /** * 处理所有接口数据验证异常 */ @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e){ // 打印堆栈信息 log.error(ThrowableUtil.getStackTrace(e)); ObjectError objectError = e.getBindingResult().getAllErrors().get(0); String message = objectError.getDefaultMessage(); if (objectError instanceof FieldError) { message = ((FieldError) objectError).getField() + ": " + message; } return buildResponseEntity(ApiError.error(message)); } /** * 统一返回 */ private ResponseEntity buildResponseEntity(ApiError apiError) { return new ResponseEntity<>(apiError, HttpStatus.valueOf(apiError.getStatus())); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/AnonTagUtils.java ================================================ /* * Copyright 2019-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import me.zhengjie.annotation.rest.AnonymousAccess; import me.zhengjie.utils.enums.RequestMethodEnum; import org.springframework.context.ApplicationContext; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import java.util.*; /** * @author Zheng Jie * @description 匿名标记工具 * @date 2025-01-13 **/ public class AnonTagUtils { /** * 获取匿名标记的URL * @param applicationContext / * @return / */ public static Map> getAnonymousUrl(ApplicationContext applicationContext){ RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping"); Map handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods(); Map> anonymousUrls = new HashMap<>(8); // 获取匿名标记 Set get = new HashSet<>(); Set post = new HashSet<>(); Set put = new HashSet<>(); Set patch = new HashSet<>(); Set delete = new HashSet<>(); Set all = new HashSet<>(); for (Map.Entry infoEntry : handlerMethodMap.entrySet()) { HandlerMethod handlerMethod = infoEntry.getValue(); AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class); if (null != anonymousAccess) { List requestMethods = new ArrayList<>(infoEntry.getKey().getMethodsCondition().getMethods()); RequestMethodEnum request = RequestMethodEnum.find(requestMethods.isEmpty() ? RequestMethodEnum.ALL.getType() : requestMethods.get(0).name()); if (infoEntry.getKey().getPatternsCondition()!=null) { switch (Objects.requireNonNull(request)) { case GET: get.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); break; case POST: post.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); break; case PUT: put.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); break; case PATCH: patch.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); break; case DELETE: delete.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); break; default: all.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); break; } } } } anonymousUrls.put(RequestMethodEnum.GET.getType(), get); anonymousUrls.put(RequestMethodEnum.POST.getType(), post); anonymousUrls.put(RequestMethodEnum.PUT.getType(), put); anonymousUrls.put(RequestMethodEnum.PATCH.getType(), patch); anonymousUrls.put(RequestMethodEnum.DELETE.getType(), delete); anonymousUrls.put(RequestMethodEnum.ALL.getType(), all); return anonymousUrls; } /** * 获取所有匿名标记的URL * @param applicationContext / * @return / */ public static Set getAllAnonymousUrl(ApplicationContext applicationContext){ Set allUrl = new HashSet<>(); Map> anonymousUrls = getAnonymousUrl(applicationContext); for (String key : anonymousUrls.keySet()) { allUrl.addAll(anonymousUrls.get(key)); } return allUrl; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/BigDecimalUtils.java ================================================ /* * Copyright 2019-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import java.math.BigDecimal; import java.math.RoundingMode; /** * @author Zheng Jie * @description 计算类 * @date 2024-12-27 **/ public class BigDecimalUtils { /** * 将对象转换为 BigDecimal * @param obj 输入对象 * @return 转换后的 BigDecimal */ private static BigDecimal toBigDecimal(Object obj) { if (obj instanceof BigDecimal) { return (BigDecimal) obj; } else if (obj instanceof Long) { return BigDecimal.valueOf((Long) obj); } else if (obj instanceof Integer) { return BigDecimal.valueOf((Integer) obj); } else if (obj instanceof Double) { return new BigDecimal(String.valueOf(obj)); } else { throw new IllegalArgumentException("Unsupported type"); } } /** * 加法 * @param a 加数 * @param b 加数 * @return 两个加数的和,保留两位小数 */ public static BigDecimal add(Object a, Object b) { BigDecimal bdA = toBigDecimal(a); BigDecimal bdB = toBigDecimal(b); return bdA.add(bdB).setScale(2, RoundingMode.HALF_UP); } /** * 减法 * @param a 被减数 * @param b 减数 * @return 两数的差,保留两位小数 */ public static BigDecimal subtract(Object a, Object b) { BigDecimal bdA = toBigDecimal(a); BigDecimal bdB = toBigDecimal(b); return bdA.subtract(bdB).setScale(2, RoundingMode.HALF_UP); } /** * 乘法 * @param a 乘数 * @param b 乘数 * @return 两个乘数的积,保留两位小数 */ public static BigDecimal multiply(Object a, Object b) { BigDecimal bdA = toBigDecimal(a); BigDecimal bdB = toBigDecimal(b); return bdA.multiply(bdB).setScale(2, RoundingMode.HALF_UP); } /** * 除法 * @param a 被除数 * @param b 除数 * @return 两数的商,保留两位小数 */ public static BigDecimal divide(Object a, Object b) { BigDecimal bdA = toBigDecimal(a); BigDecimal bdB = toBigDecimal(b); return bdA.divide(bdB, 2, RoundingMode.HALF_UP); } /** * 除法 * @param a 被除数 * @param b 除数 * @param scale 保留小数位数 * @return 两数的商,保留两位小数 */ public static BigDecimal divide(Object a, Object b, int scale) { BigDecimal bdA = toBigDecimal(a); BigDecimal bdB = toBigDecimal(b); return bdA.divide(bdB, scale, RoundingMode.HALF_UP); } /** * 分转元 * @param obj 分的金额 * @return 转换后的元,保留两位小数 */ public static BigDecimal centsToYuan(Object obj) { BigDecimal cents = toBigDecimal(obj); return cents.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); } /** * 元转分 * @param obj 元的金额 * @return 转换后的分 */ public static Long yuanToCents(Object obj) { BigDecimal yuan = toBigDecimal(obj); return yuan.multiply(BigDecimal.valueOf(100)).setScale(0, RoundingMode.HALF_UP).longValue(); } public static void main(String[] args) { BigDecimal num1 = new BigDecimal("10.123"); BigDecimal num2 = new BigDecimal("2.456"); System.out.println("加法结果: " + add(num1, num2)); System.out.println("减法结果: " + subtract(num1, num2)); System.out.println("乘法结果: " + multiply(num1, num2)); System.out.println("除法结果: " + divide(num1, num2)); Long cents = 12345L; System.out.println("分转元结果: " + centsToYuan(cents)); BigDecimal yuan = new BigDecimal("123.45"); System.out.println("元转分结果: " + yuanToCents(yuan)); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/CacheKey.java ================================================ /* * Copyright 2019-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; /** * @author liaojinlong * @date 2020/6/11 15:49 * @description 关于缓存的Key集合 */ public interface CacheKey { /** * 用户 */ String USER_ID = "user::id:"; /** * 数据 */ String DATA_USER = "data::user:"; /** * 菜单 */ String MENU_ID = "menu::id:"; String MENU_USER = "menu::user:"; /** * 角色授权 */ String ROLE_AUTH = "role::auth:"; String ROLE_USER = "role::user:"; /** * 角色信息 */ String ROLE_ID = "role::id:"; /** * 部门 */ String DEPT_ID = "dept::id:"; /** * 岗位 */ String JOB_ID = "job::id:"; /** * 数据字典 */ String DICT_NAME = "dict::name:"; } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/CloseUtil.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import java.io.Closeable; /** * @author Zheng Jie * @website https://eladmin.vip * @description 用于关闭各种连接,缺啥补啥 * @date 2021-03-05 **/ public class CloseUtil { public static void close(Closeable closeable) { if (null != closeable) { try { closeable.close(); } catch (Exception e) { // 静默关闭 } } } public static void close(AutoCloseable closeable) { if (null != closeable) { try { closeable.close(); } catch (Exception e) { // 静默关闭 } } } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/DateUtil.java ================================================ /* * Copyright 2019-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import java.time.*; import java.time.format.DateTimeFormatter; import java.util.Date; /** * @author: liaojinlong * @date: 2020/6/11 16:28 * @apiNote: JDK 8 新日期类 格式化与字符串转换 工具类 */ public class DateUtil { public static final DateTimeFormatter DFY_MD_HMS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); public static final DateTimeFormatter DFY_MD = DateTimeFormatter.ofPattern("yyyy-MM-dd"); /** * LocalDateTime 转时间戳 * * @param localDateTime / * @return / */ public static Long getTimeStamp(LocalDateTime localDateTime) { return localDateTime.atZone(ZoneId.systemDefault()).toEpochSecond(); } /** * 时间戳转LocalDateTime * * @param timeStamp / * @return / */ public static LocalDateTime fromTimeStamp(Long timeStamp) { return LocalDateTime.ofEpochSecond(timeStamp, 0, OffsetDateTime.now().getOffset()); } /** * LocalDateTime 转 Date * Jdk8 后 不推荐使用 {@link Date} Date * * @param localDateTime / * @return / */ public static Date toDate(LocalDateTime localDateTime) { return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); } /** * LocalDate 转 Date * Jdk8 后 不推荐使用 {@link Date} Date * * @param localDate / * @return / */ public static Date toDate(LocalDate localDate) { return toDate(localDate.atTime(LocalTime.now(ZoneId.systemDefault()))); } /** * Date转 LocalDateTime * Jdk8 后 不推荐使用 {@link Date} Date * * @param date / * @return / */ public static LocalDateTime toLocalDateTime(Date date) { return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); } /** * 日期 格式化 * * @param localDateTime / * @param patten / * @return / */ public static String localDateTimeFormat(LocalDateTime localDateTime, String patten) { DateTimeFormatter df = DateTimeFormatter.ofPattern(patten); return df.format(localDateTime); } /** * 日期 格式化 * * @param localDateTime / * @param df / * @return / */ public static String localDateTimeFormat(LocalDateTime localDateTime, DateTimeFormatter df) { return df.format(localDateTime); } /** * 日期格式化 yyyy-MM-dd HH:mm:ss * * @param localDateTime / * @return / */ public static String localDateTimeFormatyMdHms(LocalDateTime localDateTime) { return DFY_MD_HMS.format(localDateTime); } /** * 日期格式化 yyyy-MM-dd * * @param localDateTime / * @return / */ public String localDateTimeFormatyMd(LocalDateTime localDateTime) { return DFY_MD.format(localDateTime); } /** * 字符串转 LocalDateTime ,字符串格式 yyyy-MM-dd * * @param localDateTime / * @return / */ public static LocalDateTime parseLocalDateTimeFormat(String localDateTime, String pattern) { DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern); return LocalDateTime.from(dateTimeFormatter.parse(localDateTime)); } /** * 字符串转 LocalDateTime ,字符串格式 yyyy-MM-dd * * @param localDateTime / * @return / */ public static LocalDateTime parseLocalDateTimeFormat(String localDateTime, DateTimeFormatter dateTimeFormatter) { return LocalDateTime.from(dateTimeFormatter.parse(localDateTime)); } /** * 字符串转 LocalDateTime ,字符串格式 yyyy-MM-dd HH:mm:ss * * @param localDateTime / * @return / */ public static LocalDateTime parseLocalDateTimeFormatyMdHms(String localDateTime) { return LocalDateTime.from(DFY_MD_HMS.parse(localDateTime)); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/ElConstant.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; /** * 常用静态常量 * * @author Zheng Jie * @date 2018-12-26 */ public class ElConstant { /** * win 系统 */ public static final String WIN = "win"; /** * mac 系统 */ public static final String MAC = "mac"; } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/EncryptUtils.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import javax.crypto.spec.IvParameterSpec; import java.nio.charset.StandardCharsets; /** * 加密 * @author Zheng Jie * @date 2018-11-23 */ public class EncryptUtils { private static final String STR_PARAM = "Passw0rd"; private static final IvParameterSpec IV = new IvParameterSpec(STR_PARAM.getBytes(StandardCharsets.UTF_8)); private static DESKeySpec getDesKeySpec(String source) throws Exception { if (source == null || source.isEmpty()) { return null; } String strKey = "Passw0rd"; return new DESKeySpec(strKey.getBytes(StandardCharsets.UTF_8)); } /** * 对称加密 */ public static String desEncrypt(String source) throws Exception { Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); DESKeySpec desKeySpec = getDesKeySpec(source); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); SecretKey secretKey = keyFactory.generateSecret(desKeySpec); cipher.init(Cipher.ENCRYPT_MODE, secretKey, IV); return byte2hex(cipher.doFinal(source.getBytes(StandardCharsets.UTF_8))).toUpperCase(); } /** * 对称解密 */ public static String desDecrypt(String source) throws Exception { Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); byte[] src = hex2byte(source.getBytes(StandardCharsets.UTF_8)); DESKeySpec desKeySpec = getDesKeySpec(source); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); SecretKey secretKey = keyFactory.generateSecret(desKeySpec); cipher.init(Cipher.DECRYPT_MODE, secretKey, IV); byte[] retByte = cipher.doFinal(src); return new String(retByte); } private static String byte2hex(byte[] inStr) { String stmp; StringBuilder out = new StringBuilder(inStr.length * 2); for (byte b : inStr) { stmp = Integer.toHexString(b & 0xFF); if (stmp.length() == 1) { out.append("0").append(stmp); } else { out.append(stmp); } } return out.toString(); } private static byte[] hex2byte(byte[] b) { int size = 2; if ((b.length % size) != 0) { throw new IllegalArgumentException("长度不是偶数"); } byte[] b2 = new byte[b.length / 2]; for (int n = 0; n < b.length; n += size) { String item = new String(b, n, 2); b2[n / 2] = (byte) Integer.parseInt(item, 16); } return b2; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/FileUtil.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.poi.excel.BigExcelWriter; import cn.hutool.poi.excel.ExcelUtil; import lombok.extern.slf4j.Slf4j; import me.zhengjie.exception.BadRequestException; import org.apache.poi.util.IOUtils; import org.apache.poi.xssf.streaming.SXSSFSheet; import org.springframework.web.multipart.MultipartFile; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.nio.file.Files; import java.security.MessageDigest; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.*; import java.util.stream.Collectors; /** * File工具类,扩展 hutool 工具包 * * @author Zheng Jie * @date 2018-12-27 */ @Slf4j public class FileUtil extends cn.hutool.core.io.FileUtil { /** * 系统临时目录 *
* windows 包含路径分割符,但Linux 不包含, * 在windows \\==\ 前提下, * 为安全起见 同意拼装 路径分割符, *
     *       java.io.tmpdir
     *       windows : C:\Users/xxx\AppData\Local\Temp\
     *       linux: /temp
     * 
*/ public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator; /** * 定义GB的计算常量 */ private static final int GB = 1024 * 1024 * 1024; /** * 定义MB的计算常量 */ private static final int MB = 1024 * 1024; /** * 定义KB的计算常量 */ private static final int KB = 1024; /** * 格式化小数 */ private static final DecimalFormat DF = new DecimalFormat("0.00"); public static final String IMAGE = "图片"; public static final String TXT = "文档"; public static final String MUSIC = "音乐"; public static final String VIDEO = "视频"; public static final String OTHER = "其他"; /** * MultipartFile转File */ public static File toFile(MultipartFile multipartFile) { // 获取文件名 String fileName = multipartFile.getOriginalFilename(); // 获取文件后缀 String prefix = "." + getExtensionName(fileName); File file = null; try { // 用uuid作为文件名,防止生成的临时文件重复 file = new File(SYS_TEM_DIR + IdUtil.simpleUUID() + prefix); // MultipartFile to File multipartFile.transferTo(file); } catch (IOException e) { log.error(e.getMessage(), e); } return file; } /** * 获取文件扩展名,不带 . */ public static String getExtensionName(String filename) { if ((filename != null) && (!filename.isEmpty())) { int dot = filename.lastIndexOf('.'); if ((dot > -1) && (dot < (filename.length() - 1))) { return filename.substring(dot + 1); } } return filename; } /** * Java文件操作 获取不带扩展名的文件名 */ public static String getFileNameNoEx(String filename) { if ((filename != null) && (!filename.isEmpty())) { int dot = filename.lastIndexOf('.'); if (dot > -1) { return filename.substring(0, dot); } } return filename; } /** * 文件大小转换 */ public static String getSize(long size) { String resultSize; if (size / GB >= 1) { //如果当前Byte的值大于等于1GB resultSize = DF.format(size / (float) GB) + "GB"; } else if (size / MB >= 1) { //如果当前Byte的值大于等于1MB resultSize = DF.format(size / (float) MB) + "MB"; } else if (size / KB >= 1) { //如果当前Byte的值大于等于1KB resultSize = DF.format(size / (float) KB) + "KB"; } else { resultSize = size + "B"; } return resultSize; } /** * inputStream 转 File */ static File inputStreamToFile(InputStream ins, String name){ File file = new File(SYS_TEM_DIR + name); if (file.exists()) { return file; } OutputStream os = null; try { os = Files.newOutputStream(file.toPath()); int bytesRead; int len = 8192; byte[] buffer = new byte[len]; while ((bytesRead = ins.read(buffer, 0, len)) != -1) { os.write(buffer, 0, bytesRead); } } catch (Exception e) { log.error(e.getMessage(), e); } finally { CloseUtil.close(os); CloseUtil.close(ins); } return file; } /** * 将文件名解析成文件的上传路径 */ public static File upload(MultipartFile file, String filePath) { Date date = new Date(); SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssS"); // 过滤非法文件名 String name = getFileNameNoEx(verifyFilename(file.getOriginalFilename())); String suffix = getExtensionName(file.getOriginalFilename()); String nowStr = "-" + format.format(date); try { String fileName = name + nowStr + "." + suffix; String path = filePath + fileName; // getCanonicalFile 可解析正确各种路径 File dest = new File(path).getCanonicalFile(); // 检测是否存在目录 if (!dest.getParentFile().exists()) { if (!dest.getParentFile().mkdirs()) { System.out.println("was not successful."); } } // 文件写入 file.transferTo(dest); return dest; } catch (Exception e) { log.error(e.getMessage(), e); } return null; } /** * 防止 CSV/XLSX 注入(CWE-1236) *

* 当单元格值以 {@code = + - @ \t \r} 开头时,电子表格软件可能将其解释为公式。 * 本方法在这类值前添加单引号前缀,并转义值中已有的单引号,防止公式注入攻击。 *

* * @param value 原始单元格值 * @return 经过安全处理的单元格值 * @see OWASP CSV Injection */ public static String sanitizeCellValue(String value) { if (value == null || value.isEmpty()) { return value; } char first = value.charAt(0); if (first == '=' || first == '+' || first == '-' || first == '@' || first == '\t' || first == '\r') { // 转义值中已有的单引号,防止攻击者利用单引号逃逸 return "'" + value.replace("'", "''"); } return value; } /** * 导出excel */ public static void downloadExcel(List> list, HttpServletResponse response) throws IOException { String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx"; File file = new File(tempPath); BigExcelWriter writer = ExcelUtil.getBigWriter(file); // 处理数据以防止CSV/XLSX注入(CWE-1236) List> sanitizedList = list.stream().map(map -> { Map sanitizedMap = new LinkedHashMap<>(); map.forEach((key, value) -> { if (value instanceof String) { sanitizedMap.put(key, sanitizeCellValue((String) value)); } else { sanitizedMap.put(key, value); } }); return sanitizedMap; }).collect(Collectors.toList()); // 一次性写出内容,使用默认样式,强制输出标题 writer.write(sanitizedList, true); SXSSFSheet sheet = (SXSSFSheet)writer.getSheet(); //上面需要强转SXSSFSheet 不然没有trackAllColumnsForAutoSizing方法 sheet.trackAllColumnsForAutoSizing(); //列宽自适应 writer.autoSizeColumnAll(); //response为HttpServletResponse对象 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"); //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码 response.setHeader("Content-Disposition", "attachment;filename=file.xlsx"); ServletOutputStream out = response.getOutputStream(); // 终止后删除临时文件 file.deleteOnExit(); writer.flush(out, true); //此处记得关闭输出Servlet流 IoUtil.close(out); } public static String getFileType(String type) { String documents = "txt doc pdf ppt pps xlsx xls docx"; String music = "mp3 wav wma mpa ram ra aac aif m4a"; String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg"; String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg"; if (image.contains(type)) { return IMAGE; } else if (documents.contains(type)) { return TXT; } else if (music.contains(type)) { return MUSIC; } else if (video.contains(type)) { return VIDEO; } else { return OTHER; } } public static void checkSize(long maxSize, long size) { // 1M int len = 1024 * 1024; if (size > (maxSize * len)) { throw new BadRequestException("文件超出规定大小:" + maxSize + "MB"); } } /** * 判断两个文件是否相同 */ public static boolean check(File file1, File file2) { String img1Md5 = getMd5(file1); String img2Md5 = getMd5(file2); if(img1Md5 != null){ return img1Md5.equals(img2Md5); } return false; } /** * 判断两个文件是否相同 */ public static boolean check(String file1Md5, String file2Md5) { return file1Md5.equals(file2Md5); } private static byte[] getByte(File file) { // 得到文件长度 byte[] b = new byte[(int) file.length()]; InputStream in = null; try { in = Files.newInputStream(file.toPath()); try { System.out.println(in.read(b)); } catch (IOException e) { log.error(e.getMessage(), e); } } catch (Exception e) { log.error(e.getMessage(), e); return null; } finally { CloseUtil.close(in); } return b; } private static String getMd5(byte[] bytes) { // 16进制字符 char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; try { MessageDigest mdTemp = MessageDigest.getInstance("MD5"); mdTemp.update(bytes); byte[] md = mdTemp.digest(); int j = md.length; char[] str = new char[j * 2]; int k = 0; // 移位 输出字符串 for (byte byte0 : md) { str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { log.error(e.getMessage(), e); } return null; } /** * 下载文件 * * @param request / * @param response / * @param file / */ public static void downloadFile(HttpServletRequest request, HttpServletResponse response, File file, boolean deleteOnExit) { response.setCharacterEncoding(request.getCharacterEncoding()); response.setContentType("application/octet-stream"); FileInputStream fis = null; try { fis = new FileInputStream(file); response.setHeader("Content-Disposition", "attachment; filename=" + file.getName()); IOUtils.copy(fis, response.getOutputStream()); response.flushBuffer(); } catch (Exception e) { log.error(e.getMessage(), e); } finally { if (fis != null) { try { fis.close(); if (deleteOnExit) { file.deleteOnExit(); } } catch (IOException e) { log.error(e.getMessage(), e); } } } } /** * 验证并过滤非法的文件名 * @param fileName 文件名 * @return 文件名 */ public static String verifyFilename(String fileName) { // 过滤掉特殊字符 fileName = fileName.replaceAll("[\\\\/:*?\"<>|~\\s]", ""); // 去掉文件名开头和结尾的空格和点 fileName = fileName.trim().replaceAll("^[. ]+|[. ]+$", ""); // 不允许文件名超过255(在Mac和Linux中)或260(在Windows中)个字符 int maxFileNameLength = 255; if (System.getProperty("os.name").startsWith("Windows")) { maxFileNameLength = 260; } if (fileName.length() > maxFileNameLength) { fileName = fileName.substring(0, maxFileNameLength); } // 过滤掉控制字符 fileName = fileName.replaceAll("[\\p{Cntrl}]", ""); // 过滤掉 ".." 路径 fileName = fileName.replaceAll("\\.{2,}", ""); // 去掉文件名开头的 ".." fileName = fileName.replaceAll("^\\.+/", ""); // 保留文件名中最后一个 "." 字符,过滤掉其他 "." fileName = fileName.replaceAll("^(.*)(\\.[^.]*)$", "$1").replaceAll("\\.", "") + fileName.replaceAll("^(.*)(\\.[^.]*)$", "$2"); return fileName; } public static String getMd5(File file) { return getMd5(getByte(file)); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/PageResult.java ================================================ package me.zhengjie.utils; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; import java.util.List; /** * 分页结果封装类 * @author Zheng Jie * @date 2018-11-23 * @param */ @Data @NoArgsConstructor @AllArgsConstructor public class PageResult implements Serializable { private List content; private long totalElements; } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/PageUtil.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import org.springframework.data.domain.Page; import java.util.*; /** * 分页工具 * @author Zheng Jie * @date 2018-12-10 */ public class PageUtil extends cn.hutool.core.util.PageUtil { /** * List 分页 */ public static List paging(int page, int size , List list) { int fromIndex = page * size; int toIndex = page * size + size; if(fromIndex > list.size()){ return Collections.emptyList(); } else if(toIndex >= list.size()) { return list.subList(fromIndex,list.size()); } else { return list.subList(fromIndex,toIndex); } } /** * Page 数据处理,预防redis反序列化报错 */ public static PageResult toPage(Page page) { return new PageResult<>(page.getContent(), page.getTotalElements()); } /** * 自定义分页 */ public static PageResult toPage(List list, long totalElements) { return new PageResult<>(list, totalElements); } /** * 返回空数据 */ public static PageResult noData () { return new PageResult<>(null, 0); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/QueryHelp.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import lombok.extern.slf4j.Slf4j; import me.zhengjie.annotation.DataPermission; import me.zhengjie.annotation.Query; import javax.persistence.criteria.*; import java.lang.reflect.Field; import java.util.*; /** * @author Zheng Jie * @date 2019-6-4 14:59:48 */ @Slf4j @SuppressWarnings({"unchecked","all"}) public class QueryHelp { public static Predicate getPredicate(Root root, Q query, CriteriaBuilder cb) { List list = new ArrayList<>(); if(query == null){ return cb.and(list.toArray(new Predicate[0])); } // 数据权限验证 DataPermission permission = query.getClass().getAnnotation(DataPermission.class); if(permission != null){ // 获取数据权限 List dataScopes = SecurityUtils.getCurrentUserDataScope(); if(CollectionUtil.isNotEmpty(dataScopes)){ if(StringUtils.isNotBlank(permission.joinName()) && StringUtils.isNotBlank(permission.fieldName())) { Join join = root.join(permission.joinName(), JoinType.LEFT); list.add(getExpression(permission.fieldName(),join, root).in(dataScopes)); } else if (StringUtils.isBlank(permission.joinName()) && StringUtils.isNotBlank(permission.fieldName())) { list.add(getExpression(permission.fieldName(),null, root).in(dataScopes)); } } } try { Map joinKey = new HashMap<>(); List fields = getAllFields(query.getClass(), new ArrayList<>()); for (Field field : fields) { boolean accessible = field.isAccessible(); // 设置对象的访问权限,保证对private的属性的访 field.setAccessible(true); Query q = field.getAnnotation(Query.class); if (q != null) { String propName = q.propName(); String joinName = q.joinName(); String blurry = q.blurry(); String attributeName = isBlank(propName) ? field.getName() : propName; Class fieldType = field.getType(); Object val = field.get(query); if (ObjectUtil.isNull(val) || "".equals(val)) { continue; } Join join = null; // 模糊多字段 if (ObjectUtil.isNotEmpty(blurry)) { String[] blurrys = blurry.split(","); List orPredicate = new ArrayList<>(); for (String s : blurrys) { orPredicate.add(cb.like(root.get(s).as(String.class), "%" + val.toString() + "%")); } Predicate[] p = new Predicate[orPredicate.size()]; list.add(cb.or(orPredicate.toArray(p))); continue; } if (ObjectUtil.isNotEmpty(joinName)) { join = joinKey.get(joinName); if(join == null){ String[] joinNames = joinName.split(">"); for (String name : joinNames) { switch (q.join()) { case LEFT: if(ObjectUtil.isNotNull(join) && ObjectUtil.isNotNull(val)){ join = join.join(name, JoinType.LEFT); } else { join = root.join(name, JoinType.LEFT); } break; case RIGHT: if(ObjectUtil.isNotNull(join) && ObjectUtil.isNotNull(val)){ join = join.join(name, JoinType.RIGHT); } else { join = root.join(name, JoinType.RIGHT); } break; case INNER: if(ObjectUtil.isNotNull(join) && ObjectUtil.isNotNull(val)){ join = join.join(name, JoinType.INNER); } else { join = root.join(name, JoinType.INNER); } break; default: break; } } joinKey.put(joinName, join); } } switch (q.type()) { case EQUAL: list.add(cb.equal(getExpression(attributeName,join,root) .as((Class) fieldType),val)); break; case GREATER_THAN: list.add(cb.greaterThanOrEqualTo(getExpression(attributeName,join,root) .as((Class) fieldType), (Comparable) val)); break; case LESS_THAN: list.add(cb.lessThanOrEqualTo(getExpression(attributeName,join,root) .as((Class) fieldType), (Comparable) val)); break; case LESS_THAN_NQ: list.add(cb.lessThan(getExpression(attributeName,join,root) .as((Class) fieldType), (Comparable) val)); break; case INNER_LIKE: list.add(cb.like(getExpression(attributeName,join,root) .as(String.class), "%" + val.toString() + "%")); break; case LEFT_LIKE: list.add(cb.like(getExpression(attributeName,join,root) .as(String.class), "%" + val.toString())); break; case RIGHT_LIKE: list.add(cb.like(getExpression(attributeName,join,root) .as(String.class), val.toString() + "%")); break; case IN: if (CollUtil.isNotEmpty((Collection)val)) { list.add(getExpression(attributeName,join,root).in((Collection) val)); } break; case NOT_IN: if (CollUtil.isNotEmpty((Collection)val)) { list.add(getExpression(attributeName,join,root).in((Collection) val).not()); } break; case NOT_EQUAL: list.add(cb.notEqual(getExpression(attributeName,join,root), val)); break; case NOT_NULL: list.add(cb.isNotNull(getExpression(attributeName,join,root))); break; case IS_NULL: list.add(cb.isNull(getExpression(attributeName,join,root))); break; case BETWEEN: List between = new ArrayList<>((List)val); if(between.size() == 2){ list.add(cb.between(getExpression(attributeName, join, root).as((Class) between.get(0).getClass()), (Comparable) between.get(0), (Comparable) between.get(1))); } break; case FIND_IN_SET: list.add(cb.greaterThan(cb.function("FIND_IN_SET", Integer.class, cb.literal(val.toString()), root.get(attributeName)), 0)); break; default: break; } } field.setAccessible(accessible); } } catch (Exception e) { log.error(e.getMessage(), e); } int size = list.size(); return cb.and(list.toArray(new Predicate[size])); } @SuppressWarnings("unchecked") private static Expression getExpression(String attributeName, Join join, Root root) { if (ObjectUtil.isNotEmpty(join)) { return join.get(attributeName); } else { return root.get(attributeName); } } private static boolean isBlank(final CharSequence cs) { int strLen; if (cs == null || (strLen = cs.length()) == 0) { return true; } for (int i = 0; i < strLen; i++) { if (!Character.isWhitespace(cs.charAt(i))) { return false; } } return true; } public static List getAllFields(Class clazz, List fields) { if (clazz != null) { fields.addAll(Arrays.asList(clazz.getDeclaredFields())); getAllFields(clazz.getSuperclass(), fields); } return fields; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/RedisUtils.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson2.JSON; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.*; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.stereotype.Component; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * @author / */ @Component @SuppressWarnings({"all"}) public class RedisUtils { private static final Logger log = LoggerFactory.getLogger(RedisUtils.class); private RedisTemplate redisTemplate; public RedisUtils(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; this.redisTemplate.setKeySerializer(new StringRedisSerializer()); this.redisTemplate.setHashKeySerializer(new StringRedisSerializer()); } /** * 指定缓存失效时间 * * @param key 键 * @param time 时间(秒) 注意:这里将会替换原有的时间 */ public boolean expire(String key, long time) { try { if (time > 0) { redisTemplate.expire(key, time, TimeUnit.SECONDS); } } catch (Exception e) { log.error(e.getMessage(), e); return false; } return true; } /** * 指定缓存失效时间 * * @param key 键 * @param time 时间(秒) 注意:这里将会替换原有的时间 * @param timeUnit 单位 */ public boolean expire(String key, long time, TimeUnit timeUnit) { try { if (time > 0) { redisTemplate.expire(key, time, timeUnit); } } catch (Exception e) { log.error(e.getMessage(), e); return false; } return true; } /** * 根据 key 获取过期时间 * * @param key 键 不能为null * @return 时间(秒) 返回0代表为永久有效 */ public long getExpire(Object key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } /** * 查找匹配key * * @param pattern key * @return / */ public List scan(String pattern) { ScanOptions options = ScanOptions.scanOptions().match(pattern).build(); RedisConnectionFactory factory = redisTemplate.getConnectionFactory(); RedisConnection rc = Objects.requireNonNull(factory).getConnection(); Cursor cursor = rc.scan(options); List result = new ArrayList<>(); while (cursor.hasNext()) { result.add(new String(cursor.next())); } try { RedisConnectionUtils.releaseConnection(rc, factory); } catch (Exception e) { log.error(e.getMessage(), e); } return result; } /** * 分页查询 key * * @param patternKey key * @param page 页码 * @param size 每页数目 * @return / */ public List findKeysForPage(String patternKey, int page, int size) { ScanOptions options = ScanOptions.scanOptions().match(patternKey).build(); RedisConnectionFactory factory = redisTemplate.getConnectionFactory(); RedisConnection rc = Objects.requireNonNull(factory).getConnection(); Cursor cursor = rc.scan(options); List result = new ArrayList<>(size); int tmpIndex = 0; int fromIndex = page * size; int toIndex = page * size + size; while (cursor.hasNext()) { if (tmpIndex >= fromIndex && tmpIndex < toIndex) { result.add(new String(cursor.next())); tmpIndex++; continue; } // 获取到满足条件的数据后,就可以退出了 if (tmpIndex >= toIndex) { break; } tmpIndex++; cursor.next(); } try { RedisConnectionUtils.releaseConnection(rc, factory); } catch (Exception e) { log.error(e.getMessage(), e); } return result; } /** * 判断key是否存在 * * @param key 键 * @return true 存在 false不存在 */ public boolean hasKey(String key) { try { return redisTemplate.hasKey(key); } catch (Exception e) { log.error(e.getMessage(), e); return false; } } /** * 删除缓存 * * @param key 可以传一个值 或多个 */ public void del(String... keys) { if (keys != null && keys.length > 0) { if (keys.length == 1) { boolean result = redisTemplate.delete(keys[0]); log.debug("--------------------------------------------"); log.debug(new StringBuilder("删除缓存:").append(keys[0]).append(",结果:").append(result).toString()); log.debug("--------------------------------------------"); } else { Set keySet = new HashSet<>(); for (String key : keys) { if (redisTemplate.hasKey(key)) keySet.add(key); } long count = redisTemplate.delete(keySet); log.debug("--------------------------------------------"); log.debug("成功删除缓存:" + keySet.toString()); log.debug("缓存删除数量:" + count + "个"); log.debug("--------------------------------------------"); } } } /** * 批量模糊删除key * @param pattern */ public void scanDel(String pattern){ ScanOptions options = ScanOptions.scanOptions().match(pattern).build(); try (Cursor cursor = redisTemplate.executeWithStickyConnection( (RedisCallback>) connection -> (Cursor) new ConvertingCursor<>( connection.scan(options), redisTemplate.getKeySerializer()::deserialize))) { while (cursor.hasNext()) { redisTemplate.delete(cursor.next()); } } } // ============================String============================= /** * 普通缓存获取 * * @param key 键 * @return 值 */ public Object get(String key) { return key == null ? null : redisTemplate.opsForValue().get(key); } /** * 普通缓存获取 * * @param key 键 * @return 值 */ public T get(String key, Class clazz) { Object value = key == null ? null : redisTemplate.opsForValue().get(key); if (value == null) { return null; } // 如果 value 不是目标类型,则尝试将其反序列化为 clazz 类型 if (!clazz.isInstance(value)) { return JSON.parseObject(value.toString(), clazz); } else if (clazz.isInstance(value)) { return clazz.cast(value); } else { return null; } } /** * 普通缓存获取 * * @param key 键 * @param clazz 列表中元素的类型 * @return 值 */ public List getList(String key, Class clazz) { Object value = key == null ? null : redisTemplate.opsForValue().get(key); if (value == null) { return null; } if (value instanceof List) { List list = (List) value; // 检查每个元素是否为指定类型 if (list.stream().allMatch(clazz::isInstance)) { return list.stream().map(clazz::cast).collect(Collectors.toList()); } } return null; } /** * 普通缓存获取 * * @param key 键 * @return 值 */ public String getStr(String key) { if(StrUtil.isBlank(key)){ return null; } Object value = redisTemplate.opsForValue().get(key); if (value == null) { return null; } else { return String.valueOf(value); } } /** * 批量获取 * * @param keys * @return */ public List multiGet(List keys) { List list = redisTemplate.opsForValue().multiGet(Sets.newHashSet(keys)); List resultList = Lists.newArrayList(); Optional.ofNullable(list).ifPresent(e-> list.forEach(ele-> Optional.ofNullable(ele).ifPresent(resultList::add))); return resultList; } /** * 普通缓存放入 * * @param key 键 * @param value 值 * @return true成功 false失败 */ public boolean set(String key, Object value) { int attempt = 0; while (attempt < 3) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { attempt++; log.error("Attempt {} failed: {}", attempt, e.getMessage(), e); } } return false; } /** * 普通缓存放入并设置时间 * * @param key 键 * @param value 值 * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期,注意:这里将会替换原有的时间 * @return true成功 false 失败 */ public boolean set(String key, Object value, long time) { try { if (time > 0) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } else { set(key, value); } return true; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } /** * 普通缓存放入并设置时间 * * @param key 键 * @param value 值 * @param time 时间,注意:这里将会替换原有的时间 * @param timeUnit 类型 * @return true成功 false 失败 */ public boolean set(String key, Object value, long time, TimeUnit timeUnit) { try { if (time > 0) { redisTemplate.opsForValue().set(key, value, time, timeUnit); } else { set(key, value); } return true; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } // ================================Map================================= /** * HashGet * * @param key 键 不能为null * @param item 项 不能为null * @return 值 */ public Object hget(String key, String item) { return redisTemplate.opsForHash().get(key, item); } /** * 获取hashKey对应的所有键值 * * @param key 键 * @return 对应的多个键值 */ public Map hmget(String key) { return redisTemplate.opsForHash().entries(key); } /** * HashSet * * @param key 键 * @param map 对应多个键值 * @return true 成功 false 失败 */ public boolean hmset(String key, Map map) { try { redisTemplate.opsForHash().putAll(key, map); return true; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } /** * HashSet * * @param key 键 * @param map 对应多个键值 * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 * @return true成功 false失败 */ public boolean hmset(String key, Map map, long time) { try { redisTemplate.opsForHash().putAll(key, map); if (time > 0) { expire(key, time); } return true; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } /** * 向一张hash表中放入数据,如果不存在将创建 * * @param key 键 * @param item 项 * @param value 值 * @return true 成功 false失败 */ public boolean hset(String key, String item, Object value) { try { redisTemplate.opsForHash().put(key, item, value); return true; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } /** * 向一张hash表中放入数据,如果不存在将创建 * * @param key 键 * @param item 项 * @param value 值 * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 * @return true 成功 false失败 */ public boolean hset(String key, String item, Object value, long time) { try { redisTemplate.opsForHash().put(key, item, value); if (time > 0) { expire(key, time); } return true; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } /** * 删除hash表中的值 * * @param key 键 不能为null * @param item 项 可以使多个 不能为null */ public void hdel(String key, Object... item) { redisTemplate.opsForHash().delete(key, item); } /** * 判断hash表中是否有该项的值 * * @param key 键 不能为null * @param item 项 不能为null * @return true 存在 false不存在 */ public boolean hHasKey(String key, String item) { return redisTemplate.opsForHash().hasKey(key, item); } /** * hash递增 如果不存在,就会创建一个 并把新增后的值返回 * * @param key 键 * @param item 项 * @param by 要增加几(大于0) * @return */ public double hincr(String key, String item, double by) { return redisTemplate.opsForHash().increment(key, item, by); } /** * hash递减 * * @param key 键 * @param item 项 * @param by 要减少记(小于0) * @return */ public double hdecr(String key, String item, double by) { return redisTemplate.opsForHash().increment(key, item, -by); } // ============================set============================= /** * 根据key获取Set中的所有值 * * @param key 键 * @return */ public Set sGet(String key) { try { return redisTemplate.opsForSet().members(key); } catch (Exception e) { log.error(e.getMessage(), e); return null; } } /** * 根据value从一个set中查询,是否存在 * * @param key 键 * @param value 值 * @return true 存在 false不存在 */ public boolean sHasKey(String key, Object value) { try { return redisTemplate.opsForSet().isMember(key, value); } catch (Exception e) { log.error(e.getMessage(), e); return false; } } /** * 将数据放入set缓存 * * @param key 键 * @param values 值 可以是多个 * @return 成功个数 */ public long sSet(String key, Object... values) { try { return redisTemplate.opsForSet().add(key, values); } catch (Exception e) { log.error(e.getMessage(), e); return 0; } } /** * 将set数据放入缓存 * * @param key 键 * @param time 时间(秒) 注意:这里将会替换原有的时间 * @param values 值 可以是多个 * @return 成功个数 */ public long sSetAndTime(String key, long time, Object... values) { try { Long count = redisTemplate.opsForSet().add(key, values); if (time > 0) { expire(key, time); } return count; } catch (Exception e) { log.error(e.getMessage(), e); return 0; } } /** * 获取set缓存的长度 * * @param key 键 * @return */ public long sGetSetSize(String key) { try { return redisTemplate.opsForSet().size(key); } catch (Exception e) { log.error(e.getMessage(), e); return 0; } } /** * 移除值为value的 * * @param key 键 * @param values 值 可以是多个 * @return 移除的个数 */ public long setRemove(String key, Object... values) { try { Long count = redisTemplate.opsForSet().remove(key, values); return count; } catch (Exception e) { log.error(e.getMessage(), e); return 0; } } // ===============================list================================= /** * 获取list缓存的内容 * * @param key 键 * @param start 开始 * @param end 结束 0 到 -1代表所有值 * @return */ public List lGet(String key, long start, long end) { try { return redisTemplate.opsForList().range(key, start, end); } catch (Exception e) { log.error(e.getMessage(), e); return null; } } /** * 获取list缓存的长度 * * @param key 键 * @return */ public long lGetListSize(String key) { try { return redisTemplate.opsForList().size(key); } catch (Exception e) { log.error(e.getMessage(), e); return 0; } } /** * 通过索引 获取list中的值 * * @param key 键 * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 * @return */ public Object lGetIndex(String key, long index) { try { return redisTemplate.opsForList().index(key, index); } catch (Exception e) { log.error(e.getMessage(), e); return null; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 * @return */ public boolean lSet(String key, Object value) { try { redisTemplate.opsForList().rightPush(key, value); return true; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 * @param time 时间(秒) 注意:这里将会替换原有的时间 * @return */ public boolean lSet(String key, Object value, long time) { try { redisTemplate.opsForList().rightPush(key, value); if (time > 0) { expire(key, time); } return true; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 * @return */ public boolean lSet(String key, List value) { try { redisTemplate.opsForList().rightPushAll(key, value); return true; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 * @param time 时间(秒) 注意:这里将会替换原有的时间 * @return */ public boolean lSet(String key, List value, long time) { try { redisTemplate.opsForList().rightPushAll(key, value); if (time > 0) { expire(key, time); } return true; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } /** * 根据索引修改list中的某条数据 * * @param key 键 * @param index 索引 * @param value 值 * @return / */ public boolean lUpdateIndex(String key, long index, Object value) { try { redisTemplate.opsForList().set(key, index, value); return true; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } /** * 移除N个值为value * * @param key 键 * @param count 移除多少个 * @param value 值 * @return 移除的个数 */ public long lRemove(String key, long count, Object value) { try { return redisTemplate.opsForList().remove(key, count, value); } catch (Exception e) { log.error(e.getMessage(), e); return 0; } } /** * @param prefix 前缀 * @param ids id */ public void delByKeys(String prefix, Set ids) { Set keys = new HashSet<>(); for (Long id : ids) { keys.addAll(redisTemplate.keys(new StringBuffer(prefix).append(id).toString())); } long count = redisTemplate.delete(keys); } // ============================incr============================= /** * 递增 * @param key * @return */ public Long increment(String key) { return redisTemplate.opsForValue().increment(key); } /** * 递减 * @param key * @return */ public Long decrement(String key) { return redisTemplate.opsForValue().decrement(key); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/RequestHolder.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Objects; /** * 获取 HttpServletRequest * @author Zheng Jie * @date 2018-11-24 */ public class RequestHolder { public static HttpServletRequest getHttpServletRequest() { return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/RsaUtils.java ================================================ package me.zhengjie.utils; import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import java.io.ByteArrayOutputStream; import java.security.*; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; /** * @author https://www.cnblogs.com/nihaorz/p/10690643.html * @description Rsa 工具类,公钥私钥生成,加解密 * @date 2020-05-18 **/ public class RsaUtils { private static final String SRC = "123456"; public static void main(String[] args) throws Exception { System.out.println("\n"); RsaKeyPair keyPair = generateKeyPair(); System.out.println("公钥:" + keyPair.getPublicKey()); System.out.println("私钥:" + keyPair.getPrivateKey()); System.out.println("\n"); test1(keyPair); System.out.println("\n"); test2(keyPair); System.out.println("\n"); } /** * 公钥加密私钥解密 */ private static void test1(RsaKeyPair keyPair) throws Exception { System.out.println("***************** 公钥加密私钥解密开始 *****************"); String text1 = encryptByPublicKey(keyPair.getPublicKey(), RsaUtils.SRC); String text2 = decryptByPrivateKey(keyPair.getPrivateKey(), text1); System.out.println("加密前:" + RsaUtils.SRC); System.out.println("加密后:" + text1); System.out.println("解密后:" + text2); if (RsaUtils.SRC.equals(text2)) { System.out.println("解密字符串和原始字符串一致,解密成功"); } else { System.out.println("解密字符串和原始字符串不一致,解密失败"); } System.out.println("***************** 公钥加密私钥解密结束 *****************"); } /** * 私钥加密公钥解密 * @throws Exception / */ private static void test2(RsaKeyPair keyPair) throws Exception { System.out.println("***************** 私钥加密公钥解密开始 *****************"); String text1 = encryptByPrivateKey(keyPair.getPrivateKey(), RsaUtils.SRC); String text2 = decryptByPublicKey(keyPair.getPublicKey(), text1); System.out.println("加密前:" + RsaUtils.SRC); System.out.println("加密后:" + text1); System.out.println("解密后:" + text2); if (RsaUtils.SRC.equals(text2)) { System.out.println("解密字符串和原始字符串一致,解密成功"); } else { System.out.println("解密字符串和原始字符串不一致,解密失败"); } System.out.println("***************** 私钥加密公钥解密结束 *****************"); } /** * 公钥解密 * * @param publicKeyText 公钥 * @param text 待解密的信息 * @return / * @throws Exception / */ public static String decryptByPublicKey(String publicKeyText, String text) throws Exception { X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, publicKey); byte[] result = doLongerCipherFinal(Cipher.DECRYPT_MODE, cipher, Base64.decodeBase64(text)); return new String(result); } /** * 私钥加密 * * @param privateKeyText 私钥 * @param text 待加密的信息 * @return / * @throws Exception / */ public static String encryptByPrivateKey(String privateKeyText, String text) throws Exception { PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, privateKey); byte[] result = doLongerCipherFinal(Cipher.ENCRYPT_MODE, cipher, text.getBytes()); return Base64.encodeBase64String(result); } /** * 私钥解密 * * @param privateKeyText 私钥 * @param text 待解密的文本 * @return / * @throws Exception / */ public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception { PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] result = doLongerCipherFinal(Cipher.DECRYPT_MODE, cipher, Base64.decodeBase64(text)); return new String(result); } /** * 公钥加密 * * @param publicKeyText 公钥 * @param text 待加密的文本 * @return / */ public static String encryptByPublicKey(String publicKeyText, String text) throws Exception { X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] result = doLongerCipherFinal(Cipher.ENCRYPT_MODE, cipher, text.getBytes()); return Base64.encodeBase64String(result); } private static byte[] doLongerCipherFinal(int opMode,Cipher cipher, byte[] source) throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); if (opMode == Cipher.DECRYPT_MODE) { out.write(cipher.doFinal(source)); } else { int offset = 0; int totalSize = source.length; while (totalSize - offset > 0) { int size = Math.min(cipher.getOutputSize(0) - 11, totalSize - offset); out.write(cipher.doFinal(source, offset, size)); offset += size; } } out.close(); return out.toByteArray(); } /** * 构建RSA密钥对 * * @return / * @throws NoSuchAlgorithmException / */ public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(1024); KeyPair keyPair = keyPairGenerator.generateKeyPair(); RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded()); String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded()); return new RsaKeyPair(publicKeyString, privateKeyString); } /** * RSA密钥对对象 */ public static class RsaKeyPair { private final String publicKey; private final String privateKey; public RsaKeyPair(String publicKey, String privateKey) { this.publicKey = publicKey; this.privateKey = privateKey; } public String getPublicKey() { return publicKey; } public String getPrivateKey() { return privateKey; } } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import cn.hutool.core.collection.CollUtil; import cn.hutool.jwt.JWT; import cn.hutool.jwt.JWTUtil; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import lombok.extern.slf4j.Slf4j; import me.zhengjie.utils.enums.DataScopeEnum; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.Objects; /** * 获取当前登录的用户 * @author Zheng Jie * @date 2019-01-17 */ @Slf4j @Component public class SecurityUtils { public static String header; public static String tokenStartWith; @Value("${jwt.header}") public void setHeader(String header) { SecurityUtils.header = header; } @Value("${jwt.token-start-with}") public void setTokenStartWith(String tokenStartWith) { SecurityUtils.tokenStartWith = tokenStartWith; } /** * 获取当前登录的用户 * @return UserDetails */ public static UserDetails getCurrentUser() { UserDetailsService userDetailsService = SpringBeanHolder.getBean(UserDetailsService.class); return userDetailsService.loadUserByUsername(getCurrentUsername()); } /** * 获取当前用户的数据权限 * @return / */ public static List getCurrentUserDataScope(){ UserDetails userDetails = getCurrentUser(); // 将 Java 对象转换为 JSONObject 对象 JSONObject jsonObject = (JSONObject) JSON.toJSON(userDetails); JSONArray jsonArray = jsonObject.getJSONArray("dataScopes"); return JSON.parseArray(jsonArray.toJSONString(), Long.class); } /** * 获取数据权限级别 * @return 级别 */ public static String getDataScopeType() { List dataScopes = getCurrentUserDataScope(); if(CollUtil.isEmpty(dataScopes)){ return ""; } return DataScopeEnum.ALL.getValue(); } /** * 获取用户ID * @return 系统用户ID */ public static Long getCurrentUserId() { return getCurrentUserId(getToken()); } /** * 获取用户ID * @return 系统用户ID */ public static Long getCurrentUserId(String token) { JWT jwt = JWTUtil.parseToken(token); return Long.valueOf(jwt.getPayload("userId").toString()); } /** * 获取系统用户名称 * * @return 系统用户名称 */ public static String getCurrentUsername() { return getCurrentUsername(getToken()); } /** * 获取系统用户名称 * * @return 系统用户名称 */ public static String getCurrentUsername(String token) { JWT jwt = JWTUtil.parseToken(token); return jwt.getPayload("sub").toString(); } /** * 获取Token * @return / */ public static String getToken() { HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder .getRequestAttributes())).getRequest(); String bearerToken = request.getHeader(header); if (bearerToken != null && bearerToken.startsWith(tokenStartWith)) { // 去掉令牌前缀 return bearerToken.replace(tokenStartWith, ""); } else { log.debug("非法Token:{}", bearerToken); } return null; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/SpringBeanHolder.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.beans.factory.DisposableBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * @author Jie * @date 2019-01-07 */ @Slf4j @SuppressWarnings({"unchecked","all"}) public class SpringBeanHolder implements ApplicationContextAware, DisposableBean { private static ApplicationContext applicationContext = null; private static final List CALL_BACKS = new ArrayList<>(); private static boolean addCallback = true; /** * 针对 某些初始化方法,在SpringContextHolder 未初始化时 提交回调方法。 * 在SpringContextHolder 初始化后,进行回调使用 * * @param callBack 回调函数 */ public synchronized static void addCallBacks(SpringBeanHolder.CallBack callBack) { if (addCallback) { SpringBeanHolder.CALL_BACKS.add(callBack); } else { log.warn("CallBack:{} 已无法添加!立即执行", callBack.getCallBackName()); callBack.executor(); } } /** * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. */ public static T getBean(String name) { assertContextInjected(); return (T) applicationContext.getBean(name); } /** * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. */ public static T getBean(Class requiredType) { assertContextInjected(); return applicationContext.getBean(requiredType); } /** * 获取SpringBoot 配置信息 * * @param property 属性key * @param defaultValue 默认值 * @param requiredType 返回类型 * @return / */ public static T getProperties(String property, T defaultValue, Class requiredType) { T result = defaultValue; try { result = getBean(Environment.class).getProperty(property, requiredType); } catch (Exception ignored) {} return result; } /** * 获取SpringBoot 配置信息 * * @param property 属性key * @return / */ public static String getProperties(String property) { return getProperties(property, null, String.class); } /** * 获取SpringBoot 配置信息 * * @param property 属性key * @param requiredType 返回类型 * @return / */ public static T getProperties(String property, Class requiredType) { return getProperties(property, null, requiredType); } /** * 检查ApplicationContext不为空. */ private static void assertContextInjected() { if (applicationContext == null) { throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" + ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder."); } } /** * 清除SpringContextHolder中的ApplicationContext为Null. */ private static void clearHolder() { log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext); applicationContext = null; } @Override public void destroy() { SpringBeanHolder.clearHolder(); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (SpringBeanHolder.applicationContext != null) { log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringBeanHolder.applicationContext); } SpringBeanHolder.applicationContext = applicationContext; if (addCallback) { for (SpringBeanHolder.CallBack callBack : SpringBeanHolder.CALL_BACKS) { callBack.executor(); } CALL_BACKS.clear(); } SpringBeanHolder.addCallback = false; } /** * 获取 @Service 的所有 bean 名称 * @return / */ public static List getAllServiceBeanName() { return new ArrayList<>(Arrays.asList(applicationContext .getBeanNamesForAnnotation(Service.class))); } interface CallBack { /** * 回调执行方法 */ void executor(); /** * 本回调任务名称 * @return / */ default String getCallBackName() { return Thread.currentThread().getId() + ":" + this.getClass().getName(); } } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/StringUtils.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgentUtil; import lombok.extern.slf4j.Slf4j; import net.dreamlu.mica.ip2region.core.Ip2regionSearcher; import net.dreamlu.mica.ip2region.core.IpInfo; import javax.servlet.http.HttpServletRequest; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.UnknownHostException; import java.util.Calendar; import java.util.Date; import java.util.Enumeration; /** * @author Zheng Jie * 字符串工具类, 继承org.apache.commons.lang3.StringUtils类 */ @Slf4j public class StringUtils extends org.apache.commons.lang3.StringUtils { private static final char SEPARATOR = '_'; private static final String UNKNOWN = "unknown"; /** * 注入bean */ private final static Ip2regionSearcher IP_SEARCHER = SpringBeanHolder.getBean(Ip2regionSearcher.class); /** * 驼峰命名法工具 * * @return toCamelCase(" hello_world ") == "helloWorld" * toCapitalizeCamelCase("hello_world") == "HelloWorld" * toUnderScoreCase("helloWorld") = "hello_world" */ public static String toCamelCase(String s) { if (s == null) { return null; } s = s.toLowerCase(); StringBuilder sb = new StringBuilder(s.length()); boolean upperCase = false; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == SEPARATOR) { upperCase = true; } else if (upperCase) { sb.append(Character.toUpperCase(c)); upperCase = false; } else { sb.append(c); } } return sb.toString(); } /** * 驼峰命名法工具 * * @return toCamelCase(" hello_world ") == "helloWorld" * toCapitalizeCamelCase("hello_world") == "HelloWorld" * toUnderScoreCase("helloWorld") = "hello_world" */ public static String toCapitalizeCamelCase(String s) { if (s == null) { return null; } s = toCamelCase(s); return s.substring(0, 1).toUpperCase() + s.substring(1); } /** * 驼峰命名法工具 * * @return toCamelCase(" hello_world ") == "helloWorld" * toCapitalizeCamelCase("hello_world") == "HelloWorld" * toUnderScoreCase("helloWorld") = "hello_world" */ static String toUnderScoreCase(String s) { if (s == null) { return null; } StringBuilder sb = new StringBuilder(); boolean upperCase = false; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); boolean nextUpperCase = true; if (i < (s.length() - 1)) { nextUpperCase = Character.isUpperCase(s.charAt(i + 1)); } if ((i > 0) && Character.isUpperCase(c)) { if (!upperCase || !nextUpperCase) { sb.append(SEPARATOR); } upperCase = true; } else { upperCase = false; } sb.append(Character.toLowerCase(c)); } return sb.toString(); } /** * 获取ip地址 */ public static String getIp(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } String comma = ","; String localhost = "127.0.0.1"; if (ip.contains(comma)) { ip = ip.split(",")[0]; } if (localhost.equals(ip)) { // 获取本机真正的ip地址 try { ip = InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException e) { log.error(e.getMessage(), e); } } return ip; } /** * 根据ip获取详细地址 */ public static String getCityInfo(String ip) { IpInfo ipInfo = IP_SEARCHER.memorySearch(ip); if(ipInfo != null){ return ipInfo.getAddress(); } return null; } /** * 获取浏览器 */ public static String getBrowser(HttpServletRequest request) { UserAgent ua = UserAgentUtil.parse(request.getHeader("User-Agent")); String browser = ua.getBrowser().toString() + " " + ua.getVersion(); return browser.replace(".0.0.0",""); } /** * 获得当天是周几 */ public static String getWeekDay() { String[] weekDays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; Calendar cal = Calendar.getInstance(); cal.setTime(new Date()); int w = cal.get(Calendar.DAY_OF_WEEK) - 1; if (w < 0) { w = 0; } return weekDays[w]; } /** * 获取当前机器的IP * * @return / */ public static String getLocalIp() { try { InetAddress candidateAddress = null; // 遍历所有的网络接口 for (Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); interfaces.hasMoreElements();) { NetworkInterface anInterface = interfaces.nextElement(); // 在所有的接口下再遍历IP for (Enumeration inetAddresses = anInterface.getInetAddresses(); inetAddresses.hasMoreElements();) { InetAddress inetAddr = inetAddresses.nextElement(); // 排除loopback类型地址 if (!inetAddr.isLoopbackAddress()) { if (inetAddr.isSiteLocalAddress()) { // 如果是site-local地址,就是它了 return inetAddr.getHostAddress(); } else if (candidateAddress == null) { // site-local类型的地址未被发现,先记录候选地址 candidateAddress = inetAddr; } } } } if (candidateAddress != null) { return candidateAddress.getHostAddress(); } // 如果没有发现 non-loopback地址.只能用最次选的方案 InetAddress jdkSuppliedAddress = InetAddress.getLocalHost(); if (jdkSuppliedAddress == null) { return ""; } return jdkSuppliedAddress.getHostAddress(); } catch (Exception e) { return ""; } } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/ThrowableUtil.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import java.io.PrintWriter; import java.io.StringWriter; /** * 异常工具 2019-01-06 * @author Zheng Jie */ public class ThrowableUtil { /** * 获取堆栈信息 */ public static String getStackTrace(Throwable throwable){ StringWriter sw = new StringWriter(); try (PrintWriter pw = new PrintWriter(sw)) { throwable.printStackTrace(pw); return sw.toString(); } } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/ValidationUtil.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import cn.hutool.core.lang.Validator; import cn.hutool.core.util.ObjectUtil; import me.zhengjie.exception.BadRequestException; /** * 验证工具 * * @author Zheng Jie * @date 2018-11-23 */ public class ValidationUtil { /** * 验证空 */ public static void isNull(Object obj, String entity, String parameter , Object value){ if(ObjectUtil.isNull(obj)){ String msg = entity + " 不存在: "+ parameter +" is "+ value; throw new BadRequestException(msg); } } /** * 验证是否为邮箱 */ public static boolean isEmail(String email) { return Validator.isEmail(email); } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/enums/CodeBiEnum.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils.enums; import lombok.AllArgsConstructor; import lombok.Getter; /** *

* 验证码业务场景 *

* @author Zheng Jie * @date 2020-05-02 */ @Getter @AllArgsConstructor public enum CodeBiEnum { /* 旧邮箱修改邮箱 */ ONE(1, "旧邮箱修改邮箱"), /* 通过邮箱修改密码 */ TWO(2, "通过邮箱修改密码"); private final Integer code; private final String description; public static CodeBiEnum find(Integer code) { for (CodeBiEnum value : CodeBiEnum.values()) { if (value.getCode().equals(code)) { return value; } } return null; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/enums/CodeEnum.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils.enums; import lombok.AllArgsConstructor; import lombok.Getter; /** *

* 验证码业务场景对应的 Redis 中的 key *

* @author Zheng Jie * @date 2020-05-02 */ @Getter @AllArgsConstructor public enum CodeEnum { /* 通过手机号码重置邮箱 */ PHONE_RESET_EMAIL_CODE("phone_reset_email_code_", "通过手机号码重置邮箱"), /* 通过旧邮箱重置邮箱 */ EMAIL_RESET_EMAIL_CODE("email_reset_email_code_", "通过旧邮箱重置邮箱"), /* 通过手机号码重置密码 */ PHONE_RESET_PWD_CODE("phone_reset_pwd_code_", "通过手机号码重置密码"), /* 通过邮箱重置密码 */ EMAIL_RESET_PWD_CODE("email_reset_pwd_code_", "通过邮箱重置密码"); private final String key; private final String description; } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/enums/DataScopeEnum.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils.enums; import lombok.AllArgsConstructor; import lombok.Getter; /** *

* 数据权限枚举 *

* @author Zheng Jie * @date 2020-05-07 */ @Getter @AllArgsConstructor public enum DataScopeEnum { /* 全部的数据权限 */ ALL("全部", "全部的数据权限"), /* 自己部门的数据权限 */ THIS_LEVEL("本级", "自己部门的数据权限"), /* 自定义的数据权限 */ CUSTOMIZE("自定义", "自定义的数据权限"); private final String value; private final String description; public static DataScopeEnum find(String val) { for (DataScopeEnum dataScopeEnum : DataScopeEnum.values()) { if (dataScopeEnum.getValue().equals(val)) { return dataScopeEnum; } } return null; } } ================================================ FILE: eladmin-common/src/main/java/me/zhengjie/utils/enums/RequestMethodEnum.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils.enums; import lombok.AllArgsConstructor; import lombok.Getter; /** * @author Zheng Jie * @website https://eladmin.vip * @description * @date 2020-06-10 **/ @Getter @AllArgsConstructor public enum RequestMethodEnum { /** * 搜寻 @AnonymousGetMapping */ GET("GET"), /** * 搜寻 @AnonymousPostMapping */ POST("POST"), /** * 搜寻 @AnonymousPutMapping */ PUT("PUT"), /** * 搜寻 @AnonymousPatchMapping */ PATCH("PATCH"), /** * 搜寻 @AnonymousDeleteMapping */ DELETE("DELETE"), /** * 否则就是所有 Request 接口都放行 */ ALL("All"); /** * Request 类型 */ private final String type; public static RequestMethodEnum find(String type) { for (RequestMethodEnum value : RequestMethodEnum.values()) { if (value.getType().equals(type)) { return value; } } return ALL; } } ================================================ FILE: eladmin-common/src/test/java/me/zhengjie/utils/DateUtilsTest.java ================================================ package me.zhengjie.utils; import org.junit.jupiter.api.Test; import java.time.LocalDateTime; import java.util.Date; public class DateUtilsTest { @Test public void test1() { long l = System.currentTimeMillis() / 1000; LocalDateTime localDateTime = DateUtil.fromTimeStamp(l); System.out.print(DateUtil.localDateTimeFormatyMdHms(localDateTime)); } @Test public void test2() { LocalDateTime now = LocalDateTime.now(); System.out.println(DateUtil.localDateTimeFormatyMdHms(now)); Date date = DateUtil.toDate(now); LocalDateTime localDateTime = DateUtil.toLocalDateTime(date); System.out.println(DateUtil.localDateTimeFormatyMdHms(localDateTime)); LocalDateTime localDateTime1 = DateUtil.fromTimeStamp(date.getTime() / 1000); System.out.println(DateUtil.localDateTimeFormatyMdHms(localDateTime1)); } } ================================================ FILE: eladmin-common/src/test/java/me/zhengjie/utils/EncryptUtilsTest.java ================================================ package me.zhengjie.utils; import org.junit.jupiter.api.Test; import static me.zhengjie.utils.EncryptUtils.*; import static org.junit.jupiter.api.Assertions.assertEquals; public class EncryptUtilsTest { /** * 对称加密 */ @Test public void testDesEncrypt() { try { assertEquals("7772841DC6099402", desEncrypt("123456")); } catch (Exception e) { e.printStackTrace(); } } /** * 对称解密 */ @Test public void testDesDecrypt() { try { assertEquals("123456", desDecrypt("7772841DC6099402")); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: eladmin-common/src/test/java/me/zhengjie/utils/FileUtilTest.java ================================================ package me.zhengjie.utils; import org.junit.jupiter.api.Test; import org.springframework.mock.web.MockMultipartFile; import static me.zhengjie.utils.FileUtil.*; import static org.junit.jupiter.api.Assertions.assertEquals; public class FileUtilTest { @Test public void testToFile() { long retval = toFile(new MockMultipartFile("foo", (byte[]) null)).getTotalSpace(); assertEquals(500695072768L, retval); } @Test public void testGetExtensionName() { assertEquals("foo", getExtensionName("foo")); assertEquals("exe", getExtensionName("bar.exe")); } @Test public void testGetFileNameNoEx() { assertEquals("foo", getFileNameNoEx("foo")); assertEquals("bar", getFileNameNoEx("bar.txt")); } @Test public void testGetSize() { assertEquals("1000B ", getSize(1000)); assertEquals("1.00KB ", getSize(1024)); assertEquals("1.00MB ", getSize(1048576)); assertEquals("1.00GB ", getSize(1073741824)); } } ================================================ FILE: eladmin-common/src/test/java/me/zhengjie/utils/StringUtilsTest.java ================================================ package me.zhengjie.utils; import org.junit.jupiter.api.Test; import org.springframework.mock.web.MockHttpServletRequest; import java.text.SimpleDateFormat; import java.util.Date; import static me.zhengjie.utils.StringUtils.getIp; import static me.zhengjie.utils.StringUtils.getWeekDay; import static me.zhengjie.utils.StringUtils.toCamelCase; import static me.zhengjie.utils.StringUtils.toCapitalizeCamelCase; import static me.zhengjie.utils.StringUtils.toUnderScoreCase; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; public class StringUtilsTest { @Test public void testToCamelCase() { assertNull(toCamelCase(null)); } @Test public void testToCapitalizeCamelCase() { assertNull(StringUtils.toCapitalizeCamelCase(null)); assertEquals("HelloWorld", toCapitalizeCamelCase("hello_world")); } @Test public void testToUnderScoreCase() { assertNull(StringUtils.toUnderScoreCase(null)); assertEquals("hello_world", toUnderScoreCase("helloWorld")); assertEquals("\u0000\u0000", toUnderScoreCase("\u0000\u0000")); assertEquals("\u0000_a", toUnderScoreCase("\u0000A")); } @Test public void testGetWeekDay() { SimpleDateFormat simpleDateformat = new SimpleDateFormat("E"); assertEquals(simpleDateformat.format(new Date()), getWeekDay()); } @Test public void testGetIP() { assertEquals("127.0.0.1", getIp(new MockHttpServletRequest())); } } ================================================ FILE: eladmin-generator/pom.xml ================================================ eladmin me.zhengjie 2.7 4.0.0 eladmin-generator 代码生成模块 1.10 me.zhengjie eladmin-common 2.7 org.springframework.boot spring-boot-starter-freemarker commons-configuration commons-configuration ${configuration.version} ================================================ FILE: eladmin-generator/src/main/java/me/zhengjie/domain/ColumnInfo.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import me.zhengjie.utils.GenUtil; import javax.persistence.*; import java.io.Serializable; /** * 列的数据信息 * @author Zheng Jie * @date 2019-01-02 */ @Getter @Setter @Entity @NoArgsConstructor @Table(name = "code_column") public class ColumnInfo implements Serializable { @Id @Column(name = "column_id") @ApiModelProperty(value = "ID", hidden = true) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ApiModelProperty(value = "表名") private String tableName; @ApiModelProperty(value = "数据库字段名称") private String columnName; @ApiModelProperty(value = "数据库字段类型") private String columnType; @ApiModelProperty(value = "数据库字段键类型") private String keyType; @ApiModelProperty(value = "字段额外的参数") private String extra; @ApiModelProperty(value = "数据库字段描述") private String remark; @ApiModelProperty(value = "是否必填") private Boolean notNull; @ApiModelProperty(value = "是否在列表显示") private Boolean listShow; @ApiModelProperty(value = "是否表单显示") private Boolean formShow; @ApiModelProperty(value = "表单类型") private String formType; @ApiModelProperty(value = "查询 1:模糊 2:精确") private String queryType; @ApiModelProperty(value = "字典名称") private String dictName; @ApiModelProperty(value = "日期注解") private String dateAnnotation; public ColumnInfo(String tableName, String columnName, Boolean notNull, String columnType, String remark, String keyType, String extra) { this.tableName = tableName; this.columnName = columnName; this.columnType = columnType; this.keyType = keyType; this.extra = extra; this.notNull = notNull; if(GenUtil.PK.equalsIgnoreCase(keyType) && GenUtil.EXTRA.equalsIgnoreCase(extra)){ this.notNull = false; } this.remark = remark; this.listShow = true; this.formShow = true; } } ================================================ FILE: eladmin-generator/src/main/java/me/zhengjie/domain/GenConfig.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import javax.persistence.*; import javax.validation.constraints.NotBlank; import java.io.Serializable; /** * 代码生成配置 * @author Zheng Jie * @date 2019-01-03 */ @Getter @Setter @Entity @NoArgsConstructor @Table(name = "code_config") public class GenConfig implements Serializable { public GenConfig(String tableName) { this.tableName = tableName; } @Id @Column(name = "config_id") @ApiModelProperty(value = "ID", hidden = true) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotBlank @ApiModelProperty(value = "表名") private String tableName; @ApiModelProperty(value = "接口名称") private String apiAlias; @NotBlank @ApiModelProperty(value = "包路径") private String pack; @NotBlank @ApiModelProperty(value = "模块名") private String moduleName; @NotBlank @ApiModelProperty(value = "前端文件路径") private String path; @ApiModelProperty(value = "前端文件路径") private String apiPath; @ApiModelProperty(value = "作者") private String author; @ApiModelProperty(value = "表前缀") private String prefix; @ApiModelProperty(value = "是否覆盖") private Boolean cover = false; } ================================================ FILE: eladmin-generator/src/main/java/me/zhengjie/domain/vo/TableInfo.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.domain.vo; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * 表的数据信息 * @author Zheng Jie * @date 2019-01-02 */ @Data @AllArgsConstructor @NoArgsConstructor public class TableInfo { @ApiModelProperty(value = "表名称") private Object tableName; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") @ApiModelProperty(value = "创建日期:yyyy-MM-dd HH:mm:ss") private Object createTime; @ApiModelProperty(value = "数据库引擎") private Object engine; @ApiModelProperty(value = "编码集") private Object coding; @ApiModelProperty(value = "备注") private Object remark; } ================================================ FILE: eladmin-generator/src/main/java/me/zhengjie/repository/ColumnInfoRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.repository; import me.zhengjie.domain.ColumnInfo; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; /** * @author Zheng Jie * @date 2019-01-14 */ public interface ColumnInfoRepository extends JpaRepository { /** * 查询表信息 * @param tableName 表格名 * @return 表信息 */ List findByTableNameOrderByIdAsc(String tableName); } ================================================ FILE: eladmin-generator/src/main/java/me/zhengjie/repository/GenConfigRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.repository; import me.zhengjie.domain.GenConfig; import org.springframework.data.jpa.repository.JpaRepository; /** * @author Zheng Jie * @date 2019-01-14 */ public interface GenConfigRepository extends JpaRepository { /** * 查询表配置 * @param tableName 表名 * @return / */ GenConfig findByTableName(String tableName); } ================================================ FILE: eladmin-generator/src/main/java/me/zhengjie/rest/GenConfigController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.domain.GenConfig; import me.zhengjie.service.GenConfigService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; /** * @author Zheng Jie * @date 2019-01-14 */ @RestController @RequiredArgsConstructor @RequestMapping("/api/genConfig") @Api(tags = "系统:代码生成器配置管理") public class GenConfigController { private final GenConfigService genConfigService; @ApiOperation("查询") @GetMapping(value = "/{tableName}") public ResponseEntity queryGenConfig(@PathVariable String tableName){ return new ResponseEntity<>(genConfigService.find(tableName), HttpStatus.OK); } @PutMapping @ApiOperation("修改") public ResponseEntity updateGenConfig(@Validated @RequestBody GenConfig genConfig){ return new ResponseEntity<>(genConfigService.update(genConfig.getTableName(), genConfig),HttpStatus.OK); } } ================================================ FILE: eladmin-generator/src/main/java/me/zhengjie/rest/GeneratorController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.domain.ColumnInfo; import me.zhengjie.domain.vo.TableInfo; import me.zhengjie.exception.BadRequestException; import me.zhengjie.service.GenConfigService; import me.zhengjie.service.GeneratorService; import me.zhengjie.utils.PageResult; import me.zhengjie.utils.PageUtil; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.List; /** * @author Zheng Jie * @date 2019-01-02 */ @RestController @RequiredArgsConstructor @RequestMapping("/api/generator") @Api(tags = "系统:代码生成管理") public class GeneratorController { private final GeneratorService generatorService; private final GenConfigService genConfigService; @Value("${generator.enabled}") private Boolean generatorEnabled; @ApiOperation("查询数据库数据") @GetMapping(value = "/tables/all") public ResponseEntity queryAllTables(){ return new ResponseEntity<>(generatorService.getTables(), HttpStatus.OK); } @ApiOperation("查询数据库数据") @GetMapping(value = "/tables") public ResponseEntity> queryTables(@RequestParam(defaultValue = "") String name, @RequestParam(defaultValue = "0")Integer page, @RequestParam(defaultValue = "10")Integer size){ int[] startEnd = PageUtil.transToStartEnd(page, size); return new ResponseEntity<>(generatorService.getTables(name,startEnd), HttpStatus.OK); } @ApiOperation("查询字段数据") @GetMapping(value = "/columns") public ResponseEntity> queryColumns(@RequestParam String tableName){ List columnInfos = generatorService.getColumns(tableName); return new ResponseEntity<>(PageUtil.toPage(columnInfos,columnInfos.size()), HttpStatus.OK); } @ApiOperation("保存字段数据") @PutMapping public ResponseEntity saveColumn(@RequestBody List columnInfos){ generatorService.save(columnInfos); return new ResponseEntity<>(HttpStatus.OK); } @ApiOperation("同步字段数据") @PostMapping(value = "sync") public ResponseEntity syncColumn(@RequestBody List tables){ for (String table : tables) { generatorService.sync(generatorService.getColumns(table), generatorService.query(table)); } return new ResponseEntity<>(HttpStatus.OK); } @ApiOperation("生成代码") @PostMapping(value = "/{tableName}/{type}") public ResponseEntity generatorCode(@PathVariable String tableName, @PathVariable Integer type, HttpServletRequest request, HttpServletResponse response){ if(!generatorEnabled && type == 0){ throw new BadRequestException("此环境不允许生成代码,请选择预览或者下载查看!"); } switch (type){ // 生成代码 case 0: generatorService.generator(genConfigService.find(tableName), generatorService.getColumns(tableName)); break; // 预览 case 1: return generatorService.preview(genConfigService.find(tableName), generatorService.getColumns(tableName)); // 打包 case 2: generatorService.download(genConfigService.find(tableName), generatorService.getColumns(tableName), request, response); break; default: throw new BadRequestException("没有这个选项"); } return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-generator/src/main/java/me/zhengjie/service/GenConfigService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service; import me.zhengjie.domain.GenConfig; /** * @author Zheng Jie * @date 2019-01-14 */ public interface GenConfigService { /** * 查询表配置 * @param tableName 表名 * @return 表配置 */ GenConfig find(String tableName); /** * 更新表配置 * @param tableName 表名 * @param genConfig 表配置 * @return 表配置 */ GenConfig update(String tableName, GenConfig genConfig); } ================================================ FILE: eladmin-generator/src/main/java/me/zhengjie/service/GeneratorService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service; import me.zhengjie.domain.GenConfig; import me.zhengjie.domain.ColumnInfo; import me.zhengjie.domain.vo.TableInfo; import me.zhengjie.utils.PageResult; import org.springframework.http.ResponseEntity; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.List; /** * @author Zheng Jie * @date 2019-01-02 */ public interface GeneratorService { /** * 查询数据库元数据 * @param name 表名 * @param startEnd 分页参数 * @return / */ PageResult getTables(String name, int[] startEnd); /** * 得到数据表的元数据 * @param name 表名 * @return / */ List getColumns(String name); /** * 同步表数据 * @param columnInfos / * @param columnInfoList / */ void sync(List columnInfos, List columnInfoList); /** * 保持数据 * @param columnInfos / */ void save(List columnInfos); /** * 获取所有table * @return / */ Object getTables(); /** * 代码生成 * @param genConfig 配置信息 * @param columns 字段信息 */ void generator(GenConfig genConfig, List columns); /** * 预览 * @param genConfig 配置信息 * @param columns 字段信息 * @return / */ ResponseEntity preview(GenConfig genConfig, List columns); /** * 打包下载 * @param genConfig 配置信息 * @param columns 字段信息 * @param request / * @param response / */ void download(GenConfig genConfig, List columns, HttpServletRequest request, HttpServletResponse response); /** * 查询数据库的表字段数据数据 * @param table / * @return / */ List query(String table); } ================================================ FILE: eladmin-generator/src/main/java/me/zhengjie/service/impl/GenConfigServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.impl; import lombok.RequiredArgsConstructor; import me.zhengjie.domain.GenConfig; import me.zhengjie.repository.GenConfigRepository; import me.zhengjie.service.GenConfigService; import org.springframework.stereotype.Service; import java.io.File; /** * @author Zheng Jie * @date 2019-01-14 */ @Service @RequiredArgsConstructor @SuppressWarnings({"unchecked","all"}) public class GenConfigServiceImpl implements GenConfigService { private final GenConfigRepository genConfigRepository; @Override public GenConfig find(String tableName) { GenConfig genConfig = genConfigRepository.findByTableName(tableName); if(genConfig == null){ return new GenConfig(tableName); } return genConfig; } @Override public GenConfig update(String tableName, GenConfig genConfig) { String separator = File.separator; String[] paths; String symbol = "\\"; if (symbol.equals(separator)) { paths = genConfig.getPath().split("\\\\"); } else { paths = genConfig.getPath().split(File.separator); } StringBuilder api = new StringBuilder(); for (String path : paths) { api.append(path); api.append(separator); if ("src".equals(path)) { api.append("api"); break; } } genConfig.setApiPath(api.toString()); return genConfigRepository.save(genConfig); } } ================================================ FILE: eladmin-generator/src/main/java/me/zhengjie/service/impl/GeneratorServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.impl; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ZipUtil; import lombok.RequiredArgsConstructor; import me.zhengjie.domain.GenConfig; import me.zhengjie.domain.ColumnInfo; import me.zhengjie.domain.vo.TableInfo; import me.zhengjie.exception.BadRequestException; import me.zhengjie.repository.ColumnInfoRepository; import me.zhengjie.service.GeneratorService; import me.zhengjie.utils.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * @author Zheng Jie * @date 2019-01-02 */ @Service @RequiredArgsConstructor @SuppressWarnings({"unchecked","all"}) public class GeneratorServiceImpl implements GeneratorService { private static final Logger log = LoggerFactory.getLogger(GeneratorServiceImpl.class); @PersistenceContext private EntityManager em; private final ColumnInfoRepository columnInfoRepository; private final String CONFIG_MESSAGE = "请先配置生成器"; @Override public Object getTables() { // 使用预编译防止sql注入 String sql = "select table_name ,create_time , engine, table_collation, table_comment from information_schema.tables " + "where table_schema = (select database()) " + "order by create_time desc"; Query query = em.createNativeQuery(sql); return query.getResultList(); } @Override public PageResult getTables(String name, int[] startEnd) { // 使用预编译防止sql注入 String sql = "select table_name ,create_time , engine, table_collation, table_comment from information_schema.tables " + "where table_schema = (select database()) " + "and table_name like :table order by create_time desc"; Query query = em.createNativeQuery(sql); query.setFirstResult(startEnd[0]); query.setMaxResults(startEnd[1] - startEnd[0]); query.setParameter("table", StringUtils.isNotBlank(name) ? ("%" + name + "%") : "%%"); List result = query.getResultList(); List tableInfos = new ArrayList<>(); for (Object obj : result) { Object[] arr = (Object[]) obj; tableInfos.add(new TableInfo(arr[0], arr[1], arr[2], arr[3], ObjectUtil.isNotEmpty(arr[4]) ? arr[4] : "-")); } String countSql = "select count(1) from information_schema.tables " + "where table_schema = (select database()) and table_name like :table"; Query queryCount = em.createNativeQuery(countSql); queryCount.setParameter("table", StringUtils.isNotBlank(name) ? ("%" + name + "%") : "%%"); BigInteger totalElements = (BigInteger) queryCount.getSingleResult(); return PageUtil.toPage(tableInfos, totalElements.longValue()); } @Override public List getColumns(String tableName) { List columnInfos = columnInfoRepository.findByTableNameOrderByIdAsc(tableName); if (CollectionUtil.isNotEmpty(columnInfos)) { return columnInfos; } else { columnInfos = query(tableName); return columnInfoRepository.saveAll(columnInfos); } } @Override public List query(String tableName) { // 使用预编译防止sql注入 String sql = "select column_name, is_nullable, data_type, column_comment, column_key, extra from information_schema.columns " + "where table_name = ? and table_schema = (select database()) order by ordinal_position"; Query query = em.createNativeQuery(sql); query.setParameter(1, tableName); List result = query.getResultList(); List columnInfos = new ArrayList<>(); for (Object obj : result) { Object[] arr = (Object[]) obj; columnInfos.add( new ColumnInfo( tableName, arr[0].toString(), "NO".equals(arr[1]), arr[2].toString(), ObjectUtil.isNotNull(arr[3]) ? arr[3].toString() : null, ObjectUtil.isNotNull(arr[4]) ? arr[4].toString() : null, ObjectUtil.isNotNull(arr[5]) ? arr[5].toString() : null) ); } return columnInfos; } @Override public void sync(List columnInfos, List columnInfoList) { // 第一种情况,数据库类字段改变或者新增字段 for (ColumnInfo columnInfo : columnInfoList) { // 根据字段名称查找 List columns = columnInfos.stream().filter(c -> c.getColumnName().equals(columnInfo.getColumnName())).collect(Collectors.toList()); // 如果能找到,就修改部分可能被字段 if (CollectionUtil.isNotEmpty(columns)) { ColumnInfo column = columns.get(0); column.setColumnType(columnInfo.getColumnType()); column.setExtra(columnInfo.getExtra()); column.setKeyType(columnInfo.getKeyType()); if (StringUtils.isBlank(column.getRemark())) { column.setRemark(columnInfo.getRemark()); } columnInfoRepository.save(column); } else { // 如果找不到,则保存新字段信息 columnInfoRepository.save(columnInfo); } } // 第二种情况,数据库字段删除了 for (ColumnInfo columnInfo : columnInfos) { // 根据字段名称查找 List columns = columnInfoList.stream().filter(c -> c.getColumnName().equals(columnInfo.getColumnName())).collect(Collectors.toList()); // 如果找不到,就代表字段被删除了,则需要删除该字段 if (CollectionUtil.isEmpty(columns)) { columnInfoRepository.delete(columnInfo); } } } @Override public void save(List columnInfos) { columnInfoRepository.saveAll(columnInfos); } @Override public void generator(GenConfig genConfig, List columns) { if (genConfig.getId() == null) { throw new BadRequestException(CONFIG_MESSAGE); } try { GenUtil.generatorCode(columns, genConfig); } catch (IOException e) { log.error(e.getMessage(), e); throw new BadRequestException("生成失败,请手动处理已生成的文件"); } } @Override public ResponseEntity preview(GenConfig genConfig, List columns) { if (genConfig.getId() == null) { throw new BadRequestException(CONFIG_MESSAGE); } List> genList = GenUtil.preview(columns, genConfig); return new ResponseEntity<>(genList, HttpStatus.OK); } @Override public void download(GenConfig genConfig, List columns, HttpServletRequest request, HttpServletResponse response) { if (genConfig.getId() == null) { throw new BadRequestException(CONFIG_MESSAGE); } try { File file = new File(GenUtil.download(columns, genConfig)); String zipPath = file.getPath() + ".zip"; ZipUtil.zip(file.getPath(), zipPath); FileUtil.downloadFile(request, response, new File(zipPath), true); } catch (IOException e) { throw new BadRequestException("打包失败"); } } } ================================================ FILE: eladmin-generator/src/main/java/me/zhengjie/utils/ColUtil.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import org.apache.commons.configuration.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * sql字段转java * * @author Zheng Jie * @date 2019-01-03 */ public class ColUtil { private static final Logger log = LoggerFactory.getLogger(ColUtil.class); /** * 转换mysql数据类型为java数据类型 * * @param type 数据库字段类型 * @return String */ static String cloToJava(String type) { Configuration config = getConfig(); assert config != null; return config.getString(type, "unknowType"); } /** * 获取配置信息 */ public static PropertiesConfiguration getConfig() { try { return new PropertiesConfiguration("gen.properties"); } catch (ConfigurationException e) { log.error(e.getMessage(), e); } return null; } } ================================================ FILE: eladmin-generator/src/main/java/me/zhengjie/utils/GenUtil.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.template.*; import lombok.extern.slf4j.Slf4j; import me.zhengjie.domain.GenConfig; import me.zhengjie.domain.ColumnInfo; import org.springframework.util.ObjectUtils; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.time.LocalDate; import java.util.*; import static me.zhengjie.utils.FileUtil.SYS_TEM_DIR; /** * 代码生成 * * @author Zheng Jie * @date 2019-01-02 */ @Slf4j @SuppressWarnings({"unchecked", "all"}) public class GenUtil { private static final String TIMESTAMP = "Timestamp"; private static final String BIGDECIMAL = "BigDecimal"; public static final String PK = "PRI"; public static final String EXTRA = "auto_increment"; /** * 获取后端代码模板名称 * * @return List */ private static List getAdminTemplateNames() { List templateNames = new ArrayList<>(); templateNames.add("Entity"); templateNames.add("Dto"); templateNames.add("Mapper"); templateNames.add("Controller"); templateNames.add("QueryCriteria"); templateNames.add("Service"); templateNames.add("ServiceImpl"); templateNames.add("Repository"); return templateNames; } /** * 获取前端代码模板名称 * * @return List */ private static List getFrontTemplateNames() { List templateNames = new ArrayList<>(); templateNames.add("index"); templateNames.add("api"); return templateNames; } public static List> preview(List columns, GenConfig genConfig) { Map genMap = getGenMap(columns, genConfig); List> genList = new ArrayList<>(); // 获取后端模版 List templates = getAdminTemplateNames(); TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH)); for (String templateName : templates) { Map map = new HashMap<>(1); Template template = engine.getTemplate("admin/" + templateName + ".ftl"); map.put("content", template.render(genMap)); map.put("name", templateName); genList.add(map); } // 获取前端模版 templates = getFrontTemplateNames(); for (String templateName : templates) { Map map = new HashMap<>(1); Template template = engine.getTemplate("front/" + templateName + ".ftl"); map.put(templateName, template.render(genMap)); map.put("content", template.render(genMap)); map.put("name", templateName); genList.add(map); } return genList; } public static String download(List columns, GenConfig genConfig) throws IOException { // 拼接的路径:/tmpeladmin-gen-temp/,这个路径在Linux下需要root用户才有权限创建,非root用户会权限错误而失败,更改为: /tmp/eladmin-gen-temp/ // String tempPath =SYS_TEM_DIR + "eladmin-gen-temp" + File.separator + genConfig.getTableName() + File.separator; String tempPath = SYS_TEM_DIR + "eladmin-gen-temp" + File.separator + genConfig.getTableName() + File.separator; Map genMap = getGenMap(columns, genConfig); TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH)); // 生成后端代码 List templates = getAdminTemplateNames(); for (String templateName : templates) { Template template = engine.getTemplate("admin/" + templateName + ".ftl"); String filePath = getAdminFilePath(templateName, genConfig, genMap.get("className").toString(), tempPath + "eladmin" + File.separator); assert filePath != null; File file = new File(filePath); // 如果非覆盖生成 if (!genConfig.getCover() && FileUtil.exist(file)) { continue; } // 生成代码 genFile(file, template, genMap); } // 生成前端代码 templates = getFrontTemplateNames(); for (String templateName : templates) { Template template = engine.getTemplate("front/" + templateName + ".ftl"); String path = tempPath + "eladmin-web" + File.separator; String apiPath = path + "src" + File.separator + "api" + File.separator; String srcPath = path + "src" + File.separator + "views" + File.separator + genMap.get("changeClassName").toString() + File.separator; String filePath = getFrontFilePath(templateName, apiPath, srcPath, genMap.get("changeClassName").toString()); assert filePath != null; File file = new File(filePath); // 如果非覆盖生成 if (!genConfig.getCover() && FileUtil.exist(file)) { continue; } // 生成代码 genFile(file, template, genMap); } return tempPath; } public static void generatorCode(List columnInfos, GenConfig genConfig) throws IOException { Map genMap = getGenMap(columnInfos, genConfig); TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH)); // 生成后端代码 List templates = getAdminTemplateNames(); for (String templateName : templates) { Template template = engine.getTemplate("admin/" + templateName + ".ftl"); String rootPath = System.getProperty("user.dir"); String filePath = getAdminFilePath(templateName, genConfig, genMap.get("className").toString(), rootPath); assert filePath != null; File file = new File(filePath); // 如果非覆盖生成 if (!genConfig.getCover() && FileUtil.exist(file)) { continue; } // 生成代码 genFile(file, template, genMap); } // 生成前端代码 templates = getFrontTemplateNames(); for (String templateName : templates) { Template template = engine.getTemplate("front/" + templateName + ".ftl"); String filePath = getFrontFilePath(templateName, genConfig.getApiPath(), genConfig.getPath(), genMap.get("changeClassName").toString()); assert filePath != null; File file = new File(filePath); // 如果非覆盖生成 if (!genConfig.getCover() && FileUtil.exist(file)) { continue; } // 生成代码 genFile(file, template, genMap); } } // 获取模版数据 private static Map getGenMap(List columnInfos, GenConfig genConfig) { // 存储模版字段数据 Map genMap = new HashMap<>(16); // 接口别名 genMap.put("apiAlias", genConfig.getApiAlias()); // 包名称 genMap.put("package", genConfig.getPack()); // 模块名称 genMap.put("moduleName", genConfig.getModuleName()); // 作者 genMap.put("author", genConfig.getAuthor()); // 创建日期 genMap.put("date", LocalDate.now().toString()); // 表名 genMap.put("tableName", genConfig.getTableName()); // 大写开头的类名 String className = StringUtils.toCapitalizeCamelCase(genConfig.getTableName()); // 小写开头的类名 String changeClassName = StringUtils.toCamelCase(genConfig.getTableName()); // 判断是否去除表前缀 if (StringUtils.isNotEmpty(genConfig.getPrefix())) { className = StringUtils.toCapitalizeCamelCase(StrUtil.removePrefix(genConfig.getTableName(), genConfig.getPrefix())); changeClassName = StringUtils.toCamelCase(StrUtil.removePrefix(genConfig.getTableName(), genConfig.getPrefix())); changeClassName = StringUtils.uncapitalize(changeClassName); } // 保存类名 genMap.put("className", className); // 保存小写开头的类名 genMap.put("changeClassName", changeClassName); // 存在 Timestamp 字段 genMap.put("hasTimestamp", false); // 查询类中存在 Timestamp 字段 genMap.put("queryHasTimestamp", false); // 存在 BigDecimal 字段 genMap.put("hasBigDecimal", false); // 查询类中存在 BigDecimal 字段 genMap.put("queryHasBigDecimal", false); // 是否需要创建查询 genMap.put("hasQuery", false); // 自增主键 genMap.put("auto", false); // 存在字典 genMap.put("hasDict", false); // 存在日期注解 genMap.put("hasDateAnnotation", false); // 保存字段信息 List> columns = new ArrayList<>(); // 保存查询字段的信息 List> queryColumns = new ArrayList<>(); // 存储字典信息 List dicts = new ArrayList<>(); // 存储 between 信息 List> betweens = new ArrayList<>(); // 存储不为空的字段信息 List> isNotNullColumns = new ArrayList<>(); for (ColumnInfo column : columnInfos) { Map listMap = new HashMap<>(16); // 字段描述 listMap.put("remark", column.getRemark()); // 字段类型 listMap.put("columnKey", column.getKeyType()); // 主键类型 String colType = ColUtil.cloToJava(column.getColumnType()); // 小写开头的字段名 String changeColumnName = StringUtils.toCamelCase(column.getColumnName()); // 大写开头的字段名 String capitalColumnName = StringUtils.toCapitalizeCamelCase(column.getColumnName()); if (PK.equals(column.getKeyType())) { // 存储主键类型 genMap.put("pkColumnType", colType); // 存储小写开头的字段名 genMap.put("pkChangeColName", changeColumnName); // 存储大写开头的字段名 genMap.put("pkCapitalColName", capitalColumnName); } // 是否存在 Timestamp 类型的字段 if (TIMESTAMP.equals(colType)) { genMap.put("hasTimestamp", true); } // 是否存在 BigDecimal 类型的字段 if (BIGDECIMAL.equals(colType)) { genMap.put("hasBigDecimal", true); } // 主键是否自增 if (EXTRA.equals(column.getExtra())) { genMap.put("auto", true); } // 主键存在字典 if (StringUtils.isNotBlank(column.getDictName())) { genMap.put("hasDict", true); if(!dicts.contains(column.getDictName())) dicts.add(column.getDictName()); } // 存储字段类型 listMap.put("columnType", colType); // 存储字原始段名称 listMap.put("columnName", column.getColumnName()); // 不为空 listMap.put("istNotNull", column.getNotNull()); // 字段列表显示 listMap.put("columnShow", column.getListShow()); // 表单显示 listMap.put("formShow", column.getFormShow()); // 表单组件类型 listMap.put("formType", StringUtils.isNotBlank(column.getFormType()) ? column.getFormType() : "Input"); // 小写开头的字段名称 listMap.put("changeColumnName", changeColumnName); //大写开头的字段名称 listMap.put("capitalColumnName", capitalColumnName); // 字典名称 listMap.put("dictName", column.getDictName()); // 日期注解 listMap.put("dateAnnotation", column.getDateAnnotation()); if (StringUtils.isNotBlank(column.getDateAnnotation())) { genMap.put("hasDateAnnotation", true); } // 添加非空字段信息 if (column.getNotNull()) { isNotNullColumns.add(listMap); } // 判断是否有查询,如有则把查询的字段set进columnQuery if (!StringUtils.isBlank(column.getQueryType())) { // 查询类型 listMap.put("queryType", column.getQueryType()); // 是否存在查询 genMap.put("hasQuery", true); if (TIMESTAMP.equals(colType)) { // 查询中存储 Timestamp 类型 genMap.put("queryHasTimestamp", true); } if (BIGDECIMAL.equals(colType)) { // 查询中存储 BigDecimal 类型 genMap.put("queryHasBigDecimal", true); } if ("between".equalsIgnoreCase(column.getQueryType())) { betweens.add(listMap); } else { // 添加到查询列表中 queryColumns.add(listMap); } } // 添加到字段列表中 columns.add(listMap); } // 保存字段列表 genMap.put("columns", columns); // 保存查询列表 genMap.put("queryColumns", queryColumns); // 保存字段列表 genMap.put("dicts", dicts); // 保存查询列表 genMap.put("betweens", betweens); // 保存非空字段信息 genMap.put("isNotNullColumns", isNotNullColumns); return genMap; } /** * 定义后端文件路径以及名称 */ private static String getAdminFilePath(String templateName, GenConfig genConfig, String className, String rootPath) { String projectPath = rootPath + File.separator + genConfig.getModuleName(); String packagePath = projectPath + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator; if (!ObjectUtils.isEmpty(genConfig.getPack())) { packagePath += genConfig.getPack().replace(".", File.separator) + File.separator; } if ("Entity".equals(templateName)) { return packagePath + "domain" + File.separator + className + ".java"; } if ("Controller".equals(templateName)) { return packagePath + "rest" + File.separator + className + "Controller.java"; } if ("Service".equals(templateName)) { return packagePath + "service" + File.separator + className + "Service.java"; } if ("ServiceImpl".equals(templateName)) { return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java"; } if ("Dto".equals(templateName)) { return packagePath + "service" + File.separator + "dto" + File.separator + className + "Dto.java"; } if ("QueryCriteria".equals(templateName)) { return packagePath + "service" + File.separator + "dto" + File.separator + className + "QueryCriteria.java"; } if ("Mapper".equals(templateName)) { return packagePath + "service" + File.separator + "mapstruct" + File.separator + className + "Mapper.java"; } if ("Repository".equals(templateName)) { return packagePath + "repository" + File.separator + className + "Repository.java"; } return null; } /** * 定义前端文件路径以及名称 */ private static String getFrontFilePath(String templateName, String apiPath, String path, String apiName) { if ("api".equals(templateName)) { return apiPath + File.separator + apiName + ".js"; } if ("index".equals(templateName)) { return path + File.separator + "index.vue"; } return null; } private static void genFile(File file, Template template, Map map) throws IOException { // 生成目标文件 Writer writer = null; try { FileUtil.touch(file); writer = new FileWriter(file); template.render(map, writer); } catch (TemplateException | IOException e) { throw new RuntimeException(e); } finally { assert writer != null; writer.close(); } } } ================================================ FILE: eladmin-generator/src/main/resources/gen.properties ================================================ # Database type to Java type tinyint=Integer smallint=Integer mediumint=Integer int=Integer integer=Integer bigint=Long float=Float double=Double decimal=BigDecimal bit=Boolean char=String varchar=String tinytext=String text=String mediumtext=String longtext=String date=Timestamp datetime=Timestamp timestamp=Timestamp ================================================ FILE: eladmin-generator/src/main/resources/template/admin/Controller.ftl ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ${package}.rest; import me.zhengjie.annotation.Log; import ${package}.domain.${className}; import ${package}.service.${className}Service; import ${package}.service.dto.${className}QueryCriteria; import org.springframework.data.domain.Pageable; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import io.swagger.annotations.*; import java.io.IOException; import javax.servlet.http.HttpServletResponse; import me.zhengjie.utils.PageResult; import ${package}.service.dto.${className}Dto; /** * @website https://eladmin.vip * @author ${author} * @date ${date} **/ @RestController @RequiredArgsConstructor @Api(tags = "${apiAlias}") @RequestMapping("/api/${changeClassName}") public class ${className}Controller { private final ${className}Service ${changeClassName}Service; @ApiOperation("导出数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('${changeClassName}:list')") public void export${className}(HttpServletResponse response, ${className}QueryCriteria criteria) throws IOException { ${changeClassName}Service.download(${changeClassName}Service.queryAll(criteria), response); } @GetMapping @ApiOperation("查询${apiAlias}") @PreAuthorize("@el.check('${changeClassName}:list')") public ResponseEntity> query${className}(${className}QueryCriteria criteria, Pageable pageable){ return new ResponseEntity<>(${changeClassName}Service.queryAll(criteria,pageable),HttpStatus.OK); } @PostMapping @Log("新增${apiAlias}") @ApiOperation("新增${apiAlias}") @PreAuthorize("@el.check('${changeClassName}:add')") public ResponseEntity create${className}(@Validated @RequestBody ${className} resources){ ${changeClassName}Service.create(resources); return new ResponseEntity<>(HttpStatus.CREATED); } @PutMapping @Log("修改${apiAlias}") @ApiOperation("修改${apiAlias}") @PreAuthorize("@el.check('${changeClassName}:edit')") public ResponseEntity update${className}(@Validated @RequestBody ${className} resources){ ${changeClassName}Service.update(resources); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @DeleteMapping @Log("删除${apiAlias}") @ApiOperation("删除${apiAlias}") @PreAuthorize("@el.check('${changeClassName}:del')") public ResponseEntity delete${className}(@ApiParam(value = "传ID数组[]") @RequestBody ${pkColumnType}[] ids) { ${changeClassName}Service.deleteAll(ids); return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-generator/src/main/resources/template/admin/Dto.ftl ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ${package}.service.dto; import lombok.Data; <#if hasTimestamp> import java.sql.Timestamp; <#if hasBigDecimal> import java.math.BigDecimal; import java.io.Serializable; <#if !auto && pkColumnType = 'Long'> import com.alibaba.fastjson2.annotation.JSONField; import com.alibaba.fastjson2.serializer.ToStringSerializer; import io.swagger.annotations.ApiModelProperty; /** * @website https://eladmin.vip * @description / * @author ${author} * @date ${date} **/ @Data public class ${className}Dto implements Serializable { <#if columns??> <#list columns as column> <#if column.remark != ''> @ApiModelProperty(value = "${column.remark}") <#else> @ApiModelProperty(value = "${column.changeColumnName}") <#if column.columnKey = 'PRI'> <#if !auto && pkColumnType = 'Long'> /** 防止精度丢失 */ @JSONField(serializeUsing = ToStringSerializer.class) private ${column.columnType} ${column.changeColumnName}; } ================================================ FILE: eladmin-generator/src/main/resources/template/admin/Entity.ftl ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ${package}.domain; import lombok.Data; import cn.hutool.core.bean.BeanUtil; import io.swagger.annotations.ApiModelProperty; import cn.hutool.core.bean.copier.CopyOptions; import javax.persistence.*; <#if isNotNullColumns??> import javax.validation.constraints.*; <#if hasDateAnnotation> import javax.persistence.Entity; import javax.persistence.Table; import org.hibernate.annotations.*; <#if hasTimestamp> import java.sql.Timestamp; <#if hasBigDecimal> import java.math.BigDecimal; <#assign notBlankUsed = false> <#assign notNullUsed = false> <#if columns??> <#list columns as column> <#if column.istNotNull && column.columnKey != 'PRI'> <#if column.columnType = 'String'> <#assign notBlankUsed = true> <#else> <#assign notNullUsed = true> <#if notBlankUsed> import javax.validation.constraints.NotBlank; <#if notNullUsed> import javax.validation.constraints.NotNull; import java.io.Serializable; /** * @website https://eladmin.vip * @description / * @author ${author} * @date ${date} **/ @Entity @Data @Table(name="${tableName}") public class ${className} implements Serializable { <#if columns??> <#list columns as column> <#if column.columnKey = 'PRI'> @Id <#if auto> @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "`${column.columnName}`"<#if column.columnKey = 'UNI'>,unique = true<#if column.istNotNull && column.columnKey != 'PRI'>,nullable = false) <#if column.istNotNull && column.columnKey != 'PRI'> <#if column.columnType = 'String'> @NotBlank <#else> @NotNull <#if (column.dateAnnotation)?? && column.dateAnnotation != ''> <#if column.dateAnnotation = 'CreationTimestamp'> @CreationTimestamp <#else> @UpdateTimestamp <#if column.remark != ''> @ApiModelProperty(value = "${column.remark}") <#else> @ApiModelProperty(value = "${column.changeColumnName}") private ${column.columnType} ${column.changeColumnName}; public void copy(${className} source){ BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); } } ================================================ FILE: eladmin-generator/src/main/resources/template/admin/Mapper.ftl ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ${package}.service.mapstruct; import me.zhengjie.base.BaseMapper; import ${package}.domain.${className}; import ${package}.service.dto.${className}Dto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @website https://eladmin.vip * @author ${author} * @date ${date} **/ @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface ${className}Mapper extends BaseMapper<${className}Dto, ${className}> { } ================================================ FILE: eladmin-generator/src/main/resources/template/admin/QueryCriteria.ftl ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ${package}.service.dto; import lombok.Data; <#if queryHasTimestamp> import java.sql.Timestamp; <#if queryHasBigDecimal> import java.math.BigDecimal; <#if betweens??> import java.util.List; <#if queryColumns??> import me.zhengjie.annotation.Query; import io.swagger.annotations.ApiModelProperty; /** * @website https://eladmin.vip * @author ${author} * @date ${date} **/ @Data public class ${className}QueryCriteria{ <#if queryColumns??> <#list queryColumns as column> <#if column.queryType = '='> /** 精确 */ @Query <#if column.remark != ''> @ApiModelProperty(value = "${column.remark}") <#else> @ApiModelProperty(value = "${column.changeColumnName}") private ${column.columnType} ${column.changeColumnName}; <#if column.queryType = 'Like'> /** 模糊 */ @Query(type = Query.Type.INNER_LIKE) <#if column.remark != ''> @ApiModelProperty(value = "${column.remark}") <#else> @ApiModelProperty(value = "${column.changeColumnName}") private ${column.columnType} ${column.changeColumnName}; <#if column.queryType = '!='> /** 不等于 */ @Query(type = Query.Type.NOT_EQUAL) <#if column.remark != ''> @ApiModelProperty(value = "${column.remark}") <#else> @ApiModelProperty(value = "${column.changeColumnName}") private ${column.columnType} ${column.changeColumnName}; <#if column.queryType = 'NotNull'> /** 不为空 */ @Query(type = Query.Type.NOT_NULL) <#if column.remark != ''> @ApiModelProperty(value = "${column.remark}") <#else> @ApiModelProperty(value = "${column.changeColumnName}") private ${column.columnType} ${column.changeColumnName}; <#if column.queryType = '>='> /** 大于等于 */ @Query(type = Query.Type.GREATER_THAN) <#if column.remark != ''> @ApiModelProperty(value = "${column.remark}") <#else> @ApiModelProperty(value = "${column.changeColumnName}") private ${column.columnType} ${column.changeColumnName}; <#if column.queryType = '<='> /** 小于等于 */ @Query(type = Query.Type.LESS_THAN) <#if column.remark != ''> @ApiModelProperty(value = "${column.remark}") <#else> @ApiModelProperty(value = "${column.changeColumnName}") private ${column.columnType} ${column.changeColumnName}; <#if betweens??> <#list betweens as column> /** BETWEEN */ @Query(type = Query.Type.BETWEEN) private List<${column.columnType}> ${column.changeColumnName}; } ================================================ FILE: eladmin-generator/src/main/resources/template/admin/Repository.ftl ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ${package}.repository; import ${package}.domain.${className}; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * @website https://eladmin.vip * @author ${author} * @date ${date} **/ public interface ${className}Repository extends JpaRepository<${className}, ${pkColumnType}>, JpaSpecificationExecutor<${className}> { <#if columns??> <#list columns as column> <#if column.columnKey = 'UNI'> /** * 根据 ${column.capitalColumnName} 查询 * @param ${column.columnName} / * @return / */ ${className} findBy${column.capitalColumnName}(${column.columnType} ${column.columnName}); } ================================================ FILE: eladmin-generator/src/main/resources/template/admin/Service.ftl ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ${package}.service; import ${package}.domain.${className}; import ${package}.service.dto.${className}Dto; import ${package}.service.dto.${className}QueryCriteria; import org.springframework.data.domain.Pageable; import java.util.Map; import java.util.List; import java.io.IOException; import javax.servlet.http.HttpServletResponse; import me.zhengjie.utils.PageResult; /** * @website https://eladmin.vip * @description 服务接口 * @author ${author} * @date ${date} **/ public interface ${className}Service { /** * 查询数据分页 * @param criteria 条件 * @param pageable 分页参数 * @return Map */ PageResult<${className}Dto> queryAll(${className}QueryCriteria criteria, Pageable pageable); /** * 查询所有数据不分页 * @param criteria 条件参数 * @return List<${className}Dto> */ List<${className}Dto> queryAll(${className}QueryCriteria criteria); /** * 根据ID查询 * @param ${pkChangeColName} ID * @return ${className}Dto */ ${className}Dto findById(${pkColumnType} ${pkChangeColName}); /** * 创建 * @param resources / */ void create(${className} resources); /** * 编辑 * @param resources / */ void update(${className} resources); /** * 多选删除 * @param ids / */ void deleteAll(${pkColumnType}[] ids); /** * 导出数据 * @param all 待导出的数据 * @param response / * @throws IOException / */ void download(List<${className}Dto> all, HttpServletResponse response) throws IOException; } ================================================ FILE: eladmin-generator/src/main/resources/template/admin/ServiceImpl.ftl ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ${package}.service.impl; import ${package}.domain.${className}; <#if columns??> <#list columns as column> <#if column.columnKey = 'UNI'> <#if column_index = 1> import me.zhengjie.exception.EntityExistException; import me.zhengjie.utils.ValidationUtil; import me.zhengjie.utils.FileUtil; import lombok.RequiredArgsConstructor; import ${package}.repository.${className}Repository; import ${package}.service.${className}Service; import ${package}.service.dto.${className}Dto; import ${package}.service.dto.${className}QueryCriteria; import ${package}.service.mapstruct.${className}Mapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; <#if !auto && pkColumnType = 'Long'> import cn.hutool.core.lang.Snowflake; import cn.hutool.core.util.IdUtil; <#if !auto && pkColumnType = 'String'> import cn.hutool.core.util.IdUtil; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import me.zhengjie.utils.PageUtil; import me.zhengjie.utils.QueryHelp; import java.util.List; import java.util.Map; import java.io.IOException; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.LinkedHashMap; import me.zhengjie.utils.PageResult; /** * @website https://eladmin.vip * @description 服务实现 * @author ${author} * @date ${date} **/ @Service @RequiredArgsConstructor public class ${className}ServiceImpl implements ${className}Service { private final ${className}Repository ${changeClassName}Repository; private final ${className}Mapper ${changeClassName}Mapper; @Override public PageResult<${className}Dto> queryAll(${className}QueryCriteria criteria, Pageable pageable){ Page<${className}> page = ${changeClassName}Repository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); return PageUtil.toPage(page.map(${changeClassName}Mapper::toDto)); } @Override public List<${className}Dto> queryAll(${className}QueryCriteria criteria){ return ${changeClassName}Mapper.toDto(${changeClassName}Repository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); } @Override @Transactional public ${className}Dto findById(${pkColumnType} ${pkChangeColName}) { ${className} ${changeClassName} = ${changeClassName}Repository.findById(${pkChangeColName}).orElseGet(${className}::new); ValidationUtil.isNull(${changeClassName}.get${pkCapitalColName}(),"${className}","${pkChangeColName}",${pkChangeColName}); return ${changeClassName}Mapper.toDto(${changeClassName}); } @Override @Transactional(rollbackFor = Exception.class) public void create(${className} resources) { <#if !auto && pkColumnType = 'Long'> Snowflake snowflake = IdUtil.createSnowflake(1, 1); resources.set${pkCapitalColName}(snowflake.nextId()); <#if !auto && pkColumnType = 'String'> resources.set${pkCapitalColName}(IdUtil.simpleUUID()); <#if columns??> <#list columns as column> <#if column.columnKey = 'UNI'> if(${changeClassName}Repository.findBy${column.capitalColumnName}(resources.get${column.capitalColumnName}()) != null){ throw new EntityExistException(${className}.class,"${column.columnName}",resources.get${column.capitalColumnName}()); } ${changeClassName}Repository.save(resources); } @Override @Transactional(rollbackFor = Exception.class) public void update(${className} resources) { ${className} ${changeClassName} = ${changeClassName}Repository.findById(resources.get${pkCapitalColName}()).orElseGet(${className}::new); ValidationUtil.isNull( ${changeClassName}.get${pkCapitalColName}(),"${className}","id",resources.get${pkCapitalColName}()); <#if columns??> <#list columns as column> <#if column.columnKey = 'UNI'> <#if column_index = 1> ${className} ${changeClassName}1 = null; ${changeClassName}1 = ${changeClassName}Repository.findBy${column.capitalColumnName}(resources.get${column.capitalColumnName}()); if(${changeClassName}1 != null && !${changeClassName}1.get${pkCapitalColName}().equals(${changeClassName}.get${pkCapitalColName}())){ throw new EntityExistException(${className}.class,"${column.columnName}",resources.get${column.capitalColumnName}()); } ${changeClassName}.copy(resources); ${changeClassName}Repository.save(${changeClassName}); } @Override public void deleteAll(${pkColumnType}[] ids) { for (${pkColumnType} ${pkChangeColName} : ids) { ${changeClassName}Repository.deleteById(${pkChangeColName}); } } @Override public void download(List<${className}Dto> all, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (${className}Dto ${changeClassName} : all) { Map map = new LinkedHashMap<>(); <#list columns as column> <#if column.columnKey != 'PRI'> <#if column.remark != ''> map.put("${column.remark}", ${changeClassName}.get${column.capitalColumnName}()); <#else> map.put(" ${column.changeColumnName}", ${changeClassName}.get${column.capitalColumnName}()); list.add(map); } FileUtil.downloadExcel(list, response); } } ================================================ FILE: eladmin-generator/src/main/resources/template/front/api.ftl ================================================ import request from '@/utils/request' export function add(data) { return request({ url: 'api/${changeClassName}', method: 'post', data }) } export function del(ids) { return request({ url: 'api/${changeClassName}/', method: 'delete', data: ids }) } export function edit(data) { return request({ url: 'api/${changeClassName}', method: 'put', data }) } export default { add, edit, del } ================================================ FILE: eladmin-generator/src/main/resources/template/front/index.ftl ================================================ <#--noinspection ALL--> ================================================ FILE: eladmin-logging/pom.xml ================================================ eladmin me.zhengjie 2.7 4.0.0 eladmin-logging 日志模块 me.zhengjie eladmin-common 2.7 ================================================ FILE: eladmin-logging/src/main/java/me/zhengjie/annotation/Log.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author Zheng Jie * @date 2018-11-24 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Log { String value() default ""; } ================================================ FILE: eladmin-logging/src/main/java/me/zhengjie/aspect/LogAspect.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.aspect; import lombok.extern.slf4j.Slf4j; import me.zhengjie.domain.SysLog; import me.zhengjie.service.SysLogService; import me.zhengjie.utils.RequestHolder; import me.zhengjie.utils.SecurityUtils; import me.zhengjie.utils.StringUtils; import me.zhengjie.utils.ThrowableUtil; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; /** * @author Zheng Jie * @date 2018-11-24 */ @Component @Aspect @Slf4j public class LogAspect { private final SysLogService sysLogService; ThreadLocal currentTime = new ThreadLocal<>(); public LogAspect(SysLogService sysLogService) { this.sysLogService = sysLogService; } /** * 配置切入点 */ @Pointcut("@annotation(me.zhengjie.annotation.Log)") public void logPointcut() { // 该方法无方法体,主要为了让同类中其他方法使用此切入点 } /** * 配置环绕通知,使用在方法logPointcut()上注册的切入点 * * @param joinPoint join point for advice */ @Around("logPointcut()") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { Object result; currentTime.set(System.currentTimeMillis()); result = joinPoint.proceed(); SysLog sysLog = new SysLog("INFO",System.currentTimeMillis() - currentTime.get()); currentTime.remove(); HttpServletRequest request = RequestHolder.getHttpServletRequest(); sysLogService.save(getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request),joinPoint, sysLog); return result; } /** * 配置异常通知 * * @param joinPoint join point for advice * @param e exception */ @AfterThrowing(pointcut = "logPointcut()", throwing = "e") public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { SysLog sysLog = new SysLog("ERROR",System.currentTimeMillis() - currentTime.get()); currentTime.remove(); sysLog.setExceptionDetail(ThrowableUtil.getStackTrace(e).getBytes()); HttpServletRequest request = RequestHolder.getHttpServletRequest(); sysLogService.save(getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request), (ProceedingJoinPoint)joinPoint, sysLog); } /** * 获取用户名 * @return / */ public String getUsername() { try { return SecurityUtils.getCurrentUsername(); }catch (Exception e){ return ""; } } } ================================================ FILE: eladmin-logging/src/main/java/me/zhengjie/domain/SysLog.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.domain; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.hibernate.annotations.CreationTimestamp; import javax.persistence.*; import java.io.Serializable; import java.sql.Timestamp; /** * @author Zheng Jie * @date 2018-11-24 */ @Entity @Getter @Setter @Table(name = "sys_log") @NoArgsConstructor public class SysLog implements Serializable { @Id @Column(name = "log_id") @ApiModelProperty(value = "ID") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ApiModelProperty(value = "操作用户") private String username; @ApiModelProperty(value = "描述") private String description; @ApiModelProperty(value = "方法名") private String method; @ApiModelProperty(value = "参数") private String params; @ApiModelProperty(value = "日志类型") private String logType; @ApiModelProperty(value = "请求ip") private String requestIp; @ApiModelProperty(value = "地址") private String address; @ApiModelProperty(value = "浏览器") private String browser; @ApiModelProperty(value = "请求耗时") private Long time; @ApiModelProperty(value = "异常详细") private byte[] exceptionDetail; /** 创建日期 */ @CreationTimestamp @ApiModelProperty(value = "创建日期:yyyy-MM-dd HH:mm:ss") @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") private Timestamp createTime; public SysLog(String logType, Long time) { this.logType = logType; this.time = time; } } ================================================ FILE: eladmin-logging/src/main/java/me/zhengjie/repository/LogRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.repository; import me.zhengjie.domain.SysLog; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; /** * @author Zheng Jie * @date 2018-11-24 */ @Repository public interface LogRepository extends JpaRepository, JpaSpecificationExecutor { /** * 根据日志类型删除信息 * @param logType 日志类型 */ @Modifying @Query(value = "delete from sys_log where log_type = ?1", nativeQuery = true) void deleteByLogType(String logType); } ================================================ FILE: eladmin-logging/src/main/java/me/zhengjie/rest/SysLogController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.annotation.Log; import me.zhengjie.service.SysLogService; import me.zhengjie.service.dto.SysLogQueryCriteria; import me.zhengjie.service.dto.SysLogSmallDto; import me.zhengjie.utils.PageResult; import me.zhengjie.utils.SecurityUtils; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author Zheng Jie * @date 2018-11-24 */ @RestController @RequiredArgsConstructor @RequestMapping("/api/logs") @Api(tags = "系统:日志管理") public class SysLogController { private final SysLogService sysLogService; @Log("导出数据") @ApiOperation("导出数据") @GetMapping(value = "/download") @PreAuthorize("@el.check()") public void exportLog(HttpServletResponse response, SysLogQueryCriteria criteria) throws IOException { criteria.setLogType("INFO"); sysLogService.download(sysLogService.queryAll(criteria), response); } @Log("导出错误数据") @ApiOperation("导出错误数据") @GetMapping(value = "/error/download") @PreAuthorize("@el.check()") public void exportErrorLog(HttpServletResponse response, SysLogQueryCriteria criteria) throws IOException { criteria.setLogType("ERROR"); sysLogService.download(sysLogService.queryAll(criteria), response); } @GetMapping @ApiOperation("日志查询") @PreAuthorize("@el.check()") public ResponseEntity queryLog(SysLogQueryCriteria criteria, Pageable pageable){ criteria.setLogType("INFO"); return new ResponseEntity<>(sysLogService.queryAll(criteria,pageable), HttpStatus.OK); } @GetMapping(value = "/user") @ApiOperation("用户日志查询") public ResponseEntity> queryUserLog(SysLogQueryCriteria criteria, Pageable pageable){ criteria.setLogType("INFO"); criteria.setUsername(SecurityUtils.getCurrentUsername()); return new ResponseEntity<>(sysLogService.queryAllByUser(criteria,pageable), HttpStatus.OK); } @GetMapping(value = "/error") @ApiOperation("错误日志查询") @PreAuthorize("@el.check()") public ResponseEntity queryErrorLog(SysLogQueryCriteria criteria, Pageable pageable){ criteria.setLogType("ERROR"); return new ResponseEntity<>(sysLogService.queryAll(criteria,pageable), HttpStatus.OK); } @GetMapping(value = "/error/{id}") @ApiOperation("日志异常详情查询") @PreAuthorize("@el.check()") public ResponseEntity queryErrorLogDetail(@PathVariable Long id){ return new ResponseEntity<>(sysLogService.findByErrDetail(id), HttpStatus.OK); } @DeleteMapping(value = "/del/error") @Log("删除所有ERROR日志") @ApiOperation("删除所有ERROR日志") @PreAuthorize("@el.check()") public ResponseEntity delAllErrorLog(){ sysLogService.delAllByError(); return new ResponseEntity<>(HttpStatus.OK); } @DeleteMapping(value = "/del/info") @Log("删除所有INFO日志") @ApiOperation("删除所有INFO日志") @PreAuthorize("@el.check()") public ResponseEntity delAllInfoLog(){ sysLogService.delAllByInfo(); return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-logging/src/main/java/me/zhengjie/service/SysLogService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service; import me.zhengjie.domain.SysLog; import me.zhengjie.service.dto.SysLogQueryCriteria; import me.zhengjie.service.dto.SysLogSmallDto; import me.zhengjie.utils.PageResult; import org.aspectj.lang.ProceedingJoinPoint; import org.springframework.data.domain.Pageable; import org.springframework.scheduling.annotation.Async; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; /** * @author Zheng Jie * @date 2018-11-24 */ public interface SysLogService { /** * 分页查询 * @param criteria 查询条件 * @param pageable 分页参数 * @return / */ Object queryAll(SysLogQueryCriteria criteria, Pageable pageable); /** * 查询全部数据 * @param criteria 查询条件 * @return / */ List queryAll(SysLogQueryCriteria criteria); /** * 查询用户日志 * @param criteria 查询条件 * @param pageable 分页参数 * @return - */ PageResult queryAllByUser(SysLogQueryCriteria criteria, Pageable pageable); /** * 保存日志数据 * @param username 用户 * @param browser 浏览器 * @param ip 请求IP * @param joinPoint / * @param sysLog 日志实体 */ @Async void save(String username, String browser, String ip, ProceedingJoinPoint joinPoint, SysLog sysLog); /** * 查询异常详情 * @param id 日志ID * @return Object */ Object findByErrDetail(Long id); /** * 导出日志 * @param sysLogs 待导出的数据 * @param response / * @throws IOException / */ void download(List sysLogs, HttpServletResponse response) throws IOException; /** * 删除所有错误日志 */ void delAllByError(); /** * 删除所有INFO日志 */ void delAllByInfo(); } ================================================ FILE: eladmin-logging/src/main/java/me/zhengjie/service/dto/SysLogErrorDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; import java.sql.Timestamp; /** * @author Zheng Jie * @date 2019-5-22 */ @Data public class SysLogErrorDto implements Serializable { @ApiModelProperty(value = "ID") private Long id; @ApiModelProperty(value = "用户名") private String username; @ApiModelProperty(value = "描述") private String description; @ApiModelProperty(value = "方法") private String method; @ApiModelProperty(value = "参数") private String params; @ApiModelProperty(value = "浏览器") private String browser; @ApiModelProperty(value = "请求ip") private String requestIp; @ApiModelProperty(value = "地址") private String address; @ApiModelProperty(value = "创建时间") private Timestamp createTime; } ================================================ FILE: eladmin-logging/src/main/java/me/zhengjie/service/dto/SysLogQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import me.zhengjie.annotation.Query; import java.sql.Timestamp; import java.util.List; /** * 日志查询类 * @author Zheng Jie * @date 2019-6-4 09:23:07 */ @Data public class SysLogQueryCriteria { @ApiModelProperty(value = "模糊查询") @Query(blurry = "username,description,address,requestIp,method,params") private String blurry; @Query @ApiModelProperty(value = "用户名") private String username; @Query @ApiModelProperty(value = "日志类型") private String logType; @ApiModelProperty(value = "创建时间") @Query(type = Query.Type.BETWEEN) private List createTime; } ================================================ FILE: eladmin-logging/src/main/java/me/zhengjie/service/dto/SysLogSmallDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; import java.sql.Timestamp; /** * @author Zheng Jie * @date 2019-5-22 */ @Data public class SysLogSmallDto implements Serializable { @ApiModelProperty(value = "描述") private String description; @ApiModelProperty(value = "请求IP") private String requestIp; @ApiModelProperty(value = "耗时") private Long time; @ApiModelProperty(value = "地址") private String address; @ApiModelProperty(value = "浏览器") private String browser; @ApiModelProperty(value = "创建时间") private Timestamp createTime; } ================================================ FILE: eladmin-logging/src/main/java/me/zhengjie/service/impl/SysLogServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.impl; import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ObjectUtil; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import lombok.RequiredArgsConstructor; import me.zhengjie.domain.SysLog; import me.zhengjie.repository.LogRepository; import me.zhengjie.service.SysLogService; import me.zhengjie.service.dto.SysLogQueryCriteria; import me.zhengjie.service.dto.SysLogSmallDto; import me.zhengjie.service.mapstruct.LogErrorMapper; import me.zhengjie.service.mapstruct.LogSmallMapper; import me.zhengjie.utils.*; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.*; /** * @author Zheng Jie * @date 2018-11-24 */ @Service @RequiredArgsConstructor public class SysLogServiceImpl implements SysLogService { private final LogRepository logRepository; private final LogErrorMapper logErrorMapper; private final LogSmallMapper logSmallMapper; // 定义敏感字段常量数组 private static final String[] SENSITIVE_KEYS = {"password"}; @Override public Object queryAll(SysLogQueryCriteria criteria, Pageable pageable) { Page page = logRepository.findAll(((root, criteriaQuery, cb) -> QueryHelp.getPredicate(root, criteria, cb)), pageable); String status = "ERROR"; if (status.equals(criteria.getLogType())) { return PageUtil.toPage(page.map(logErrorMapper::toDto)); } return PageUtil.toPage(page); } @Override public List queryAll(SysLogQueryCriteria criteria) { return logRepository.findAll(((root, criteriaQuery, cb) -> QueryHelp.getPredicate(root, criteria, cb))); } @Override public PageResult queryAllByUser(SysLogQueryCriteria criteria, Pageable pageable) { Page page = logRepository.findAll(((root, criteriaQuery, cb) -> QueryHelp.getPredicate(root, criteria, cb)), pageable); return PageUtil.toPage(page.map(logSmallMapper::toDto)); } @Override @Transactional(rollbackFor = Exception.class) public void save(String username, String browser, String ip, ProceedingJoinPoint joinPoint, SysLog sysLog) { if (sysLog == null) { throw new IllegalArgumentException("Log 不能为 null!"); } // 获取方法签名 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); me.zhengjie.annotation.Log aopLog = method.getAnnotation(me.zhengjie.annotation.Log.class); // 方法路径 String methodName = joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()"; // 获取参数 JSONObject params = getParameter(method, joinPoint.getArgs()); // 填充基本信息 sysLog.setRequestIp(ip); sysLog.setAddress(StringUtils.getCityInfo(sysLog.getRequestIp())); sysLog.setMethod(methodName); sysLog.setUsername(username); sysLog.setParams(JSON.toJSONString(params)); sysLog.setBrowser(browser); sysLog.setDescription(aopLog.value()); // 如果没有获取到用户名,尝试从参数中获取 if(StringUtils.isBlank(sysLog.getUsername())){ sysLog.setUsername(params.getString("username")); } // 保存 logRepository.save(sysLog); } /** * 根据方法和传入的参数获取请求参数 */ private JSONObject getParameter(Method method, Object[] args) { JSONObject params = new JSONObject(); Parameter[] parameters = method.getParameters(); for (int i = 0; i < parameters.length; i++) { // 过滤掉 MultiPartFile if (args[i] instanceof MultipartFile) { continue; } // 过滤掉 HttpServletResponse if (args[i] instanceof HttpServletResponse) { continue; } // 过滤掉 HttpServletRequest if (args[i] instanceof HttpServletRequest) { continue; } // 将RequestBody注解修饰的参数作为请求参数 RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class); if (requestBody != null) { // [el-async-1] ERROR o.s.a.i.SimpleAsyncUncaughtExceptionHandler - Unexpected exception occurred invoking async method: public void me.zhengjie.service.impl.SysLogServiceImpl.save(java.lang.String,java.lang.String,java.lang.String,org.aspectj.lang.ProceedingJoinPoint,me.zhengjie.domain.SysLog) // java.lang.ClassCastException: com.alibaba.fastjson2.JSONArray cannot be cast to com.alibaba.fastjson2.JSONObject Object json = JSON.toJSON(args[i]); if (json instanceof JSONArray) { params.put("reqBodyList", json); } else { params.putAll((JSONObject) json); } } else { String key = parameters[i].getName(); params.put(key, args[i]); } } // 遍历敏感字段数组并替换值 Set keys = params.keySet(); for (String key : SENSITIVE_KEYS) { if (keys.contains(key)) { params.put(key, "******"); } } // 返回参数 return params; } @Override public Object findByErrDetail(Long id) { SysLog sysLog = logRepository.findById(id).orElseGet(SysLog::new); ValidationUtil.isNull(sysLog.getId(), "Log", "id", id); byte[] details = sysLog.getExceptionDetail(); return Dict.create().set("exception", new String(ObjectUtil.isNotNull(details) ? details : "".getBytes())); } @Override public void download(List sysLogs, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (SysLog sysLog : sysLogs) { Map map = new LinkedHashMap<>(); map.put("用户名", sysLog.getUsername()); map.put("IP", sysLog.getRequestIp()); map.put("IP来源", sysLog.getAddress()); map.put("描述", sysLog.getDescription()); map.put("浏览器", sysLog.getBrowser()); map.put("请求耗时/毫秒", sysLog.getTime()); map.put("异常详情", new String(ObjectUtil.isNotNull(sysLog.getExceptionDetail()) ? sysLog.getExceptionDetail() : "".getBytes())); map.put("创建日期", sysLog.getCreateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } @Override @Transactional(rollbackFor = Exception.class) public void delAllByError() { logRepository.deleteByLogType("ERROR"); } @Override @Transactional(rollbackFor = Exception.class) public void delAllByInfo() { logRepository.deleteByLogType("INFO"); } } ================================================ FILE: eladmin-logging/src/main/java/me/zhengjie/service/mapstruct/LogErrorMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.domain.SysLog; import me.zhengjie.service.dto.SysLogErrorDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author Zheng Jie * @date 2019-5-22 */ @Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface LogErrorMapper extends BaseMapper { } ================================================ FILE: eladmin-logging/src/main/java/me/zhengjie/service/mapstruct/LogSmallMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.domain.SysLog; import me.zhengjie.service.dto.SysLogSmallDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author Zheng Jie * @date 2019-5-22 */ @Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface LogSmallMapper extends BaseMapper { } ================================================ FILE: eladmin-system/pom.xml ================================================ eladmin me.zhengjie 2.7 4.0.0 eladmin-system 核心模块 0.11.5 5.8.0 me.zhengjie eladmin-generator 2.7 me.zhengjie eladmin-common me.zhengjie eladmin-tools 2.7 org.springframework.boot spring-boot-starter-quartz io.jsonwebtoken jjwt-api ${jjwt.version} io.jsonwebtoken jjwt-impl ${jjwt.version} io.jsonwebtoken jjwt-jackson ${jjwt.version} ch.ethz.ganymed ganymed-ssh2 build210 com.jcraft jsch 0.1.55 com.github.oshi oshi-core 6.6.5 org.springframework.boot spring-boot-maven-plugin org.apache.maven.plugins maven-surefire-plugin true ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/AppRun.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie; import io.swagger.annotations.Api; import lombok.extern.slf4j.Slf4j; import me.zhengjie.annotation.rest.AnonymousGetMapping; import me.zhengjie.utils.SpringBeanHolder; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.ApplicationPidFileWriter; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.web.bind.annotation.RestController; /** * 开启审计功能 -> @EnableJpaAuditing * * @author Zheng Jie * @date 2018/11/15 9:20:19 */ @Slf4j @EnableAsync @RestController @Api(hidden = true) @SpringBootApplication @EnableTransactionManagement @EnableJpaAuditing(auditorAwareRef = "auditorAware") public class AppRun { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(AppRun.class); // 监控应用的PID,启动时可指定PID路径:--spring.pid.file=/home/eladmin/app.pid // 或者在 application.yml 添加文件路径,方便 kill,kill `cat /home/eladmin/app.pid` springApplication.addListeners(new ApplicationPidFileWriter()); ConfigurableApplicationContext context = springApplication.run(args); String port = context.getEnvironment().getProperty("server.port"); log.info("---------------------------------------------"); log.info("Local: http://localhost:{}", port); log.info("Swagger: http://localhost:{}/doc.html", port); log.info("---------------------------------------------"); } @Bean public SpringBeanHolder springContextHolder() { return new SpringBeanHolder(); } /** * 访问首页提示 * * @return / */ @AnonymousGetMapping("/") public String index() { return "Backend service started successfully"; } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/domain/App.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.domain; import io.swagger.annotations.ApiModelProperty; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseEntity; import javax.persistence.*; import java.io.Serializable; /** * @author zhanghouying * @date 2019-08-24 */ @Entity @Getter @Setter @Table(name="mnt_app") public class App extends BaseEntity implements Serializable { @Id @Column(name = "app_id") @ApiModelProperty(value = "ID", hidden = true) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "端口") private int port; @ApiModelProperty(value = "上传路径") private String uploadPath; @ApiModelProperty(value = "部署路径") private String deployPath; @ApiModelProperty(value = "备份路径") private String backupPath; @ApiModelProperty(value = "启动脚本") private String startScript; @ApiModelProperty(value = "部署脚本") private String deployScript; public void copy(App source){ BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/domain/Database.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.domain; import io.swagger.annotations.ApiModelProperty; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseEntity; import javax.persistence.*; import java.io.Serializable; /** * @author zhanghouying * @date 2019-08-24 */ @Entity @Getter @Setter @Table(name="mnt_database") public class Database extends BaseEntity implements Serializable { @Id @Column(name = "db_id") @ApiModelProperty(value = "ID", hidden = true) private String id; @ApiModelProperty(value = "数据库名称") private String name; @ApiModelProperty(value = "数据库连接地址") private String jdbcUrl; @ApiModelProperty(value = "数据库密码") private String pwd; @ApiModelProperty(value = "用户名") private String userName; public void copy(Database source){ BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/domain/Deploy.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.domain; import io.swagger.annotations.ApiModelProperty; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseEntity; import javax.persistence.*; import java.io.Serializable; import java.util.Set; /** * @author zhanghouying * @date 2019-08-24 */ @Entity @Getter @Setter @Table(name="mnt_deploy") public class Deploy extends BaseEntity implements Serializable { @Id @Column(name = "deploy_id") @ApiModelProperty(value = "ID", hidden = true) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToMany @ApiModelProperty(name = "服务器", hidden = true) @JoinTable(name = "mnt_deploy_server", joinColumns = {@JoinColumn(name = "deploy_id",referencedColumnName = "deploy_id")}, inverseJoinColumns = {@JoinColumn(name = "server_id",referencedColumnName = "server_id")}) private Set deploys; @ManyToOne @JoinColumn(name = "app_id") @ApiModelProperty(value = "应用编号") private App app; public void copy(Deploy source){ BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/domain/DeployHistory.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.domain; import io.swagger.annotations.ApiModelProperty; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; import lombok.Getter; import lombok.Setter; import org.hibernate.annotations.CreationTimestamp; import javax.persistence.*; import java.io.Serializable; import java.sql.Timestamp; /** * @author zhanghouying * @date 2019-08-24 */ @Entity @Getter @Setter @Table(name="mnt_deploy_history") public class DeployHistory implements Serializable { @Id @Column(name = "history_id") @ApiModelProperty(value = "ID", hidden = true) private String id; @ApiModelProperty(value = "应用名称") private String appName; @ApiModelProperty(value = "IP") private String ip; @CreationTimestamp @ApiModelProperty(value = "部署时间") private Timestamp deployDate; @ApiModelProperty(value = "部署者") private String deployUser; @ApiModelProperty(value = "部署ID") private Long deployId; public void copy(DeployHistory source){ BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/domain/ServerDeploy.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.domain; import io.swagger.annotations.ApiModelProperty; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseEntity; import javax.persistence.*; import java.io.Serializable; import java.util.Objects; /** * @author zhanghouying * @date 2019-08-24 */ @Entity @Getter @Setter @Table(name="mnt_server") public class ServerDeploy extends BaseEntity implements Serializable { @Id @Column(name = "server_id") @ApiModelProperty(value = "ID", hidden = true) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ApiModelProperty(value = "服务器名称") private String name; @ApiModelProperty(value = "IP") private String ip; @ApiModelProperty(value = "端口") private Integer port; @ApiModelProperty(value = "账号") private String account; @ApiModelProperty(value = "密码") private String password; public void copy(ServerDeploy source){ BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ServerDeploy that = (ServerDeploy) o; return Objects.equals(id, that.id) && Objects.equals(name, that.name); } @Override public int hashCode() { return Objects.hash(id, name); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/domain/enums/DataTypeEnum.java ================================================ /* * << * Davinci * == * Copyright (C) 2016 - 2019 EDP * == * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * >> * */ package me.zhengjie.modules.maint.domain.enums; import lombok.extern.slf4j.Slf4j; /** * @author / */ @Slf4j @SuppressWarnings({"unchecked","all"}) public enum DataTypeEnum { /** mysql */ MYSQL("mysql", "mysql", "com.mysql.jdbc.Driver", "`", "`", "'", "'"), /** oracle */ ORACLE("oracle", "oracle", "oracle.jdbc.driver.OracleDriver", "\"", "\"", "\"", "\""), /** sql server */ SQLSERVER("sqlserver", "sqlserver", "com.microsoft.sqlserver.jdbc.SQLServerDriver", "\"", "\"", "\"", "\""), /** h2 */ H2("h2", "h2", "org.h2.Driver", "`", "`", "\"", "\""), /** phoenix */ PHOENIX("phoenix", "hbase phoenix", "org.apache.phoenix.jdbc.PhoenixDriver", "", "", "\"", "\""), /** mongo */ MONGODB("mongo", "mongodb", "mongodb.jdbc.MongoDriver", "`", "`", "\"", "\""), /** sql4es */ ELASTICSEARCH("sql4es", "elasticsearch", "nl.anchormen.sql4es.jdbc.ESDriver", "", "", "'", "'"), /** presto */ PRESTO("presto", "presto", "com.facebook.presto.jdbc.PrestoDriver", "", "", "\"", "\""), /** moonbox */ MOONBOX("moonbox", "moonbox", "moonbox.jdbc.MbDriver", "`", "`", "`", "`"), /** cassandra */ CASSANDRA("cassandra", "cassandra", "com.github.adejanovski.cassandra.jdbc.CassandraDriver", "", "", "'", "'"), /** click house */ CLICKHOUSE("clickhouse", "clickhouse", "ru.yandex.clickhouse.ClickHouseDriver", "", "", "\"", "\""), /** kylin */ KYLIN("kylin", "kylin", "org.apache.kylin.jdbc.Driver", "\"", "\"", "\"", "\""), /** vertica */ VERTICA("vertica", "vertica", "com.vertica.jdbc.Driver", "", "", "'", "'"), /** sap */ HANA("sap", "sap hana", "com.sap.db.jdbc.Driver", "", "", "'", "'"), /** impala */ IMPALA("impala", "impala", "com.cloudera.impala.jdbc41.Driver", "", "", "'", "'"); private String feature; private String desc; private String driver; private String keywordPrefix; private String keywordSuffix; private String aliasPrefix; private String aliasSuffix; private static final String JDBC_URL_PREFIX = "jdbc:"; DataTypeEnum(String feature, String desc, String driver, String keywordPrefix, String keywordSuffix, String aliasPrefix, String aliasSuffix) { this.feature = feature; this.desc = desc; this.driver = driver; this.keywordPrefix = keywordPrefix; this.keywordSuffix = keywordSuffix; this.aliasPrefix = aliasPrefix; this.aliasSuffix = aliasSuffix; } public static DataTypeEnum urlOf(String jdbcUrl) { String url = jdbcUrl.toLowerCase().trim(); for (DataTypeEnum dataTypeEnum : values()) { if (url.startsWith(JDBC_URL_PREFIX + dataTypeEnum.feature)) { try { Class aClass = Class.forName(dataTypeEnum.getDriver()); if (null == aClass) { throw new RuntimeException("Unable to get driver instance for jdbcUrl: " + jdbcUrl); } } catch (ClassNotFoundException e) { throw new RuntimeException("Unable to get driver instance: " + jdbcUrl); } return dataTypeEnum; } } return null; } public String getFeature() { return feature; } public String getDesc() { return desc; } public String getDriver() { return driver; } public String getKeywordPrefix() { return keywordPrefix; } public String getKeywordSuffix() { return keywordSuffix; } public String getAliasPrefix() { return aliasPrefix; } public String getAliasSuffix() { return aliasSuffix; } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/repository/AppRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.repository; import me.zhengjie.modules.maint.domain.App; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * @author zhanghouying * @date 2019-08-24 */ public interface AppRepository extends JpaRepository, JpaSpecificationExecutor { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/repository/DatabaseRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.repository; import me.zhengjie.modules.maint.domain.Database; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * @author zhanghouying * @date 2019-08-24 */ public interface DatabaseRepository extends JpaRepository, JpaSpecificationExecutor { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/repository/DeployHistoryRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.repository; import me.zhengjie.modules.maint.domain.DeployHistory; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * @author zhanghouying * @date 2019-08-24 */ public interface DeployHistoryRepository extends JpaRepository, JpaSpecificationExecutor { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/repository/DeployRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.repository; import me.zhengjie.modules.maint.domain.Deploy; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * @author zhanghouying * @date 2019-08-24 */ public interface DeployRepository extends JpaRepository, JpaSpecificationExecutor { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/repository/ServerDeployRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.repository; import me.zhengjie.modules.maint.domain.ServerDeploy; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * @author zhanghouying * @date 2019-08-24 */ public interface ServerDeployRepository extends JpaRepository, JpaSpecificationExecutor { /** * 根据IP查询 * @param ip / * @return / */ ServerDeploy findByIp(String ip); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/rest/AppController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.annotation.Log; import me.zhengjie.modules.maint.domain.App; import me.zhengjie.modules.maint.service.AppService; import me.zhengjie.modules.maint.service.dto.AppDto; import me.zhengjie.modules.maint.service.dto.AppQueryCriteria; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Set; /** * @author zhanghouying * @date 2019-08-24 */ @RestController @RequiredArgsConstructor @Api(tags = "运维:应用管理") @RequestMapping("/api/app") public class AppController { private final AppService appService; @ApiOperation("导出应用数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('app:list')") public void exportApp(HttpServletResponse response, AppQueryCriteria criteria) throws IOException { appService.download(appService.queryAll(criteria), response); } @ApiOperation(value = "查询应用") @GetMapping @PreAuthorize("@el.check('app:list')") public ResponseEntity> queryApp(AppQueryCriteria criteria, Pageable pageable){ return new ResponseEntity<>(appService.queryAll(criteria,pageable),HttpStatus.OK); } @Log("新增应用") @ApiOperation(value = "新增应用") @PostMapping @PreAuthorize("@el.check('app:add')") public ResponseEntity createApp(@Validated @RequestBody App resources){ appService.create(resources); return new ResponseEntity<>(HttpStatus.CREATED); } @Log("修改应用") @ApiOperation(value = "修改应用") @PutMapping @PreAuthorize("@el.check('app:edit')") public ResponseEntity updateApp(@Validated @RequestBody App resources){ appService.update(resources); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("删除应用") @ApiOperation(value = "删除应用") @DeleteMapping @PreAuthorize("@el.check('app:del')") public ResponseEntity deleteApp(@RequestBody Set ids){ appService.delete(ids); return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/rest/DatabaseController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.annotation.Log; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.maint.domain.Database; import me.zhengjie.modules.maint.service.DatabaseService; import me.zhengjie.modules.maint.service.dto.DatabaseDto; import me.zhengjie.modules.maint.service.dto.DatabaseQueryCriteria; import me.zhengjie.modules.maint.util.SqlUtils; import me.zhengjie.utils.FileUtil; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.util.Set; /** * @author zhanghouying * @date 2019-08-24 */ @Api(tags = "运维:数据库管理") @RestController @RequiredArgsConstructor @RequestMapping("/api/database") public class DatabaseController { private final String fileSavePath = FileUtil.getTmpDirPath()+"/"; private final DatabaseService databaseService; @ApiOperation("导出数据库数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('database:list')") public void exportDatabase(HttpServletResponse response, DatabaseQueryCriteria criteria) throws IOException { databaseService.download(databaseService.queryAll(criteria), response); } @ApiOperation(value = "查询数据库") @GetMapping @PreAuthorize("@el.check('database:list')") public ResponseEntity> queryDatabase(DatabaseQueryCriteria criteria, Pageable pageable){ return new ResponseEntity<>(databaseService.queryAll(criteria,pageable),HttpStatus.OK); } @Log("新增数据库") @ApiOperation(value = "新增数据库") @PostMapping @PreAuthorize("@el.check('database:add')") public ResponseEntity createDatabase(@Validated @RequestBody Database resources){ databaseService.create(resources); return new ResponseEntity<>(HttpStatus.CREATED); } @Log("修改数据库") @ApiOperation(value = "修改数据库") @PutMapping @PreAuthorize("@el.check('database:edit')") public ResponseEntity updateDatabase(@Validated @RequestBody Database resources){ databaseService.update(resources); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("删除数据库") @ApiOperation(value = "删除数据库") @DeleteMapping @PreAuthorize("@el.check('database:del')") public ResponseEntity deleteDatabase(@RequestBody Set ids){ databaseService.delete(ids); return new ResponseEntity<>(HttpStatus.OK); } @Log("测试数据库链接") @ApiOperation(value = "测试数据库链接") @PostMapping("/testConnect") @PreAuthorize("@el.check('database:testConnect')") public ResponseEntity testConnect(@Validated @RequestBody Database resources){ return new ResponseEntity<>(databaseService.testConnection(resources),HttpStatus.CREATED); } @Log("执行SQL脚本") @ApiOperation(value = "执行SQL脚本") @PostMapping(value = "/upload") @PreAuthorize("@el.check('database:add')") public ResponseEntity uploadDatabase(@RequestBody MultipartFile file, HttpServletRequest request)throws Exception{ String id = request.getParameter("id"); DatabaseDto database = databaseService.findById(id); String fileName; if(database != null){ fileName = FileUtil.verifyFilename(file.getOriginalFilename()); File executeFile = new File(fileSavePath + fileName); FileUtil.del(executeFile); file.transferTo(executeFile); String result = SqlUtils.executeFile(database.getJdbcUrl(), database.getUserName(), database.getPwd(), executeFile); return new ResponseEntity<>(result,HttpStatus.OK); }else{ throw new BadRequestException("Database not exist"); } } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/rest/DeployController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.zhengjie.annotation.Log; import me.zhengjie.modules.maint.domain.Deploy; import me.zhengjie.modules.maint.domain.DeployHistory; import me.zhengjie.modules.maint.service.DeployService; import me.zhengjie.modules.maint.service.dto.DeployDto; import me.zhengjie.modules.maint.service.dto.DeployQueryCriteria; import me.zhengjie.utils.FileUtil; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * @author zhanghouying * @date 2019-08-24 */ @Slf4j @RestController @Api(tags = "运维:部署管理") @RequiredArgsConstructor @RequestMapping("/api/deploy") public class DeployController { private final String fileSavePath = FileUtil.getTmpDirPath()+"/"; private final DeployService deployService; @ApiOperation("导出部署数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('database:list')") public void exportDeployData(HttpServletResponse response, DeployQueryCriteria criteria) throws IOException { deployService.download(deployService.queryAll(criteria), response); } @ApiOperation(value = "查询部署") @GetMapping @PreAuthorize("@el.check('deploy:list')") public ResponseEntity> queryDeployData(DeployQueryCriteria criteria, Pageable pageable){ return new ResponseEntity<>(deployService.queryAll(criteria,pageable),HttpStatus.OK); } @Log("新增部署") @ApiOperation(value = "新增部署") @PostMapping @PreAuthorize("@el.check('deploy:add')") public ResponseEntity createDeploy(@Validated @RequestBody Deploy resources){ deployService.create(resources); return new ResponseEntity<>(HttpStatus.CREATED); } @Log("修改部署") @ApiOperation(value = "修改部署") @PutMapping @PreAuthorize("@el.check('deploy:edit')") public ResponseEntity updateDeploy(@Validated @RequestBody Deploy resources){ deployService.update(resources); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("删除部署") @ApiOperation(value = "删除部署") @DeleteMapping @PreAuthorize("@el.check('deploy:del')") public ResponseEntity deleteDeploy(@RequestBody Set ids){ deployService.delete(ids); return new ResponseEntity<>(HttpStatus.OK); } @Log("上传文件部署") @ApiOperation(value = "上传文件部署") @PostMapping(value = "/upload") @PreAuthorize("@el.check('deploy:edit')") public ResponseEntity uploadDeploy(@RequestBody MultipartFile file, HttpServletRequest request)throws Exception{ Long id = Long.valueOf(request.getParameter("id")); String fileName = ""; if(file != null){ fileName = FileUtil.verifyFilename(file.getOriginalFilename()); File deployFile = new File(fileSavePath + fileName); FileUtil.del(deployFile); file.transferTo(deployFile); //文件下一步要根据文件名字来 deployService.deploy(fileSavePath + fileName ,id); }else{ log.warn("没有找到相对应的文件"); } Map map = new HashMap<>(2); map.put("error",0); map.put("id",fileName); return new ResponseEntity<>(map,HttpStatus.OK); } @Log("系统还原") @ApiOperation(value = "系统还原") @PostMapping(value = "/serverReduction") @PreAuthorize("@el.check('deploy:edit')") public ResponseEntity serverReduction(@Validated @RequestBody DeployHistory resources){ String result = deployService.serverReduction(resources); return new ResponseEntity<>(result,HttpStatus.OK); } @Log("服务运行状态") @ApiOperation(value = "服务运行状态") @PostMapping(value = "/serverStatus") @PreAuthorize("@el.check('deploy:edit')") public ResponseEntity serverStatus(@Validated @RequestBody Deploy resources){ String result = deployService.serverStatus(resources); return new ResponseEntity<>(result,HttpStatus.OK); } @Log("启动服务") @ApiOperation(value = "启动服务") @PostMapping(value = "/startServer") @PreAuthorize("@el.check('deploy:edit')") public ResponseEntity startServer(@Validated @RequestBody Deploy resources){ String result = deployService.startServer(resources); return new ResponseEntity<>(result,HttpStatus.OK); } @Log("停止服务") @ApiOperation(value = "停止服务") @PostMapping(value = "/stopServer") @PreAuthorize("@el.check('deploy:edit')") public ResponseEntity stopServer(@Validated @RequestBody Deploy resources){ String result = deployService.stopServer(resources); return new ResponseEntity<>(result,HttpStatus.OK); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/rest/DeployHistoryController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.annotation.Log; import me.zhengjie.modules.maint.service.DeployHistoryService; import me.zhengjie.modules.maint.service.dto.DeployHistoryDto; import me.zhengjie.modules.maint.service.dto.DeployHistoryQueryCriteria; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Set; /** * @author zhanghouying * @date 2019-08-24 */ @RestController @RequiredArgsConstructor @Api(tags = "运维:部署历史管理") @RequestMapping("/api/deployHistory") public class DeployHistoryController { private final DeployHistoryService deployhistoryService; @ApiOperation("导出部署历史数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('deployHistory:list')") public void exportDeployHistory(HttpServletResponse response, DeployHistoryQueryCriteria criteria) throws IOException { deployhistoryService.download(deployhistoryService.queryAll(criteria), response); } @ApiOperation(value = "查询部署历史") @GetMapping @PreAuthorize("@el.check('deployHistory:list')") public ResponseEntity> queryDeployHistory(DeployHistoryQueryCriteria criteria, Pageable pageable){ return new ResponseEntity<>(deployhistoryService.queryAll(criteria,pageable),HttpStatus.OK); } @Log("删除DeployHistory") @ApiOperation(value = "删除部署历史") @DeleteMapping @PreAuthorize("@el.check('deployHistory:del')") public ResponseEntity deleteDeployHistory(@RequestBody Set ids){ deployhistoryService.delete(ids); return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/rest/ServerDeployController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.annotation.Log; import me.zhengjie.modules.maint.domain.ServerDeploy; import me.zhengjie.modules.maint.service.ServerDeployService; import me.zhengjie.modules.maint.service.dto.ServerDeployDto; import me.zhengjie.modules.maint.service.dto.ServerDeployQueryCriteria; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Set; /** * @author zhanghouying * @date 2019-08-24 */ @RestController @Api(tags = "运维:服务器管理") @RequiredArgsConstructor @RequestMapping("/api/serverDeploy") public class ServerDeployController { private final ServerDeployService serverDeployService; @ApiOperation("导出服务器数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('serverDeploy:list')") public void exportServerDeploy(HttpServletResponse response, ServerDeployQueryCriteria criteria) throws IOException { serverDeployService.download(serverDeployService.queryAll(criteria), response); } @ApiOperation(value = "查询服务器") @GetMapping @PreAuthorize("@el.check('serverDeploy:list')") public ResponseEntity> queryServerDeploy(ServerDeployQueryCriteria criteria, Pageable pageable){ return new ResponseEntity<>(serverDeployService.queryAll(criteria,pageable),HttpStatus.OK); } @Log("新增服务器") @ApiOperation(value = "新增服务器") @PostMapping @PreAuthorize("@el.check('serverDeploy:add')") public ResponseEntity createServerDeploy(@Validated @RequestBody ServerDeploy resources){ serverDeployService.create(resources); return new ResponseEntity<>(HttpStatus.CREATED); } @Log("修改服务器") @ApiOperation(value = "修改服务器") @PutMapping @PreAuthorize("@el.check('serverDeploy:edit')") public ResponseEntity updateServerDeploy(@Validated @RequestBody ServerDeploy resources){ serverDeployService.update(resources); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("删除服务器") @ApiOperation(value = "删除Server") @DeleteMapping @PreAuthorize("@el.check('serverDeploy:del')") public ResponseEntity deleteServerDeploy(@RequestBody Set ids){ serverDeployService.delete(ids); return new ResponseEntity<>(HttpStatus.OK); } @Log("测试连接服务器") @ApiOperation(value = "测试连接服务器") @PostMapping("/testConnect") @PreAuthorize("@el.check('serverDeploy:add')") public ResponseEntity testConnectServerDeploy(@Validated @RequestBody ServerDeploy resources){ return new ResponseEntity<>(serverDeployService.testConnect(resources),HttpStatus.CREATED); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/AppService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service; import me.zhengjie.modules.maint.domain.App; import me.zhengjie.modules.maint.service.dto.AppDto; import me.zhengjie.modules.maint.service.dto.AppQueryCriteria; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Set; /** * @author zhanghouying * @date 2019-08-24 */ public interface AppService { /** * 分页查询 * @param criteria 条件 * @param pageable 分页参数 * @return / */ PageResult queryAll(AppQueryCriteria criteria, Pageable pageable); /** * 查询全部数据 * @param criteria 条件 * @return / */ List queryAll(AppQueryCriteria criteria); /** * 根据ID查询 * @param id / * @return / */ AppDto findById(Long id); /** * 创建 * @param resources / */ void create(App resources); /** * 编辑 * @param resources / */ void update(App resources); /** * 删除 * @param ids / */ void delete(Set ids); /** * 导出数据 * @param queryAll / * @param response / * @throws IOException / */ void download(List queryAll, HttpServletResponse response) throws IOException; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/DatabaseService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service; import me.zhengjie.modules.maint.domain.Database; import me.zhengjie.modules.maint.service.dto.DatabaseDto; import me.zhengjie.modules.maint.service.dto.DatabaseQueryCriteria; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Set; /** * @author ZhangHouYing * @date 2019-08-24 */ public interface DatabaseService { /** * 分页查询 * @param criteria 条件 * @param pageable 分页参数 * @return / */ PageResult queryAll(DatabaseQueryCriteria criteria, Pageable pageable); /** * 查询全部 * @param criteria 条件 * @return / */ List queryAll(DatabaseQueryCriteria criteria); /** * 根据ID查询 * @param id / * @return / */ DatabaseDto findById(String id); /** * 创建 * @param resources / */ void create(Database resources); /** * 编辑 * @param resources / */ void update(Database resources); /** * 删除 * @param ids / */ void delete(Set ids); /** * 测试连接数据库 * @param resources / * @return / */ boolean testConnection(Database resources); /** * 导出数据 * @param queryAll / * @param response / * @throws IOException e */ void download(List queryAll, HttpServletResponse response) throws IOException; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/DeployHistoryService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service; import me.zhengjie.modules.maint.domain.DeployHistory; import me.zhengjie.modules.maint.service.dto.DeployHistoryDto; import me.zhengjie.modules.maint.service.dto.DeployHistoryQueryCriteria; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Set; /** * @author zhanghouying */ public interface DeployHistoryService { /** * 分页查询 * @param criteria 条件 * @param pageable 分页参数 * @return / */ PageResult queryAll(DeployHistoryQueryCriteria criteria, Pageable pageable); /** * 查询全部 * @param criteria 条件 * @return / */ List queryAll(DeployHistoryQueryCriteria criteria); /** * 根据ID查询 * @param id / * @return / */ DeployHistoryDto findById(String id); /** * 创建 * @param resources / */ void create(DeployHistory resources); /** * 删除 * @param ids / */ void delete(Set ids); /** * 导出数据 * @param queryAll / * @param response / * @throws IOException / */ void download(List queryAll, HttpServletResponse response) throws IOException; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/DeployService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service; import me.zhengjie.modules.maint.domain.Deploy; import me.zhengjie.modules.maint.domain.DeployHistory; import me.zhengjie.modules.maint.service.dto.DeployDto; import me.zhengjie.modules.maint.service.dto.DeployQueryCriteria; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Set; /** * @author zhanghouying * @date 2019-08-24 */ public interface DeployService { /** * 分页查询 * @param criteria 条件 * @param pageable 分页参数 * @return / */ PageResult queryAll(DeployQueryCriteria criteria, Pageable pageable); /** * 查询全部数据 * @param criteria 条件 * @return / */ List queryAll(DeployQueryCriteria criteria); /** * 根据ID查询 * @param id / * @return / */ DeployDto findById(Long id); /** * 创建 * @param resources / */ void create(Deploy resources); /** * 编辑 * @param resources / */ void update(Deploy resources); /** * 删除 * @param ids / */ void delete(Set ids); /** * 部署文件到服务器 * @param fileSavePath 文件路径 * @param appId 应用ID */ void deploy(String fileSavePath, Long appId); /** * 查询部署状态 * @param resources / * @return / */ String serverStatus(Deploy resources); /** * 启动服务 * @param resources / * @return / */ String startServer(Deploy resources); /** * 停止服务 * @param resources / * @return / */ String stopServer(Deploy resources); /** * 停止服务 * @param resources / * @return / */ String serverReduction(DeployHistory resources); /** * 导出数据 * @param queryAll / * @param response / * @throws IOException / */ void download(List queryAll, HttpServletResponse response) throws IOException; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/ServerDeployService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service; import me.zhengjie.modules.maint.domain.ServerDeploy; import me.zhengjie.modules.maint.service.dto.ServerDeployDto; import me.zhengjie.modules.maint.service.dto.ServerDeployQueryCriteria; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Set; /** * @author zhanghouying * @date 2019-08-24 */ public interface ServerDeployService { /** * 分页查询 * @param criteria 条件 * @param pageable 分页参数 * @return / */ PageResult queryAll(ServerDeployQueryCriteria criteria, Pageable pageable); /** * 查询全部数据 * @param criteria 条件 * @return / */ List queryAll(ServerDeployQueryCriteria criteria); /** * 根据ID查询 * @param id / * @return / */ ServerDeployDto findById(Long id); /** * 创建 * @param resources / */ void create(ServerDeploy resources); /** * 编辑 * @param resources / */ void update(ServerDeploy resources); /** * 删除 * @param ids / */ void delete(Set ids); /** * 根据IP查询 * @param ip / * @return / */ ServerDeployDto findByIp(String ip); /** * 测试登录服务器 * @param resources / * @return / */ Boolean testConnect(ServerDeploy resources); /** * 导出数据 * @param queryAll / * @param response / * @throws IOException / */ void download(List queryAll, HttpServletResponse response) throws IOException; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/dto/AppDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseDTO; import java.io.Serializable; /** * @author zhanghouying * @date 2019-08-24 */ @Getter @Setter public class AppDto extends BaseDTO implements Serializable { @ApiModelProperty(value = "ID") private Long id; @ApiModelProperty(value = "应用名称") private String name; @ApiModelProperty(value = "端口") private Integer port; @ApiModelProperty(value = "上传目录") private String uploadPath; @ApiModelProperty(value = "部署目录") private String deployPath; @ApiModelProperty(value = "备份目录") private String backupPath; @ApiModelProperty(value = "启动脚本") private String startScript; @ApiModelProperty(value = "部署脚本") private String deployScript; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/dto/AppQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import me.zhengjie.annotation.Query; import java.sql.Timestamp; import java.util.List; /** * @author zhanghouying * @date 2019-08-24 */ @Data public class AppQueryCriteria{ @ApiModelProperty(value = "模糊") @Query(type = Query.Type.INNER_LIKE) private String name; @ApiModelProperty(value = "创建时间") @Query(type = Query.Type.BETWEEN) private List createTime; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/dto/DatabaseDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseDTO; import java.io.Serializable; /** * @author zhanghouying * @date 2019-08-24 */ @Getter @Setter public class DatabaseDto extends BaseDTO implements Serializable { @ApiModelProperty(value = "ID") private String id; @ApiModelProperty(value = "数据库名称") private String name; @ApiModelProperty(value = "数据库连接地址") private String jdbcUrl; @ApiModelProperty(value = "数据库密码") private String pwd; @ApiModelProperty(value = "用户名") private String userName; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/dto/DatabaseQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import me.zhengjie.annotation.Query; import java.sql.Timestamp; import java.util.List; /** * @author zhanghouying * @date 2019-08-24 */ @Data public class DatabaseQueryCriteria{ @ApiModelProperty(value = "模糊") @Query(type = Query.Type.INNER_LIKE) private String name; @Query @ApiModelProperty(value = "数据库连接地址") private String jdbcUrl; @ApiModelProperty(value = "创建时间") @Query(type = Query.Type.BETWEEN) private List createTime; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/dto/DeployDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.dto; import cn.hutool.core.collection.CollectionUtil; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseDTO; import java.io.Serializable; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; /** * @author zhanghouying * @date 2019-08-24 */ @Getter @Setter public class DeployDto extends BaseDTO implements Serializable { @ApiModelProperty(value = "ID") private String id; @ApiModelProperty(value = "应用") private AppDto app; @ApiModelProperty(value = "服务器") private Set deploys; @ApiModelProperty(value = "服务器名称") private String servers; @ApiModelProperty(value = "服务状态") private String status; public String getServers() { if(CollectionUtil.isNotEmpty(deploys)){ return deploys.stream().map(ServerDeployDto::getName).collect(Collectors.joining(",")); } return servers; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } DeployDto deployDto = (DeployDto) o; return Objects.equals(id, deployDto.id); } @Override public int hashCode() { return Objects.hash(id); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/dto/DeployHistoryDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; import java.sql.Timestamp; /** * @author zhanghouying * @date 2019-08-24 */ @Data public class DeployHistoryDto implements Serializable { @ApiModelProperty(value = "ID") private String id; @ApiModelProperty(value = "应用名称") private String appName; @ApiModelProperty(value = "部署IP") private String ip; @ApiModelProperty(value = "部署时间") private Timestamp deployDate; @ApiModelProperty(value = "部署人员") private String deployUser; @ApiModelProperty(value = "部署编号") private Long deployId; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/dto/DeployHistoryQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import me.zhengjie.annotation.Query; import java.sql.Timestamp; import java.util.List; /** * @author zhanghouying * @date 2019-08-24 */ @Data public class DeployHistoryQueryCriteria{ @ApiModelProperty(value = "模糊查询") @Query(blurry = "appName,ip,deployUser") private String blurry; @Query @ApiModelProperty(value = "部署编号") private Long deployId; @ApiModelProperty(value = "部署时间") @Query(type = Query.Type.BETWEEN) private List deployDate; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/dto/DeployQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import me.zhengjie.annotation.Query; import java.sql.Timestamp; import java.util.List; /** * @author zhanghouying * @date 2019-08-24 */ @Data public class DeployQueryCriteria{ @ApiModelProperty(value = "应用名称") @Query(type = Query.Type.INNER_LIKE, propName = "name", joinName = "app") private String appName; @ApiModelProperty(value = "创建时间") @Query(type = Query.Type.BETWEEN) private List createTime; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/dto/ServerDeployDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseDTO; import java.io.Serializable; import java.util.Objects; /** * @author zhanghouying * @date 2019-08-24 */ @Getter @Setter public class ServerDeployDto extends BaseDTO implements Serializable { @ApiModelProperty(value = "ID") private Long id; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "IP") private String ip; @ApiModelProperty(value = "端口") private Integer port; @ApiModelProperty(value = "账号") private String account; @ApiModelProperty(value = "密码") private String password; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ServerDeployDto that = (ServerDeployDto) o; return Objects.equals(id, that.id) && Objects.equals(name, that.name); } @Override public int hashCode() { return Objects.hash(id, name); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/dto/ServerDeployQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import me.zhengjie.annotation.Query; import java.sql.Timestamp; import java.util.List; /** * @author zhanghouying * @date 2019-08-24 */ @Data public class ServerDeployQueryCriteria{ @ApiModelProperty(value = "模糊查询") @Query(blurry = "name,ip,account") private String blurry; @ApiModelProperty(value = "创建时间") @Query(type = Query.Type.BETWEEN) private List createTime; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/impl/AppServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.impl; import lombok.RequiredArgsConstructor; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.maint.domain.App; import me.zhengjie.modules.maint.repository.AppRepository; import me.zhengjie.modules.maint.service.AppService; import me.zhengjie.modules.maint.service.dto.AppDto; import me.zhengjie.modules.maint.service.dto.AppQueryCriteria; import me.zhengjie.modules.maint.service.mapstruct.AppMapper; import me.zhengjie.utils.*; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; /** * @author zhanghouying * @date 2019-08-24 */ @Service @RequiredArgsConstructor public class AppServiceImpl implements AppService { private final AppRepository appRepository; private final AppMapper appMapper; @Override public PageResult queryAll(AppQueryCriteria criteria, Pageable pageable){ Page page = appRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); return PageUtil.toPage(page.map(appMapper::toDto)); } @Override public List queryAll(AppQueryCriteria criteria){ return appMapper.toDto(appRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); } @Override public AppDto findById(Long id) { App app = appRepository.findById(id).orElseGet(App::new); ValidationUtil.isNull(app.getId(),"App","id",id); return appMapper.toDto(app); } @Override @Transactional(rollbackFor = Exception.class) public void create(App resources) { // 验证应用名称是否存在恶意攻击payload,https://github.com/elunez/eladmin/issues/873 String appName = resources.getName(); if (appName.contains(";") || appName.contains("|") || appName.contains("&")) { throw new IllegalArgumentException("非法的应用名称,请勿包含[; | &]等特殊字符"); } verification(resources); appRepository.save(resources); } @Override @Transactional(rollbackFor = Exception.class) public void update(App resources) { // 验证应用名称是否存在恶意攻击payload,https://github.com/elunez/eladmin/issues/873 String appName = resources.getName(); if (appName.contains(";") || appName.contains("|") || appName.contains("&")) { throw new IllegalArgumentException("非法的应用名称,请勿包含[; | &]等特殊字符"); } verification(resources); App app = appRepository.findById(resources.getId()).orElseGet(App::new); ValidationUtil.isNull(app.getId(),"App","id",resources.getId()); app.copy(resources); appRepository.save(app); } private void verification(App resources){ String opt = "/opt"; String home = "/home"; if (!(resources.getUploadPath().startsWith(opt) || resources.getUploadPath().startsWith(home))) { throw new BadRequestException("文件只能上传在opt目录或者home目录 "); } if (!(resources.getDeployPath().startsWith(opt) || resources.getDeployPath().startsWith(home))) { throw new BadRequestException("文件只能部署在opt目录或者home目录 "); } if (!(resources.getBackupPath().startsWith(opt) || resources.getBackupPath().startsWith(home))) { throw new BadRequestException("文件只能备份在opt目录或者home目录 "); } } @Override @Transactional(rollbackFor = Exception.class) public void delete(Set ids) { for (Long id : ids) { appRepository.deleteById(id); } } @Override public void download(List queryAll, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (AppDto appDto : queryAll) { Map map = new LinkedHashMap<>(); map.put("应用名称", appDto.getName()); map.put("端口", appDto.getPort()); map.put("上传目录", appDto.getUploadPath()); map.put("部署目录", appDto.getDeployPath()); map.put("备份目录", appDto.getBackupPath()); map.put("启动脚本", appDto.getStartScript()); map.put("部署脚本", appDto.getDeployScript()); map.put("创建日期", appDto.getCreateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/impl/DatabaseServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.impl; import cn.hutool.core.util.IdUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.zhengjie.modules.maint.domain.Database; import me.zhengjie.modules.maint.repository.DatabaseRepository; import me.zhengjie.modules.maint.service.DatabaseService; import me.zhengjie.modules.maint.service.dto.DatabaseDto; import me.zhengjie.modules.maint.service.dto.DatabaseQueryCriteria; import me.zhengjie.modules.maint.service.mapstruct.DatabaseMapper; import me.zhengjie.modules.maint.util.SqlUtils; import me.zhengjie.utils.*; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; /** * @author zhanghouying * @date 2019-08-24 */ @Slf4j @Service @RequiredArgsConstructor public class DatabaseServiceImpl implements DatabaseService { private final DatabaseRepository databaseRepository; private final DatabaseMapper databaseMapper; @Override public PageResult queryAll(DatabaseQueryCriteria criteria, Pageable pageable){ Page page = databaseRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); return PageUtil.toPage(page.map(databaseMapper::toDto)); } @Override public List queryAll(DatabaseQueryCriteria criteria){ return databaseMapper.toDto(databaseRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); } @Override public DatabaseDto findById(String id) { Database database = databaseRepository.findById(id).orElseGet(Database::new); ValidationUtil.isNull(database.getId(),"Database","id",id); return databaseMapper.toDto(database); } @Override @Transactional(rollbackFor = Exception.class) public void create(Database resources) { resources.setId(IdUtil.simpleUUID()); databaseRepository.save(resources); } @Override @Transactional(rollbackFor = Exception.class) public void update(Database resources) { Database database = databaseRepository.findById(resources.getId()).orElseGet(Database::new); ValidationUtil.isNull(database.getId(),"Database","id",resources.getId()); database.copy(resources); databaseRepository.save(database); } @Override @Transactional(rollbackFor = Exception.class) public void delete(Set ids) { for (String id : ids) { databaseRepository.deleteById(id); } } @Override public boolean testConnection(Database resources) { try { return SqlUtils.testConnection(resources.getJdbcUrl(), resources.getUserName(), resources.getPwd()); } catch (Exception e) { log.error(e.getMessage()); return false; } } @Override public void download(List queryAll, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (DatabaseDto databaseDto : queryAll) { Map map = new LinkedHashMap<>(); map.put("数据库名称", databaseDto.getName()); map.put("数据库连接地址", databaseDto.getJdbcUrl()); map.put("用户名", databaseDto.getUserName()); map.put("创建日期", databaseDto.getCreateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/impl/DeployHistoryServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.impl; import cn.hutool.core.util.IdUtil; import lombok.RequiredArgsConstructor; import me.zhengjie.modules.maint.domain.DeployHistory; import me.zhengjie.modules.maint.repository.DeployHistoryRepository; import me.zhengjie.modules.maint.service.DeployHistoryService; import me.zhengjie.modules.maint.service.dto.DeployHistoryDto; import me.zhengjie.modules.maint.service.dto.DeployHistoryQueryCriteria; import me.zhengjie.modules.maint.service.mapstruct.DeployHistoryMapper; import me.zhengjie.utils.*; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; /** * @author zhanghouying * @date 2019-08-24 */ @Service @RequiredArgsConstructor public class DeployHistoryServiceImpl implements DeployHistoryService { private final DeployHistoryRepository deployhistoryRepository; private final DeployHistoryMapper deployhistoryMapper; @Override public PageResult queryAll(DeployHistoryQueryCriteria criteria, Pageable pageable){ Page page = deployhistoryRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); return PageUtil.toPage(page.map(deployhistoryMapper::toDto)); } @Override public List queryAll(DeployHistoryQueryCriteria criteria){ return deployhistoryMapper.toDto(deployhistoryRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); } @Override public DeployHistoryDto findById(String id) { DeployHistory deployhistory = deployhistoryRepository.findById(id).orElseGet(DeployHistory::new); ValidationUtil.isNull(deployhistory.getId(),"DeployHistory","id",id); return deployhistoryMapper.toDto(deployhistory); } @Override @Transactional(rollbackFor = Exception.class) public void create(DeployHistory resources) { resources.setId(IdUtil.simpleUUID()); deployhistoryRepository.save(resources); } @Override @Transactional(rollbackFor = Exception.class) public void delete(Set ids) { for (String id : ids) { deployhistoryRepository.deleteById(id); } } @Override public void download(List queryAll, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (DeployHistoryDto deployHistoryDto : queryAll) { Map map = new LinkedHashMap<>(); map.put("部署编号", deployHistoryDto.getDeployId()); map.put("应用名称", deployHistoryDto.getAppName()); map.put("部署IP", deployHistoryDto.getIp()); map.put("部署时间", deployHistoryDto.getDeployDate()); map.put("部署人员", deployHistoryDto.getDeployUser()); list.add(map); } FileUtil.downloadExcel(list, response); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/impl/DeployServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.impl; import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DateUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.maint.domain.App; import me.zhengjie.modules.maint.domain.Deploy; import me.zhengjie.modules.maint.domain.DeployHistory; import me.zhengjie.modules.maint.domain.ServerDeploy; import me.zhengjie.modules.maint.repository.DeployRepository; import me.zhengjie.modules.maint.service.DeployHistoryService; import me.zhengjie.modules.maint.service.DeployService; import me.zhengjie.modules.maint.service.ServerDeployService; import me.zhengjie.modules.maint.service.dto.AppDto; import me.zhengjie.modules.maint.service.dto.DeployDto; import me.zhengjie.modules.maint.service.dto.DeployQueryCriteria; import me.zhengjie.modules.maint.service.dto.ServerDeployDto; import me.zhengjie.modules.maint.service.mapstruct.DeployMapper; import me.zhengjie.modules.maint.util.ExecuteShellUtil; import me.zhengjie.modules.maint.util.ScpClientUtil; import me.zhengjie.modules.maint.websocket.MsgType; import me.zhengjie.modules.maint.websocket.SocketMsg; import me.zhengjie.modules.maint.websocket.WebSocketServer; import me.zhengjie.utils.*; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; /** * @author zhanghouying * @date 2019-08-24 */ @Slf4j @Service @RequiredArgsConstructor public class DeployServiceImpl implements DeployService { private final String FILE_SEPARATOR = "/"; private final DeployRepository deployRepository; private final DeployMapper deployMapper; private final ServerDeployService serverDeployService; private final DeployHistoryService deployHistoryService; /** * 循环次数 */ private final Integer count = 30; @Override public PageResult queryAll(DeployQueryCriteria criteria, Pageable pageable) { Page page = deployRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable); return PageUtil.toPage(page.map(deployMapper::toDto)); } @Override public List queryAll(DeployQueryCriteria criteria) { return deployMapper.toDto(deployRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder))); } @Override public DeployDto findById(Long id) { Deploy deploy = deployRepository.findById(id).orElseGet(Deploy::new); ValidationUtil.isNull(deploy.getId(), "Deploy", "id", id); return deployMapper.toDto(deploy); } @Override @Transactional(rollbackFor = Exception.class) public void create(Deploy resources) { deployRepository.save(resources); } @Override @Transactional(rollbackFor = Exception.class) public void update(Deploy resources) { Deploy deploy = deployRepository.findById(resources.getId()).orElseGet(Deploy::new); ValidationUtil.isNull(deploy.getId(), "Deploy", "id", resources.getId()); deploy.copy(resources); deployRepository.save(deploy); } @Override @Transactional(rollbackFor = Exception.class) public void delete(Set ids) { for (Long id : ids) { deployRepository.deleteById(id); } } @Override public void deploy(String fileSavePath, Long id) { deployApp(fileSavePath, id); } /** * @param fileSavePath 本机路径 * @param id ID */ private void deployApp(String fileSavePath, Long id) { DeployDto deploy = findById(id); if (deploy == null) { sendMsg("部署信息不存在", MsgType.ERROR); throw new BadRequestException("部署信息不存在"); } AppDto app = deploy.getApp(); if (app == null) { sendMsg("包对应应用信息不存在", MsgType.ERROR); throw new BadRequestException("包对应应用信息不存在"); } int port = app.getPort(); //这个是服务器部署路径 String uploadPath = app.getUploadPath(); StringBuilder sb = new StringBuilder(); String msg; Set deploys = deploy.getDeploys(); for (ServerDeployDto deployDTO : deploys) { String ip = deployDTO.getIp(); ExecuteShellUtil executeShellUtil = getExecuteShellUtil(ip); //判断是否第一次部署 boolean flag = checkFile(executeShellUtil, app); //第一步要确认服务器上有这个目录 executeShellUtil.execute("mkdir -p " + app.getUploadPath()); executeShellUtil.execute("mkdir -p " + app.getBackupPath()); executeShellUtil.execute("mkdir -p " + app.getDeployPath()); //上传文件 msg = String.format("登陆到服务器:%s", ip); ScpClientUtil scpClientUtil = getScpClientUtil(ip); log.info(msg); sendMsg(msg, MsgType.INFO); msg = String.format("上传文件到服务器:%s
目录:%s下,请稍等...", ip, uploadPath); sendMsg(msg, MsgType.INFO); scpClientUtil.putFile(fileSavePath, uploadPath); if (flag) { sendMsg("停止原来应用", MsgType.INFO); //停止应用 stopApp(port, executeShellUtil); sendMsg("备份原来应用", MsgType.INFO); //备份应用 backupApp(executeShellUtil, ip, app.getDeployPath()+FILE_SEPARATOR, app.getName(), app.getBackupPath()+FILE_SEPARATOR, id); } sendMsg("部署应用", MsgType.INFO); //部署文件,并启动应用 String deployScript = app.getDeployScript(); executeShellUtil.execute(deployScript); sleep(3); sendMsg("应用部署中,请耐心等待部署结果,或者稍后手动查看部署状态", MsgType.INFO); int i = 0; boolean result = false; // 由于启动应用需要时间,所以需要循环获取状态,如果超过30次,则认为是启动失败 while (i++ < count){ result = checkIsRunningStatus(port, executeShellUtil); if(result){ break; } // 休眠6秒 sleep(6); } sb.append("服务器:").append(deployDTO.getName()).append("
应用:").append(app.getName()); sendResultMsg(result, sb); executeShellUtil.close(); } } private void sleep(int second) { try { Thread.sleep(second * 1000); } catch (InterruptedException e) { log.error(e.getMessage(),e); } } private void backupApp(ExecuteShellUtil executeShellUtil, String ip, String fileSavePath, String appName, String backupPath, Long id) { String deployDate = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN); StringBuilder sb = new StringBuilder(); backupPath += appName + FILE_SEPARATOR + deployDate + "\n"; sb.append("mkdir -p ").append(backupPath); sb.append("mv -f ").append(fileSavePath); sb.append(appName).append(" ").append(backupPath); log.info("备份应用脚本:" + sb.toString()); executeShellUtil.execute(sb.toString()); //还原信息入库 DeployHistory deployHistory = new DeployHistory(); deployHistory.setAppName(appName); deployHistory.setDeployUser(SecurityUtils.getCurrentUsername()); deployHistory.setIp(ip); deployHistory.setDeployId(id); deployHistoryService.create(deployHistory); } /** * 停App * * @param port 端口 * @param executeShellUtil / */ private void stopApp(int port, ExecuteShellUtil executeShellUtil) { //发送停止命令 executeShellUtil.execute(String.format("lsof -i :%d|grep -v \"PID\"|awk '{print \"kill -9\",$2}'|sh", port)); } /** * 指定端口程序是否在运行 * * @param port 端口 * @param executeShellUtil / * @return true 正在运行 false 已经停止 */ private boolean checkIsRunningStatus(int port, ExecuteShellUtil executeShellUtil) { String result = executeShellUtil.executeForResult(String.format("fuser -n tcp %d", port)); return result.indexOf("/tcp:")>0; } private void sendMsg(String msg, MsgType msgType) { try { WebSocketServer.sendInfo(new SocketMsg(msg, msgType), "deploy"); } catch (IOException e) { log.error(e.getMessage(),e); } } @Override public String serverStatus(Deploy resources) { Set serverDeploys = resources.getDeploys(); App app = resources.getApp(); for (ServerDeploy serverDeploy : serverDeploys) { StringBuilder sb = new StringBuilder(); ExecuteShellUtil executeShellUtil = getExecuteShellUtil(serverDeploy.getIp()); sb.append("服务器:").append(serverDeploy.getName()).append("
应用:").append(app.getName()); boolean result = checkIsRunningStatus(app.getPort(), executeShellUtil); if (result) { sb.append("
正在运行"); sendMsg(sb.toString(), MsgType.INFO); } else { sb.append("
已停止!"); sendMsg(sb.toString(), MsgType.ERROR); } log.info(sb.toString()); executeShellUtil.close(); } return "执行完毕"; } private boolean checkFile(ExecuteShellUtil executeShellUtil, AppDto app) { String deployPath = app.getDeployPath(); String appName = app.getName(); // 使用安全的命令执行方式,避免直接拼接字符串,https://github.com/elunez/eladmin/issues/873 String[] command = {"find", deployPath, "-name", appName}; String result = executeShellUtil.executeForResult(Arrays.toString(command)); return result.contains(appName); } /** * 启动服务 * @param resources / * @return / */ @Override public String startServer(Deploy resources) { Set deploys = resources.getDeploys(); App app = resources.getApp(); for (ServerDeploy deploy : deploys) { StringBuilder sb = new StringBuilder(); ExecuteShellUtil executeShellUtil = getExecuteShellUtil(deploy.getIp()); //为了防止重复启动,这里先停止应用 stopApp(app.getPort(), executeShellUtil); sb.append("服务器:").append(deploy.getName()).append("
应用:").append(app.getName()); sendMsg("下发启动命令", MsgType.INFO); executeShellUtil.execute(app.getStartScript()); sleep(3); sendMsg("应用启动中,请耐心等待启动结果,或者稍后手动查看运行状态", MsgType.INFO); int i = 0; boolean result = false; // 由于启动应用需要时间,所以需要循环获取状态,如果超过30次,则认为是启动失败 while (i++ < count){ result = checkIsRunningStatus(app.getPort(), executeShellUtil); if(result){ break; } // 休眠6秒 sleep(6); } sendResultMsg(result, sb); log.info(sb.toString()); executeShellUtil.close(); } return "执行完毕"; } /** * 停止服务 * @param resources / * @return / */ @Override public String stopServer(Deploy resources) { Set deploys = resources.getDeploys(); App app = resources.getApp(); for (ServerDeploy deploy : deploys) { StringBuilder sb = new StringBuilder(); ExecuteShellUtil executeShellUtil = getExecuteShellUtil(deploy.getIp()); sb.append("服务器:").append(deploy.getName()).append("
应用:").append(app.getName()); sendMsg("下发停止命令", MsgType.INFO); //停止应用 stopApp(app.getPort(), executeShellUtil); sleep(1); boolean result = checkIsRunningStatus(app.getPort(), executeShellUtil); if (result) { sb.append("
关闭失败!"); sendMsg(sb.toString(), MsgType.ERROR); } else { sb.append("
关闭成功!"); sendMsg(sb.toString(), MsgType.INFO); } log.info(sb.toString()); executeShellUtil.close(); } return "执行完毕"; } @Override public String serverReduction(DeployHistory resources) { Long deployId = resources.getDeployId(); Deploy deployInfo = deployRepository.findById(deployId).orElseGet(Deploy::new); String deployDate = DateUtil.format(resources.getDeployDate(), DatePattern.PURE_DATETIME_PATTERN); App app = deployInfo.getApp(); if (app == null) { sendMsg("应用信息不存在:" + resources.getAppName(), MsgType.ERROR); throw new BadRequestException("应用信息不存在:" + resources.getAppName()); } String backupPath = app.getBackupPath()+FILE_SEPARATOR; backupPath += resources.getAppName() + FILE_SEPARATOR + deployDate; //这个是服务器部署路径 String deployPath = app.getDeployPath(); String ip = resources.getIp(); ExecuteShellUtil executeShellUtil = getExecuteShellUtil(ip); String msg; msg = String.format("登陆到服务器:%s", ip); log.info(msg); sendMsg(msg, MsgType.INFO); sendMsg("停止原来应用", MsgType.INFO); //停止应用 stopApp(app.getPort(), executeShellUtil); //删除原来应用 sendMsg("删除应用", MsgType.INFO); executeShellUtil.execute("rm -rf " + deployPath + FILE_SEPARATOR + resources.getAppName()); //还原应用 sendMsg("还原应用", MsgType.INFO); executeShellUtil.execute("cp -r " + backupPath + "/. " + deployPath); sendMsg("启动应用", MsgType.INFO); executeShellUtil.execute(app.getStartScript()); sendMsg("应用启动中,请耐心等待启动结果,或者稍后手动查看启动状态", MsgType.INFO); int i = 0; boolean result = false; // 由于启动应用需要时间,所以需要循环获取状态,如果超过30次,则认为是启动失败 while (i++ < count){ result = checkIsRunningStatus(app.getPort(), executeShellUtil); if(result){ break; } // 休眠6秒 sleep(6); } StringBuilder sb = new StringBuilder(); sb.append("服务器:").append(ip).append("
应用:").append(resources.getAppName()); sendResultMsg(result, sb); executeShellUtil.close(); return ""; } private ExecuteShellUtil getExecuteShellUtil(String ip) { ServerDeployDto serverDeployDTO = serverDeployService.findByIp(ip); if (serverDeployDTO == null) { sendMsg("IP对应服务器信息不存在:" + ip, MsgType.ERROR); throw new BadRequestException("IP对应服务器信息不存在:" + ip); } return new ExecuteShellUtil(ip, serverDeployDTO.getAccount(), serverDeployDTO.getPassword(),serverDeployDTO.getPort()); } private ScpClientUtil getScpClientUtil(String ip) { ServerDeployDto serverDeployDTO = serverDeployService.findByIp(ip); if (serverDeployDTO == null) { sendMsg("IP对应服务器信息不存在:" + ip, MsgType.ERROR); throw new BadRequestException("IP对应服务器信息不存在:" + ip); } return ScpClientUtil.getInstance(ip, serverDeployDTO.getPort(), serverDeployDTO.getAccount(), serverDeployDTO.getPassword()); } private void sendResultMsg(boolean result, StringBuilder sb) { if (result) { sb.append("
启动成功!"); sendMsg(sb.toString(), MsgType.INFO); } else { sb.append("
启动失败!"); sendMsg(sb.toString(), MsgType.ERROR); } } @Override public void download(List queryAll, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (DeployDto deployDto : queryAll) { Map map = new LinkedHashMap<>(); map.put("应用名称", deployDto.getApp().getName()); map.put("服务器", deployDto.getServers()); map.put("部署日期", deployDto.getCreateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/impl/ServerDeployServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.impl; import lombok.RequiredArgsConstructor; import me.zhengjie.modules.maint.domain.ServerDeploy; import me.zhengjie.modules.maint.repository.ServerDeployRepository; import me.zhengjie.modules.maint.service.ServerDeployService; import me.zhengjie.modules.maint.service.dto.ServerDeployDto; import me.zhengjie.modules.maint.service.dto.ServerDeployQueryCriteria; import me.zhengjie.modules.maint.service.mapstruct.ServerDeployMapper; import me.zhengjie.modules.maint.util.ExecuteShellUtil; import me.zhengjie.utils.*; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; /** * @author zhanghouying * @date 2019-08-24 */ @Service @RequiredArgsConstructor public class ServerDeployServiceImpl implements ServerDeployService { private final ServerDeployRepository serverDeployRepository; private final ServerDeployMapper serverDeployMapper; @Override public PageResult queryAll(ServerDeployQueryCriteria criteria, Pageable pageable){ Page page = serverDeployRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); return PageUtil.toPage(page.map(serverDeployMapper::toDto)); } @Override public List queryAll(ServerDeployQueryCriteria criteria){ return serverDeployMapper.toDto(serverDeployRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); } @Override public ServerDeployDto findById(Long id) { ServerDeploy server = serverDeployRepository.findById(id).orElseGet(ServerDeploy::new); ValidationUtil.isNull(server.getId(),"ServerDeploy","id",id); return serverDeployMapper.toDto(server); } @Override public ServerDeployDto findByIp(String ip) { ServerDeploy deploy = serverDeployRepository.findByIp(ip); return serverDeployMapper.toDto(deploy); } @Override public Boolean testConnect(ServerDeploy resources) { ExecuteShellUtil executeShellUtil = null; try { executeShellUtil = new ExecuteShellUtil(resources.getIp(), resources.getAccount(), resources.getPassword(),resources.getPort()); return executeShellUtil.execute("ls")==0; } catch (Exception e) { return false; }finally { if (executeShellUtil != null) { executeShellUtil.close(); } } } @Override @Transactional(rollbackFor = Exception.class) public void create(ServerDeploy resources) { serverDeployRepository.save(resources); } @Override @Transactional(rollbackFor = Exception.class) public void update(ServerDeploy resources) { ServerDeploy serverDeploy = serverDeployRepository.findById(resources.getId()).orElseGet(ServerDeploy::new); ValidationUtil.isNull( serverDeploy.getId(),"ServerDeploy","id",resources.getId()); serverDeploy.copy(resources); serverDeployRepository.save(serverDeploy); } @Override @Transactional(rollbackFor = Exception.class) public void delete(Set ids) { for (Long id : ids) { serverDeployRepository.deleteById(id); } } @Override public void download(List queryAll, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (ServerDeployDto deployDto : queryAll) { Map map = new LinkedHashMap<>(); map.put("服务器名称", deployDto.getName()); map.put("服务器IP", deployDto.getIp()); map.put("端口", deployDto.getPort()); map.put("账号", deployDto.getAccount()); map.put("创建日期", deployDto.getCreateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/mapstruct/AppMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.maint.domain.App; import me.zhengjie.modules.maint.service.dto.AppDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author zhanghouying * @date 2019-08-24 */ @Mapper(componentModel = "spring",uses = {},unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface AppMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/mapstruct/DatabaseMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.maint.domain.Database; import me.zhengjie.modules.maint.service.dto.DatabaseDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author zhanghouying * @date 2019-08-24 */ @Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface DatabaseMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/mapstruct/DeployHistoryMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.maint.domain.DeployHistory; import me.zhengjie.modules.maint.service.dto.DeployHistoryDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author zhanghouying * @date 2019-08-24 */ @Mapper(componentModel = "spring",uses = {},unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface DeployHistoryMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/mapstruct/DeployMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.maint.domain.Deploy; import me.zhengjie.modules.maint.service.dto.DeployDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author zhanghouying * @date 2019-08-24 */ @Mapper(componentModel = "spring",uses = {AppMapper.class, ServerDeployMapper.class},unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface DeployMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/service/mapstruct/ServerDeployMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.maint.domain.ServerDeploy; import me.zhengjie.modules.maint.service.dto.ServerDeployDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author zhanghouying * @date 2019-08-24 */ @Mapper(componentModel = "spring",uses = {},unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface ServerDeployMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/util/ExecuteShellUtil.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.util; import cn.hutool.core.io.IoUtil; import com.jcraft.jsch.ChannelShell; import com.jcraft.jsch.JSch; import com.jcraft.jsch.Session; import lombok.extern.slf4j.Slf4j; import java.io.*; import java.util.Vector; /** * 执行shell命令 * * @author ZhangHouYing * @date 2019/8/10 */ @Slf4j public class ExecuteShellUtil { private Vector stdout; Session session; public ExecuteShellUtil(final String ipAddress, final String username, final String password,int port) { try { JSch jsch = new JSch(); session = jsch.getSession(username, ipAddress, port); session.setPassword(password); session.setConfig("StrictHostKeyChecking", "no"); session.connect(3000); } catch (Exception e) { log.error(e.getMessage(),e); } } public int execute(final String command) { int returnCode = 0; ChannelShell channel = null; PrintWriter printWriter = null; BufferedReader input = null; stdout = new Vector(); try { channel = (ChannelShell) session.openChannel("shell"); channel.connect(); input = new BufferedReader(new InputStreamReader(channel.getInputStream())); printWriter = new PrintWriter(channel.getOutputStream()); printWriter.println(command); printWriter.println("exit"); printWriter.flush(); log.info("The remote command is: "); String line; while ((line = input.readLine()) != null) { stdout.add(line); System.out.println(line); } } catch (Exception e) { log.error(e.getMessage(),e); return -1; }finally { IoUtil.close(printWriter); IoUtil.close(input); if (channel != null) { channel.disconnect(); } } return returnCode; } public void close(){ if (session != null) { session.disconnect(); } } public String executeForResult(String command) { execute(command); StringBuilder sb = new StringBuilder(); for (String str : stdout) { sb.append(str); } return sb.toString(); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/util/ScpClientUtil.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.util; import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.SCPClient; import com.google.common.collect.Maps; import me.zhengjie.utils.StringUtils; import java.io.IOException; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; /** * 远程执行linux命令 * @author ZhangHouYing * @date 2019-08-10 10:06 */ public class ScpClientUtil { private final String ip; private final int port; private final String username; private final String password; static private final Map instance = Maps.newHashMap(); static synchronized public ScpClientUtil getInstance(String ip, int port, String username, String password) { instance.computeIfAbsent(ip, i -> new ScpClientUtil(i, port, username, password)); return instance.get(ip); } public ScpClientUtil(String ip, int port, String username, String password) { this.ip = ip; this.port = port; this.username = username; this.password = password; } public void getFile(String remoteFile, String localTargetDirectory) { Connection conn = new Connection(ip, port); try { conn.connect(); boolean isAuthenticated = conn.authenticateWithPassword(username, password); if (!isAuthenticated) { System.err.println("authentication failed"); } SCPClient client = new SCPClient(conn); client.get(remoteFile, localTargetDirectory); } catch (IOException ex) { Logger.getLogger(SCPClient.class.getName()).log(Level.SEVERE, null, ex); }finally{ conn.close(); } } public void putFile(String localFile, String remoteTargetDirectory) { putFile(localFile, null, remoteTargetDirectory); } public void putFile(String localFile, String remoteFileName, String remoteTargetDirectory) { putFile(localFile, remoteFileName, remoteTargetDirectory,null); } public void putFile(String localFile, String remoteFileName, String remoteTargetDirectory, String mode) { Connection conn = new Connection(ip, port); try { conn.connect(); boolean isAuthenticated = conn.authenticateWithPassword(username, password); if (!isAuthenticated) { System.err.println("authentication failed"); } SCPClient client = new SCPClient(conn); if (StringUtils.isBlank(mode)) { mode = "0600"; } if (remoteFileName == null) { client.put(localFile, remoteTargetDirectory); } else { client.put(localFile, remoteFileName, remoteTargetDirectory, mode); } } catch (IOException ex) { Logger.getLogger(ScpClientUtil.class.getName()).log(Level.SEVERE, null, ex); }finally{ conn.close(); } } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/util/SqlUtils.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.util; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.util.StringUtils; import lombok.extern.slf4j.Slf4j; import me.zhengjie.modules.maint.domain.enums.DataTypeEnum; import me.zhengjie.utils.CloseUtil; import javax.sql.DataSource; import java.io.BufferedReader; import java.io.File; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; /** * @author / */ @Slf4j public class SqlUtils { /** * 获取数据源 * * @param jdbcUrl / * @param userName / * @param password / * @return DataSource */ private static DataSource getDataSource(String jdbcUrl, String userName, String password) { DruidDataSource druidDataSource = new DruidDataSource(); String className; try { className = DriverManager.getDriver(jdbcUrl.trim()).getClass().getName(); } catch (SQLException e) { throw new RuntimeException("Get class name error: =" + jdbcUrl); } if (StringUtils.isEmpty(className)) { DataTypeEnum dataTypeEnum = DataTypeEnum.urlOf(jdbcUrl); if (null == dataTypeEnum) { throw new RuntimeException("Not supported data type: jdbcUrl=" + jdbcUrl); } druidDataSource.setDriverClassName(dataTypeEnum.getDriver()); } else { druidDataSource.setDriverClassName(className); } // 去掉不安全的参数 jdbcUrl = sanitizeJdbcUrl(jdbcUrl); druidDataSource.setUrl(jdbcUrl); druidDataSource.setUsername(userName); druidDataSource.setPassword(password); // 配置获取连接等待超时的时间 druidDataSource.setMaxWait(3000); // 配置初始化大小、最小、最大 druidDataSource.setInitialSize(1); druidDataSource.setMinIdle(1); druidDataSource.setMaxActive(1); // 如果链接出现异常则直接判定为失败而不是一直重试 druidDataSource.setBreakAfterAcquireFailure(true); try { druidDataSource.init(); } catch (SQLException e) { log.error("Exception during pool initialization", e); throw new RuntimeException(e.getMessage()); } return druidDataSource; } private static Connection getConnection(String jdbcUrl, String userName, String password) { DataSource dataSource = getDataSource(jdbcUrl, userName, password); Connection connection = null; try { connection = dataSource.getConnection(); } catch (Exception ignored) {} try { int timeOut = 5; if (null == connection || connection.isClosed() || !connection.isValid(timeOut)) { log.info("connection is closed or invalid, retry get connection!"); connection = dataSource.getConnection(); } } catch (Exception e) { log.error("create connection error, jdbcUrl: {}", jdbcUrl); throw new RuntimeException("create connection error, jdbcUrl: " + jdbcUrl); } finally { CloseUtil.close(connection); } return connection; } private static void releaseConnection(Connection connection) { if (null != connection) { try { connection.close(); } catch (Exception e) { log.error(e.getMessage(),e); } } } public static boolean testConnection(String jdbcUrl, String userName, String password) { Connection connection = null; try { connection = getConnection(jdbcUrl, userName, password); if (null != connection) { return true; } } catch (Exception e) { log.error("Get connection failed:{}", e.getMessage()); } finally { releaseConnection(connection); } return false; } public static String executeFile(String jdbcUrl, String userName, String password, File sqlFile) { Connection connection = getConnection(jdbcUrl, userName, password); try { batchExecute(connection, readSqlList(sqlFile)); } catch (Exception e) { log.error("sql脚本执行发生异常:{}",e.getMessage()); return e.getMessage(); }finally { releaseConnection(connection); } return "success"; } /** * 批量执行sql * @param connection / * @param sqlList / */ public static void batchExecute(Connection connection, List sqlList) { try (Statement st = connection.createStatement()) { for (String sql : sqlList) { // 去除末尾的分号 if (sql.endsWith(";")) { sql = sql.substring(0, sql.length() - 1); } // 检查 SQL 语句是否为空 if (!sql.trim().isEmpty()) { st.addBatch(sql); } } st.executeBatch(); } catch (SQLException e) { log.error("SQL脚本批量执行发生异常: {},错误代码: {}", e.getMessage(), e.getErrorCode()); } } /** * 将文件中的sql语句以;为单位读取到列表中 * @param sqlFile / * @return / */ private static List readSqlList(File sqlFile) { List sqlList = new ArrayList<>(); StringBuilder sb = new StringBuilder(); try (BufferedReader reader = Files.newBufferedReader(sqlFile.toPath(), StandardCharsets.UTF_8)) { String line; while ((line = reader.readLine()) != null) { log.info("line: {}", line); sb.append(line.trim()); if (line.trim().endsWith(";")) { sqlList.add(sb.toString()); // 清空 StringBuilder sb.setLength(0); } else { // 在行之间加一个空格 sb.append(" "); } } if (sb.length() > 0) { sqlList.add(sb.toString().trim()); } } catch (Exception e) { log.error("读取SQL文件时发生异常: {}", e.getMessage()); } return sqlList; } /** * 去除不安全的参数 * @param jdbcUrl / * @return / */ private static String sanitizeJdbcUrl(String jdbcUrl) { // 定义不安全参数和其安全替代值 String[][] unsafeParams = { // allowLoadLocalInfile:允许使用 LOAD DATA LOCAL INFILE,可能导致文件泄露 {"allowLoadLocalInfile", "false"}, // allowUrlInLocalInfile:允许在 LOAD DATA LOCAL INFILE 中使用 URL,可能导致未经授权的文件访问 {"allowUrlInLocalInfile", "false"}, // autoDeserialize:允许自动反序列化对象,可能导致反序列化漏洞 {"autoDeserialize", "false"}, // allowNanAndInf:允许使用 NaN 和 Infinity 作为数字值,可能导致不一致的数据处理 {"allowNanAndInf", "false"}, // allowMultiQueries:允许在一个语句中执行多个查询,可能导致 SQL 注入攻击 {"allowMultiQueries", "false"}, // allowPublicKeyRetrieval:允许从服务器检索公钥,可能导致中间人攻击 {"allowPublicKeyRetrieval", "false"} }; // 替换不安全的参数 for (String[] param : unsafeParams) { jdbcUrl = jdbcUrl.replaceAll("(?i)" + param[0] + "=true", param[0] + "=" + param[1]); } return jdbcUrl; } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/websocket/MsgType.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.websocket; /** * @author ZhangHouYing * @date 2019-08-10 9:56 */ public enum MsgType { /** 连接 */ CONNECT, /** 关闭 */ CLOSE, /** 信息 */ INFO, /** 错误 */ ERROR } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/websocket/SocketMsg.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.websocket; import lombok.Data; /** * @author ZhangHouYing * @date 2019-08-10 9:55 */ @Data public class SocketMsg { private String msg; private MsgType msgType; public SocketMsg(String msg, MsgType msgType) { this.msg = msg; this.msgType = msgType; } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/maint/websocket/WebSocketServer.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.maint.websocket; import com.alibaba.fastjson2.JSON; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.Objects; import java.util.concurrent.CopyOnWriteArraySet; /** * @author ZhangHouYing * @date 2019-08-10 15:46 */ @ServerEndpoint("/webSocket/{sid}") @Slf4j @Component public class WebSocketServer { /** * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。 */ private static final CopyOnWriteArraySet webSocketSet = new CopyOnWriteArraySet<>(); /** * 与某个客户端的连接会话,需要通过它来给客户端发送数据 */ private Session session; /** * 接收sid */ private String sid=""; /** * 连接建立成功调用的方法 * */ @OnOpen public void onOpen(Session session,@PathParam("sid") String sid) { this.session = session; //如果存在就先删除一个,防止重复推送消息 webSocketSet.removeIf(webSocket -> webSocket.sid.equals(sid)); webSocketSet.add(this); this.sid=sid; } /** * 连接关闭调用的方法 */ @OnClose public void onClose() { webSocketSet.remove(this); } /** * 收到客户端消息后调用的方法 * @param message 客户端发送过来的消息*/ @OnMessage public void onMessage(String message, Session session) { log.info("收到来"+sid+"的信息:"+message); //群发消息 for (WebSocketServer item : webSocketSet) { try { item.sendMessage(message); } catch (IOException e) { log.error(e.getMessage(),e); } } } @OnError public void onError(Session session, Throwable error) { log.error("发生错误", error); } /** * 实现服务器主动推送 */ private void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); } /** * 群发自定义消息 * */ public static void sendInfo(SocketMsg socketMsg,@PathParam("sid") String sid) throws IOException { String message = JSON.toJSONString(socketMsg); log.info("推送消息到"+sid+",推送内容:"+message); for (WebSocketServer item : webSocketSet) { try { //这里可以设定只推送给这个sid的,为null则全部推送 if(sid==null) { item.sendMessage(message); }else if(item.sid.equals(sid)){ item.sendMessage(message); } } catch (IOException ignored) { } } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } WebSocketServer that = (WebSocketServer) o; return Objects.equals(session, that.session) && Objects.equals(sid, that.sid); } @Override public int hashCode() { return Objects.hash(session, sid); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/quartz/config/JobRunner.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.quartz.config; import lombok.RequiredArgsConstructor; import me.zhengjie.modules.quartz.domain.QuartzJob; import me.zhengjie.modules.quartz.repository.QuartzJobRepository; import me.zhengjie.modules.quartz.utils.QuartzManage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; import java.util.List; /** * @author Zheng Jie * @date 2019-01-07 */ @Component @RequiredArgsConstructor public class JobRunner implements ApplicationRunner { private static final Logger log = LoggerFactory.getLogger(JobRunner.class); private final QuartzJobRepository quartzJobRepository; private final QuartzManage quartzManage; /** * 项目启动时重新激活启用的定时任务 * * @param applicationArguments / */ @Override public void run(ApplicationArguments applicationArguments) { List quartzJobs = quartzJobRepository.findByIsPauseIsFalse(); quartzJobs.forEach(quartzManage::addJob); log.info("Timing task injection complete"); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/quartz/config/QuartzConfig.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.quartz.config; import lombok.extern.slf4j.Slf4j; import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; import org.springframework.lang.NonNull; import org.springframework.scheduling.quartz.AdaptableJobFactory; import org.springframework.stereotype.Component; /** * 定时任务配置 * @author / * @date 2019-01-07 */ @Slf4j @Configuration @Scope("singleton") public class QuartzConfig { /** * 解决Job中注入Spring Bean为null的问题 */ @Component("quartzJobFactory") public static class QuartzJobFactory extends AdaptableJobFactory { private final AutowireCapableBeanFactory capableBeanFactory; @Autowired public QuartzJobFactory(AutowireCapableBeanFactory capableBeanFactory) { this.capableBeanFactory = capableBeanFactory; } @NonNull @Override protected Object createJobInstance(@NonNull TriggerFiredBundle bundle) throws Exception { try { // 调用父类的方法,把Job注入到spring中 Object jobInstance = super.createJobInstance(bundle); capableBeanFactory.autowireBean(jobInstance); log.debug("Job instance created and autowired: {}", jobInstance.getClass().getName()); return jobInstance; } catch (Exception e) { log.error("Error creating job instance for bundle: {}", bundle, e); throw e; } } } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/quartz/domain/QuartzJob.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.quartz.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseEntity; import javax.persistence.*; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.io.Serializable; /** * @author Zheng Jie * @date 2019-01-07 */ @Getter @Setter @Entity @Table(name = "sys_quartz_job") public class QuartzJob extends BaseEntity implements Serializable { public static final String JOB_KEY = "JOB_KEY"; @Id @Column(name = "job_id") @NotNull(groups = {Update.class}) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Transient @ApiModelProperty(value = "用于子任务唯一标识", hidden = true) private String uuid; @ApiModelProperty(value = "定时器名称") private String jobName; @NotBlank @ApiModelProperty(value = "Bean名称") private String beanName; @NotBlank @ApiModelProperty(value = "方法名称") private String methodName; @ApiModelProperty(value = "参数") private String params; @NotBlank @ApiModelProperty(value = "cron表达式") private String cronExpression; @ApiModelProperty(value = "状态,暂时或启动") private Boolean isPause = false; @ApiModelProperty(value = "负责人") private String personInCharge; @ApiModelProperty(value = "报警邮箱") private String email; @ApiModelProperty(value = "子任务") private String subTask; @ApiModelProperty(value = "失败后暂停") private Boolean pauseAfterFailure; @NotBlank @ApiModelProperty(value = "备注") private String description; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/quartz/domain/QuartzLog.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.quartz.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.hibernate.annotations.CreationTimestamp; import javax.persistence.*; import java.io.Serializable; import java.sql.Timestamp; /** * @author Zheng Jie * @date 2019-01-07 */ @Entity @Data @Table(name = "sys_quartz_log") public class QuartzLog implements Serializable { @Id @Column(name = "log_id") @ApiModelProperty(value = "ID", hidden = true) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ApiModelProperty(value = "任务名称", hidden = true) private String jobName; @ApiModelProperty(value = "bean名称", hidden = true) private String beanName; @ApiModelProperty(value = "方法名称", hidden = true) private String methodName; @ApiModelProperty(value = "参数", hidden = true) private String params; @ApiModelProperty(value = "cron表达式", hidden = true) private String cronExpression; @ApiModelProperty(value = "状态", hidden = true) private Boolean isSuccess; @ApiModelProperty(value = "异常详情", hidden = true) private String exceptionDetail; @ApiModelProperty(value = "执行耗时", hidden = true) private Long time; @CreationTimestamp @ApiModelProperty(value = "创建时间", hidden = true) private Timestamp createTime; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/quartz/repository/QuartzJobRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.quartz.repository; import me.zhengjie.modules.quartz.domain.QuartzJob; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import java.util.List; /** * @author Zheng Jie * @date 2019-01-07 */ public interface QuartzJobRepository extends JpaRepository, JpaSpecificationExecutor { /** * 查询启用的任务 * @return List */ List findByIsPauseIsFalse(); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/quartz/repository/QuartzLogRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.quartz.repository; import me.zhengjie.modules.quartz.domain.QuartzLog; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * @author Zheng Jie * @date 2019-01-07 */ public interface QuartzLogRepository extends JpaRepository, JpaSpecificationExecutor { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/quartz/rest/QuartzJobController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.quartz.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.zhengjie.annotation.Log; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.quartz.domain.QuartzJob; import me.zhengjie.modules.quartz.domain.QuartzLog; import me.zhengjie.modules.quartz.service.QuartzJobService; import me.zhengjie.modules.quartz.service.dto.JobQueryCriteria; import me.zhengjie.utils.PageResult; import me.zhengjie.utils.SpringBeanHolder; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Set; /** * @author Zheng Jie * @date 2019-01-07 */ @Slf4j @RestController @RequiredArgsConstructor @RequestMapping("/api/jobs") @Api(tags = "系统:定时任务管理") public class QuartzJobController { private static final String ENTITY_NAME = "quartzJob"; private final QuartzJobService quartzJobService; @ApiOperation("查询定时任务") @GetMapping @PreAuthorize("@el.check('timing:list')") public ResponseEntity> queryQuartzJob(JobQueryCriteria criteria, Pageable pageable){ return new ResponseEntity<>(quartzJobService.queryAll(criteria,pageable), HttpStatus.OK); } @ApiOperation("导出任务数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('timing:list')") public void exportQuartzJob(HttpServletResponse response, JobQueryCriteria criteria) throws IOException { quartzJobService.download(quartzJobService.queryAll(criteria), response); } @ApiOperation("导出日志数据") @GetMapping(value = "/logs/download") @PreAuthorize("@el.check('timing:list')") public void exportQuartzJobLog(HttpServletResponse response, JobQueryCriteria criteria) throws IOException { quartzJobService.downloadLog(quartzJobService.queryAllLog(criteria), response); } @ApiOperation("查询任务执行日志") @GetMapping(value = "/logs") @PreAuthorize("@el.check('timing:list')") public ResponseEntity> queryQuartzJobLog(JobQueryCriteria criteria, Pageable pageable){ return new ResponseEntity<>(quartzJobService.queryAllLog(criteria,pageable), HttpStatus.OK); } @Log("新增定时任务") @ApiOperation("新增定时任务") @PostMapping @PreAuthorize("@el.check('timing:add')") public ResponseEntity createQuartzJob(@Validated @RequestBody QuartzJob resources){ if (resources.getId() != null) { throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); } // 验证Bean是不是合法的,合法的定时任务 Bean 需要用 @Service 定义 checkBean(resources.getBeanName()); quartzJobService.create(resources); return new ResponseEntity<>(HttpStatus.CREATED); } @Log("修改定时任务") @ApiOperation("修改定时任务") @PutMapping @PreAuthorize("@el.check('timing:edit')") public ResponseEntity updateQuartzJob(@Validated(QuartzJob.Update.class) @RequestBody QuartzJob resources){ // 验证Bean是不是合法的,合法的定时任务 Bean 需要用 @Service 定义 checkBean(resources.getBeanName()); quartzJobService.update(resources); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("更改定时任务状态") @ApiOperation("更改定时任务状态") @PutMapping(value = "/{id}") @PreAuthorize("@el.check('timing:edit')") public ResponseEntity updateQuartzJobStatus(@PathVariable Long id){ quartzJobService.updateIsPause(quartzJobService.findById(id)); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("执行定时任务") @ApiOperation("执行定时任务") @PutMapping(value = "/exec/{id}") @PreAuthorize("@el.check('timing:edit')") public ResponseEntity executionQuartzJob(@PathVariable Long id){ quartzJobService.execution(quartzJobService.findById(id)); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("删除定时任务") @ApiOperation("删除定时任务") @DeleteMapping @PreAuthorize("@el.check('timing:del')") public ResponseEntity deleteQuartzJob(@RequestBody Set ids){ quartzJobService.delete(ids); return new ResponseEntity<>(HttpStatus.OK); } private void checkBean(String beanName){ // 避免调用攻击者可以从SpringContextHolder获得控制jdbcTemplate类 // 并使用getDeclaredMethod调用jdbcTemplate的queryForMap函数,执行任意sql命令。 if(!SpringBeanHolder.getAllServiceBeanName().contains(beanName)){ throw new BadRequestException("非法的 Bean,请重新输入!"); } } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/quartz/service/QuartzJobService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.quartz.service; import me.zhengjie.modules.quartz.domain.QuartzJob; import me.zhengjie.modules.quartz.domain.QuartzLog; import me.zhengjie.modules.quartz.service.dto.JobQueryCriteria; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Set; /** * @author Zheng Jie * @date 2019-01-07 */ public interface QuartzJobService { /** * 分页查询 * @param criteria 条件 * @param pageable 分页参数 * @return / */ PageResult queryAll(JobQueryCriteria criteria, Pageable pageable); /** * 查询全部 * @param criteria 条件 * @return / */ List queryAll(JobQueryCriteria criteria); /** * 分页查询日志 * @param criteria 条件 * @param pageable 分页参数 * @return / */ PageResult queryAllLog(JobQueryCriteria criteria, Pageable pageable); /** * 查询全部 * @param criteria 条件 * @return / */ List queryAllLog(JobQueryCriteria criteria); /** * 创建 * @param resources / */ void create(QuartzJob resources); /** * 编辑 * @param resources / */ void update(QuartzJob resources); /** * 删除任务 * @param ids / */ void delete(Set ids); /** * 根据ID查询 * @param id ID * @return / */ QuartzJob findById(Long id); /** * 更改定时任务状态 * @param quartzJob / */ void updateIsPause(QuartzJob quartzJob); /** * 立即执行定时任务 * @param quartzJob / */ void execution(QuartzJob quartzJob); /** * 导出定时任务 * @param queryAll 待导出的数据 * @param response / * @throws IOException / */ void download(List queryAll, HttpServletResponse response) throws IOException; /** * 导出定时任务日志 * @param queryAllLog 待导出的数据 * @param response / * @throws IOException / */ void downloadLog(List queryAllLog, HttpServletResponse response) throws IOException; /** * 执行子任务 * @param tasks / * @throws InterruptedException / */ void executionSubJob(String[] tasks) throws InterruptedException; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/quartz/service/dto/JobQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.quartz.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import me.zhengjie.annotation.Query; import java.sql.Timestamp; import java.util.List; /** * @author Zheng Jie * @date 2019-6-4 10:33:02 */ @Data public class JobQueryCriteria { @ApiModelProperty(value = "任务名称") @Query(type = Query.Type.INNER_LIKE) private String jobName; @Query @ApiModelProperty(value = "是否成功") private Boolean isSuccess; @ApiModelProperty(value = "创建时间") @Query(type = Query.Type.BETWEEN) private List createTime; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/quartz/service/impl/QuartzJobServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.quartz.service.impl; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.quartz.domain.QuartzJob; import me.zhengjie.modules.quartz.domain.QuartzLog; import me.zhengjie.modules.quartz.repository.QuartzJobRepository; import me.zhengjie.modules.quartz.repository.QuartzLogRepository; import me.zhengjie.modules.quartz.service.QuartzJobService; import me.zhengjie.modules.quartz.service.dto.JobQueryCriteria; import me.zhengjie.modules.quartz.utils.QuartzManage; import me.zhengjie.utils.*; import org.quartz.CronExpression; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; /** * @author Zheng Jie * @date 2019-01-07 */ @RequiredArgsConstructor @Service(value = "quartzJobService") public class QuartzJobServiceImpl implements QuartzJobService { private final QuartzJobRepository quartzJobRepository; private final QuartzLogRepository quartzLogRepository; private final QuartzManage quartzManage; private final RedisUtils redisUtils; @Override public PageResult queryAll(JobQueryCriteria criteria, Pageable pageable){ return PageUtil.toPage(quartzJobRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable)); } @Override public PageResult queryAllLog(JobQueryCriteria criteria, Pageable pageable){ return PageUtil.toPage(quartzLogRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable)); } @Override public List queryAll(JobQueryCriteria criteria) { return quartzJobRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)); } @Override public List queryAllLog(JobQueryCriteria criteria) { return quartzLogRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)); } @Override public QuartzJob findById(Long id) { QuartzJob quartzJob = quartzJobRepository.findById(id).orElseGet(QuartzJob::new); ValidationUtil.isNull(quartzJob.getId(),"QuartzJob","id",id); return quartzJob; } @Override @Transactional(rollbackFor = Exception.class) public void create(QuartzJob resources) { if (!CronExpression.isValidExpression(resources.getCronExpression())){ throw new BadRequestException("cron表达式格式错误"); } resources = quartzJobRepository.save(resources); quartzManage.addJob(resources); } @Override @Transactional(rollbackFor = Exception.class) public void update(QuartzJob resources) { if (!CronExpression.isValidExpression(resources.getCronExpression())){ throw new BadRequestException("cron表达式格式错误"); } if(StringUtils.isNotBlank(resources.getSubTask())){ List tasks = Arrays.asList(resources.getSubTask().split("[,,]")); if (tasks.contains(resources.getId().toString())) { throw new BadRequestException("子任务中不能添加当前任务ID"); } } resources = quartzJobRepository.save(resources); quartzManage.updateJobCron(resources); } @Override public void updateIsPause(QuartzJob quartzJob) { // 置换暂停状态 if (quartzJob.getIsPause()) { quartzManage.resumeJob(quartzJob); quartzJob.setIsPause(false); } else { quartzManage.pauseJob(quartzJob); quartzJob.setIsPause(true); } quartzJobRepository.save(quartzJob); } @Override public void execution(QuartzJob quartzJob) { quartzManage.runJobNow(quartzJob); } @Override @Transactional(rollbackFor = Exception.class) public void delete(Set ids) { for (Long id : ids) { QuartzJob quartzJob = findById(id); quartzManage.deleteJob(quartzJob); quartzJobRepository.delete(quartzJob); } } @Override @Transactional(rollbackFor = Exception.class) public void executionSubJob(String[] tasks) throws InterruptedException { for (String id : tasks) { if (StrUtil.isBlank(id)) { // 如果是手动清除子任务id,会出现id为空字符串的问题 continue; } QuartzJob quartzJob = findById(Long.parseLong(id)); // 执行任务 String uuid = IdUtil.simpleUUID(); quartzJob.setUuid(uuid); // 执行任务 execution(quartzJob); // 获取执行状态,如果执行失败则停止后面的子任务执行 Boolean result = redisUtils.get(uuid, Boolean.class); while (result == null) { // 休眠5秒,再次获取子任务执行情况 Thread.sleep(5000); result = redisUtils.get(uuid, Boolean.class); } if(!result){ redisUtils.del(uuid); break; } } } @Override public void download(List quartzJobs, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (QuartzJob quartzJob : quartzJobs) { Map map = new LinkedHashMap<>(); map.put("任务名称", quartzJob.getJobName()); map.put("Bean名称", quartzJob.getBeanName()); map.put("执行方法", quartzJob.getMethodName()); map.put("参数", quartzJob.getParams()); map.put("表达式", quartzJob.getCronExpression()); map.put("状态", quartzJob.getIsPause() ? "暂停中" : "运行中"); map.put("描述", quartzJob.getDescription()); map.put("创建日期", quartzJob.getCreateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } @Override public void downloadLog(List queryAllLog, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (QuartzLog quartzLog : queryAllLog) { Map map = new LinkedHashMap<>(); map.put("任务名称", quartzLog.getJobName()); map.put("Bean名称", quartzLog.getBeanName()); map.put("执行方法", quartzLog.getMethodName()); map.put("参数", quartzLog.getParams()); map.put("表达式", quartzLog.getCronExpression()); map.put("异常详情", quartzLog.getExceptionDetail()); map.put("耗时/毫秒", quartzLog.getTime()); map.put("状态", quartzLog.getIsSuccess() ? "成功" : "失败"); map.put("创建日期", quartzLog.getCreateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/quartz/task/TestTask.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.quartz.task; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** * 测试用 * @author Zheng Jie * @date 2019-01-08 */ @Slf4j @Service public class TestTask { public void run(){ log.info("run 执行成功"); } public void run1(String str){ log.info("run1 执行成功,参数为: {}", str); } public void run2(){ log.info("run2 执行成功"); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/quartz/utils/ExecutionJob.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.quartz.utils; import cn.hutool.extra.template.Template; import cn.hutool.extra.template.TemplateConfig; import cn.hutool.extra.template.TemplateEngine; import cn.hutool.extra.template.TemplateUtil; import me.zhengjie.domain.vo.EmailVo; import me.zhengjie.modules.quartz.domain.QuartzJob; import me.zhengjie.modules.quartz.domain.QuartzLog; import me.zhengjie.modules.quartz.repository.QuartzLogRepository; import me.zhengjie.modules.quartz.service.QuartzJobService; import me.zhengjie.service.EmailService; import me.zhengjie.utils.RedisUtils; import me.zhengjie.utils.SpringBeanHolder; import me.zhengjie.utils.StringUtils; import me.zhengjie.utils.ThrowableUtil; import org.quartz.JobExecutionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.quartz.QuartzJobBean; import java.util.*; import java.util.concurrent.*; /** * 参考人人开源,... * @author / * @date 2019-01-07 */ public class ExecutionJob extends QuartzJobBean { private final Logger logger = LoggerFactory.getLogger(this.getClass()); // 此处仅供参考,可根据任务执行情况自定义线程池参数 private final ThreadPoolTaskExecutor executor = SpringBeanHolder.getBean("taskAsync"); @Override public void executeInternal(JobExecutionContext context) { // 获取任务 QuartzJob quartzJob = (QuartzJob) context.getMergedJobDataMap().get(QuartzJob.JOB_KEY); // 获取spring bean QuartzLogRepository quartzLogRepository = SpringBeanHolder.getBean(QuartzLogRepository.class); QuartzJobService quartzJobService = SpringBeanHolder.getBean(QuartzJobService.class); RedisUtils redisUtils = SpringBeanHolder.getBean(RedisUtils.class); String uuid = quartzJob.getUuid(); QuartzLog log = new QuartzLog(); log.setJobName(quartzJob.getJobName()); log.setBeanName(quartzJob.getBeanName()); log.setMethodName(quartzJob.getMethodName()); log.setParams(quartzJob.getParams()); long startTime = System.currentTimeMillis(); log.setCronExpression(quartzJob.getCronExpression()); try { // 执行任务 QuartzRunnable task = new QuartzRunnable(quartzJob.getBeanName(), quartzJob.getMethodName(), quartzJob.getParams()); Future future = executor.submit(task); future.get(); long times = System.currentTimeMillis() - startTime; log.setTime(times); if(StringUtils.isNotBlank(uuid)) { redisUtils.set(uuid, true); } // 任务状态 log.setIsSuccess(true); logger.info("任务执行成功,任务名称:{}, 执行时间:{}毫秒", quartzJob.getJobName(), times); // 判断是否存在子任务 if(StringUtils.isNotBlank(quartzJob.getSubTask())){ String[] tasks = quartzJob.getSubTask().split("[,,]"); // 执行子任务 quartzJobService.executionSubJob(tasks); } } catch (Exception e) { if(StringUtils.isNotBlank(uuid)) { redisUtils.set(uuid, false); } logger.error("任务执行失败,任务名称:{}", quartzJob.getJobName()); long times = System.currentTimeMillis() - startTime; log.setTime(times); // 任务状态 0:成功 1:失败 log.setIsSuccess(false); log.setExceptionDetail(ThrowableUtil.getStackTrace(e)); // 任务如果失败了则暂停 if(quartzJob.getPauseAfterFailure() != null && quartzJob.getPauseAfterFailure()){ //更新状态 quartzJob.setIsPause(false); quartzJobService.updateIsPause(quartzJob); } if(quartzJob.getEmail() != null){ EmailService emailService = SpringBeanHolder.getBean(EmailService.class); // 邮箱报警 if(StringUtils.isNoneBlank(quartzJob.getEmail())){ EmailVo emailVo = taskAlarm(quartzJob, ThrowableUtil.getStackTrace(e)); emailService.send(emailVo, emailService.find()); } } } finally { quartzLogRepository.save(log); } } private EmailVo taskAlarm(QuartzJob quartzJob, String msg) { EmailVo emailVo = new EmailVo(); emailVo.setSubject("定时任务【"+ quartzJob.getJobName() +"】执行失败,请尽快处理!"); Map data = new HashMap<>(16); data.put("task", quartzJob); data.put("msg", msg); TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH)); Template template = engine.getTemplate("taskAlarm.ftl"); emailVo.setContent(template.render(data)); List emails = Arrays.asList(quartzJob.getEmail().split("[,,]")); emailVo.setTos(emails); return emailVo; } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/quartz/utils/QuartzManage.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.quartz.utils; import lombok.extern.slf4j.Slf4j; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.quartz.domain.QuartzJob; import org.quartz.*; import org.quartz.impl.triggers.CronTriggerImpl; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Date; import static org.quartz.TriggerBuilder.newTrigger; /** * @author Zheng Jie * @date 2019-01-07 */ @Slf4j @Component public class QuartzManage { private static final String JOB_NAME = "TASK_"; @Resource private Scheduler scheduler; public void addJob(QuartzJob quartzJob){ try { // 构建job信息 JobDetail jobDetail = JobBuilder.newJob(ExecutionJob.class). withIdentity(JOB_NAME + quartzJob.getId()).build(); //通过触发器名和cron 表达式创建 Trigger Trigger cronTrigger = newTrigger() .withIdentity(JOB_NAME + quartzJob.getId()) .startNow() .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression())) .build(); cronTrigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob); //重置启动时间 ((CronTriggerImpl)cronTrigger).setStartTime(new Date()); //执行定时任务,如果是持久化的,这里会报错,捕获输出 try { scheduler.scheduleJob(jobDetail,cronTrigger); } catch (ObjectAlreadyExistsException e) { log.warn("定时任务已存在,跳过加载"); } // 暂停任务 if (quartzJob.getIsPause()) { pauseJob(quartzJob); } } catch (Exception e){ log.error("创建定时任务失败", e); throw new BadRequestException("创建定时任务失败"); } } /** * 更新job cron表达式 * @param quartzJob / */ public void updateJobCron(QuartzJob quartzJob){ try { TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); // 如果不存在则创建一个定时任务 if(trigger == null){ addJob(quartzJob); trigger = (CronTrigger) scheduler.getTrigger(triggerKey); } CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression()); trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); //重置启动时间 ((CronTriggerImpl)trigger).setStartTime(new Date()); trigger.getJobDataMap().put(QuartzJob.JOB_KEY,quartzJob); scheduler.rescheduleJob(triggerKey, trigger); // 暂停任务 if (quartzJob.getIsPause()) { pauseJob(quartzJob); } } catch (Exception e){ log.error("更新定时任务失败", e); throw new BadRequestException("更新定时任务失败"); } } /** * 删除一个job * @param quartzJob / */ public void deleteJob(QuartzJob quartzJob){ try { JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); scheduler.pauseJob(jobKey); scheduler.deleteJob(jobKey); } catch (Exception e){ log.error("删除定时任务失败", e); throw new BadRequestException("删除定时任务失败"); } } /** * 恢复一个job * @param quartzJob / */ public void resumeJob(QuartzJob quartzJob){ try { TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); // 如果不存在则创建一个定时任务 if(trigger == null) { addJob(quartzJob); } JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); scheduler.resumeJob(jobKey); } catch (Exception e){ log.error("恢复定时任务失败", e); throw new BadRequestException("恢复定时任务失败"); } } /** * 立即执行job * @param quartzJob / */ public void runJobNow(QuartzJob quartzJob){ try { TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); // 如果不存在则创建一个定时任务 if(trigger == null) { addJob(quartzJob); } JobDataMap dataMap = new JobDataMap(); dataMap.put(QuartzJob.JOB_KEY, quartzJob); JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); scheduler.triggerJob(jobKey,dataMap); } catch (Exception e){ log.error("定时任务执行失败", e); throw new BadRequestException("定时任务执行失败"); } } /** * 暂停一个job * @param quartzJob / */ public void pauseJob(QuartzJob quartzJob){ try { JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); scheduler.pauseJob(jobKey); } catch (Exception e){ log.error("定时任务暂停失败", e); throw new BadRequestException("定时任务暂停失败"); } } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/quartz/utils/QuartzRunnable.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.quartz.utils; import lombok.extern.slf4j.Slf4j; import me.zhengjie.utils.SpringBeanHolder; import org.apache.commons.lang3.StringUtils; import org.springframework.util.ReflectionUtils; import java.lang.reflect.Method; import java.util.concurrent.Callable; /** * 执行定时任务 * @author / */ @Slf4j public class QuartzRunnable implements Callable { private final Object target; private final Method method; private final String params; QuartzRunnable(String beanName, String methodName, String params) throws NoSuchMethodException, SecurityException { this.target = SpringBeanHolder.getBean(beanName); this.params = params; if (StringUtils.isNotBlank(params)) { this.method = target.getClass().getDeclaredMethod(methodName, String.class); } else { this.method = target.getClass().getDeclaredMethod(methodName); } } @Override @SuppressWarnings({"unchecked","all"}) public Object call() throws Exception { ReflectionUtils.makeAccessible(method); if (StringUtils.isNotBlank(params)) { method.invoke(target, params); } else { method.invoke(target); } return null; } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/config/CaptchaConfig.java ================================================ /* * Copyright 2019-2020 the original author or authors. * * Licensed under the Apache License, Version loginCode.length.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-loginCode.length.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.config; import com.wf.captcha.*; import com.wf.captcha.base.Captcha; import lombok.Data; import lombok.Getter; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.security.config.enums.LoginCodeEnum; import me.zhengjie.utils.StringUtils; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import java.awt.*; /** * 登录验证码配置信息 * @author liaojinlong * @date 2025-01-13 */ @Data @Configuration @ConfigurationProperties(prefix = "login.code") public class CaptchaConfig { /** * 验证码配置 */ @Getter private LoginCodeEnum codeType; /** * 验证码有效期 分钟 */ private Long expiration = 5L; /** * 验证码内容长度 */ private int length = 4; /** * 验证码宽度 */ private int width = 111; /** * 验证码高度 */ private int height = 36; /** * 验证码字体 */ private String fontName; /** * 字体大小 */ private int fontSize = 25; /** * 依据配置信息生产验证码 * @return / */ public Captcha getCaptcha() { Captcha captcha; switch (codeType) { case ARITHMETIC: // 算术类型 https://gitee.com/whvse/EasyCaptcha captcha = new FixedArithmeticCaptcha(width, height); // 几位数运算,默认是两位 captcha.setLen(length); break; case CHINESE: captcha = new ChineseCaptcha(width, height); captcha.setLen(length); break; case CHINESE_GIF: captcha = new ChineseGifCaptcha(width, height); captcha.setLen(length); break; case GIF: captcha = new GifCaptcha(width, height); captcha.setLen(length); break; case SPEC: captcha = new SpecCaptcha(width, height); captcha.setLen(length); break; default: throw new BadRequestException("验证码配置信息错误!正确配置查看 LoginCodeEnum "); } if(StringUtils.isNotBlank(fontName)){ captcha.setFont(new Font(fontName, Font.PLAIN, fontSize)); } return captcha; } static class FixedArithmeticCaptcha extends ArithmeticCaptcha { public FixedArithmeticCaptcha(int width, int height) { super(width, height); } @Override protected char[] alphas() { // 生成随机数字和运算符 int n1 = num(1, 10), n2 = num(1, 10); int opt = num(3); // 计算结果 int res = new int[]{n1 + n2, n1 - n2, n1 * n2}[opt]; // 转换为字符运算符 char optChar = "+-x".charAt(opt); this.setArithmeticString(String.format("%s%c%s=?", n1, optChar, n2)); this.chars = String.valueOf(res); return chars.toCharArray(); } } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/config/LoginProperties.java ================================================ /* * Copyright 2019-2020 the original author or authors. * * Licensed under the Apache License, Version loginCode.length.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-loginCode.length.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; /** * 配置文件读取 * * @author liaojinlong * @date loginCode.length0loginCode.length0/6/10 17:loginCode.length6 */ @Data @Configuration @ConfigurationProperties(prefix = "login") public class LoginProperties { /** * 账号单用户 登录 */ private boolean singleLogin = false; public static final String cacheKey = "user-login-cache:"; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityProperties.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; /** * Jwt参数配置 * * @author Zheng Jie * @date 2019年11月28日 */ @Data @Configuration @ConfigurationProperties(prefix = "jwt") public class SecurityProperties { /** * Request Headers : Authorization */ private String header; /** * 令牌前缀,最后留个空格 Bearer */ private String tokenStartWith; /** * 必须使用最少88位的Base64对该令牌进行编码 */ private String base64Secret; /** * 令牌过期时间 此处单位/毫秒 */ private Long tokenValidityInSeconds; /** * 在线用户 key,根据 key 查询 redis 中在线用户的数据 */ private String onlineKey; /** * 验证码 key */ private String codeKey; /** * token 续期检查 */ private Long detect; /** * 续期时间 */ private Long renew; public String getTokenStartWith() { return tokenStartWith + " "; } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/config/SpringSecurityConfig.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.config; import lombok.RequiredArgsConstructor; import me.zhengjie.modules.security.security.*; import me.zhengjie.modules.security.service.OnlineUserService; import me.zhengjie.utils.AnonTagUtils; import me.zhengjie.utils.enums.RequestMethodEnum; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.web.filter.CorsFilter; import java.util.*; /** * @author Zheng Jie */ @Configuration @RequiredArgsConstructor @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class SpringSecurityConfig { private final TokenProvider tokenProvider; private final CorsFilter corsFilter; private final JwtAuthenticationEntryPoint authenticationErrorHandler; private final JwtAccessDeniedHandler jwtAccessDeniedHandler; private final ApplicationContext applicationContext; private final SecurityProperties properties; private final OnlineUserService onlineUserService; @Bean GrantedAuthorityDefaults grantedAuthorityDefaults() { // 去除 ROLE_ 前缀 return new GrantedAuthorityDefaults(""); } @Bean public PasswordEncoder passwordEncoder() { // 密码加密方式 return new BCryptPasswordEncoder(); } @Bean protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { // 获取匿名标记 Map> anonymousUrls = AnonTagUtils.getAnonymousUrl(applicationContext); return httpSecurity // 禁用 CSRF .csrf().disable() .addFilter(corsFilter) // 授权异常 .exceptionHandling() .authenticationEntryPoint(authenticationErrorHandler) .accessDeniedHandler(jwtAccessDeniedHandler) // 防止iframe 造成跨域 .and() .headers() .frameOptions() .disable() // 不创建会话 .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() // 静态资源等等 .antMatchers( HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/webSocket/**" ).permitAll() // swagger 文档 .antMatchers("/swagger-ui.html").permitAll() .antMatchers("/swagger-resources/**").permitAll() .antMatchers("/webjars/**").permitAll() .antMatchers("/*/api-docs").permitAll() // 文件 .antMatchers("/avatar/**").permitAll() .antMatchers("/file/**").permitAll() // 阿里巴巴 druid .antMatchers("/druid/**").permitAll() // 放行OPTIONS请求 .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // 自定义匿名访问所有url放行:允许匿名和带Token访问,细腻化到每个 Request 类型 // GET .antMatchers(HttpMethod.GET, anonymousUrls.get(RequestMethodEnum.GET.getType()).toArray(new String[0])).permitAll() // POST .antMatchers(HttpMethod.POST, anonymousUrls.get(RequestMethodEnum.POST.getType()).toArray(new String[0])).permitAll() // PUT .antMatchers(HttpMethod.PUT, anonymousUrls.get(RequestMethodEnum.PUT.getType()).toArray(new String[0])).permitAll() // PATCH .antMatchers(HttpMethod.PATCH, anonymousUrls.get(RequestMethodEnum.PATCH.getType()).toArray(new String[0])).permitAll() // DELETE .antMatchers(HttpMethod.DELETE, anonymousUrls.get(RequestMethodEnum.DELETE.getType()).toArray(new String[0])).permitAll() // 所有类型的接口都放行 .antMatchers(anonymousUrls.get(RequestMethodEnum.ALL.getType()).toArray(new String[0])).permitAll() // 所有请求都需要认证 .anyRequest().authenticated() .and().apply(securityConfigurerAdapter()) .and().build(); } private TokenConfigurer securityConfigurerAdapter() { return new TokenConfigurer(tokenProvider, properties, onlineUserService); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/config/enums/LoginCodeEnum.java ================================================ /* * Copyright 2019-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.config.enums; /** * 验证码配置枚举 * * @author liaojinlong * @date 2020/6/10 17:40 */ public enum LoginCodeEnum { /** * 算数 */ ARITHMETIC, /** * 中文 */ CHINESE, /** * 中文闪图 */ CHINESE_GIF, /** * 闪图 */ GIF, /** * 静态 */ SPEC } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.rest; import cn.hutool.core.util.IdUtil; import com.wf.captcha.base.Captcha; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.zhengjie.annotation.Log; import me.zhengjie.annotation.rest.AnonymousDeleteMapping; import me.zhengjie.annotation.rest.AnonymousGetMapping; import me.zhengjie.annotation.rest.AnonymousPostMapping; import me.zhengjie.config.properties.RsaProperties; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.security.config.CaptchaConfig; import me.zhengjie.modules.security.config.enums.LoginCodeEnum; import me.zhengjie.modules.security.config.LoginProperties; import me.zhengjie.modules.security.config.SecurityProperties; import me.zhengjie.modules.security.security.TokenProvider; import me.zhengjie.modules.security.service.UserDetailsServiceImpl; import me.zhengjie.modules.security.service.dto.AuthUserDto; import me.zhengjie.modules.security.service.dto.JwtUserDto; import me.zhengjie.modules.security.service.OnlineUserService; import me.zhengjie.utils.RsaUtils; import me.zhengjie.utils.RedisUtils; import me.zhengjie.utils.SecurityUtils; import me.zhengjie.utils.StringUtils; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; /** * @author Zheng Jie * @date 2018-11-23 * 授权、根据token获取用户详细信息 */ @Slf4j @RestController @RequestMapping("/auth") @RequiredArgsConstructor @Api(tags = "系统:系统授权接口") public class AuthController { private final SecurityProperties properties; private final RedisUtils redisUtils; private final OnlineUserService onlineUserService; private final TokenProvider tokenProvider; private final LoginProperties loginProperties; private final CaptchaConfig captchaConfig; private final PasswordEncoder passwordEncoder; private final UserDetailsServiceImpl userDetailsService; @Log("用户登录") @ApiOperation("登录授权") @AnonymousPostMapping(value = "/login") public ResponseEntity login(@Validated @RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception { // 密码解密 String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword()); // 查询验证码 String code = redisUtils.get(authUser.getUuid(), String.class); // 清除验证码 redisUtils.del(authUser.getUuid()); if (StringUtils.isBlank(code)) { throw new BadRequestException("验证码不存在或已过期"); } if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) { throw new BadRequestException("验证码错误"); } // 获取用户信息 JwtUserDto jwtUser = userDetailsService.loadUserByUsername(authUser.getUsername()); // 验证用户密码 if (!passwordEncoder.matches(password, jwtUser.getPassword())) { throw new BadRequestException("登录密码错误"); } Authentication authentication = new UsernamePasswordAuthenticationToken(jwtUser, null, jwtUser.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); // 生成令牌 String token = tokenProvider.createToken(jwtUser); // 返回 token 与 用户信息 Map authInfo = new HashMap(2) {{ put("token", properties.getTokenStartWith() + token); put("user", jwtUser); }}; if (loginProperties.isSingleLogin()) { // 踢掉之前已经登录的token onlineUserService.kickOutForUsername(authUser.getUsername()); } // 保存在线信息 onlineUserService.save(jwtUser, token, request); // 返回登录信息 return ResponseEntity.ok(authInfo); } @ApiOperation("获取用户信息") @GetMapping(value = "/info") public ResponseEntity getUserInfo() { JwtUserDto jwtUser = (JwtUserDto) SecurityUtils.getCurrentUser(); return ResponseEntity.ok(jwtUser); } @ApiOperation("获取验证码") @AnonymousGetMapping(value = "/code") public ResponseEntity getCode() { // 获取运算的结果 Captcha captcha = captchaConfig.getCaptcha(); String uuid = properties.getCodeKey() + IdUtil.simpleUUID(); //当验证码类型为 arithmetic时且长度 >= 2 时,captcha.text()的结果有几率为浮点型 String captchaValue = captcha.text(); if (captcha.getCharType() - 1 == LoginCodeEnum.ARITHMETIC.ordinal() && captchaValue.contains(".")) { captchaValue = captchaValue.split("\\.")[0]; } // 保存 redisUtils.set(uuid, captchaValue, captchaConfig.getExpiration(), TimeUnit.MINUTES); // 验证码信息 Map imgResult = new HashMap(2) {{ put("img", captcha.toBase64()); put("uuid", uuid); }}; return ResponseEntity.ok(imgResult); } @ApiOperation("退出登录") @AnonymousDeleteMapping(value = "/logout") public ResponseEntity logout(HttpServletRequest request) { onlineUserService.logout(tokenProvider.getToken(request)); return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/rest/OnlineController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.modules.security.service.OnlineUserService; import me.zhengjie.modules.security.service.dto.OnlineUserDto; import me.zhengjie.utils.EncryptUtils; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Set; /** * @author Zheng Jie */ @RestController @RequiredArgsConstructor @RequestMapping("/auth/online") @Api(tags = "系统:在线用户管理") public class OnlineController { private final OnlineUserService onlineUserService; @ApiOperation("查询在线用户") @GetMapping @PreAuthorize("@el.check()") public ResponseEntity> queryOnlineUser(String username, Pageable pageable){ return new ResponseEntity<>(onlineUserService.getAll(username, pageable),HttpStatus.OK); } @ApiOperation("导出数据") @GetMapping(value = "/download") @PreAuthorize("@el.check()") public void exportOnlineUser(HttpServletResponse response, String username) throws IOException { onlineUserService.download(onlineUserService.getAll(username), response); } @ApiOperation("踢出用户") @DeleteMapping @PreAuthorize("@el.check()") public ResponseEntity deleteOnlineUser(@RequestBody Set keys) throws Exception { for (String token : keys) { // 解密Key token = EncryptUtils.desDecrypt(token); onlineUserService.logout(token); } return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAccessDeniedHandler.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.security; import com.fasterxml.jackson.databind.ObjectMapper; import me.zhengjie.exception.handler.ApiError; import org.springframework.http.HttpStatus; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author Zheng Jie */ @Component public class JwtAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException { //当用户在没有授权的情况下访问受保护的REST资源时,将调用此方法发送403 Forbidden响应 response.setStatus(HttpStatus.FORBIDDEN.value()); response.setContentType("application/json;charset=UTF-8"); ObjectMapper objectMapper = new ObjectMapper(); String jsonResponse = objectMapper.writeValueAsString(ApiError.error(HttpStatus.FORBIDDEN.value(), "禁止访问,您没有权限访问此资源")); response.getWriter().write(jsonResponse); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.security; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import me.zhengjie.exception.handler.ApiError; import org.springframework.http.HttpStatus; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author Zheng Jie */ @Slf4j @Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { // 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应 int code = HttpStatus.UNAUTHORIZED.value(); response.setStatus(code); response.setContentType("application/json;charset=UTF-8"); ObjectMapper objectMapper = new ObjectMapper(); String jsonResponse = objectMapper.writeValueAsString(ApiError.error(HttpStatus.UNAUTHORIZED.value(), "登录状态已过期,请重新登录")); response.getWriter().write(jsonResponse); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenConfigurer.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.security; import lombok.RequiredArgsConstructor; import me.zhengjie.modules.security.config.SecurityProperties; import me.zhengjie.modules.security.service.OnlineUserService; import me.zhengjie.modules.security.service.UserCacheManager; import org.springframework.security.config.annotation.SecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * @author / */ @RequiredArgsConstructor public class TokenConfigurer extends SecurityConfigurerAdapter { private final TokenProvider tokenProvider; private final SecurityProperties properties; private final OnlineUserService onlineUserService; @Override public void configure(HttpSecurity http) { TokenFilter customFilter = new TokenFilter(tokenProvider, properties, onlineUserService); http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenFilter.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.security; import cn.hutool.core.util.StrUtil; import me.zhengjie.modules.security.config.SecurityProperties; import me.zhengjie.modules.security.service.dto.OnlineUserDto; import me.zhengjie.modules.security.service.OnlineUserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.util.StringUtils; import org.springframework.web.filter.GenericFilterBean; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * @author / */ public class TokenFilter extends GenericFilterBean { private static final Logger log = LoggerFactory.getLogger(TokenFilter.class); private final TokenProvider tokenProvider; private final SecurityProperties properties; private final OnlineUserService onlineUserService; /** * @param tokenProvider Token * @param properties JWT * @param onlineUserService 用户在线 */ public TokenFilter(TokenProvider tokenProvider, SecurityProperties properties, OnlineUserService onlineUserService) { this.properties = properties; this.onlineUserService = onlineUserService; this.tokenProvider = tokenProvider; } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; String token = resolveToken(httpServletRequest); // 对于 Token 为空的不需要去查 Redis if(StrUtil.isNotBlank(token)){ // 获取用户Token的Key String loginKey = tokenProvider.loginKey(token); OnlineUserDto onlineUserDto = onlineUserService.getOne(loginKey); // 判断用户在线信息是否为空 if (onlineUserDto != null) { // Token 续期判断 tokenProvider.checkRenewal(token); // 获取认证信息,设置上下文 Authentication authentication = tokenProvider.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(authentication); } } filterChain.doFilter(servletRequest, servletResponse); } /** * 初步检测Token * * @param request / * @return / */ private String resolveToken(HttpServletRequest request) { String bearerToken = request.getHeader(properties.getHeader()); if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(properties.getTokenStartWith())) { // 去掉令牌前缀 return bearerToken.replace(properties.getTokenStartWith(), ""); } else { log.debug("非法Token:{}", bearerToken); } return null; } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenProvider.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.security; import cn.hutool.core.date.DateField; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.IdUtil; import io.jsonwebtoken.*; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; import lombok.extern.slf4j.Slf4j; import me.zhengjie.modules.security.config.SecurityProperties; import me.zhengjie.modules.security.service.dto.JwtUserDto; import me.zhengjie.utils.RedisUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.User; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.security.Key; import java.util.*; import java.util.concurrent.TimeUnit; /** * @author / */ @Slf4j @Component public class TokenProvider implements InitializingBean { private Key signingKey; private JwtParser jwtParser; private final RedisUtils redisUtils; private final SecurityProperties properties; public static final String AUTHORITIES_UUID_KEY = "uid"; public static final String AUTHORITIES_UID_KEY = "userId"; public TokenProvider(SecurityProperties properties, RedisUtils redisUtils) { this.properties = properties; this.redisUtils = redisUtils; } @Override public void afterPropertiesSet() { // 解码Base64密钥并创建签名密钥 byte[] keyBytes = Decoders.BASE64.decode(properties.getBase64Secret()); this.signingKey = Keys.hmacShaKeyFor(keyBytes); // 初始化 JwtParser jwtParser = Jwts.parserBuilder() .setSigningKey(signingKey) // 使用预生成的签名密钥 .build(); } /** * 创建Token 设置永不过期, * Token 的时间有效性转到Redis 维护 * * @param user / * @return / */ public String createToken(JwtUserDto user) { // 设置参数 Map claims = new HashMap<>(6); // 设置用户ID claims.put(AUTHORITIES_UID_KEY, user.getUser().getId()); // 设置UUID,确保每次Token不一样 claims.put(AUTHORITIES_UUID_KEY, IdUtil.simpleUUID()); // 直接调用 Jwts.builder() 创建新实例 return Jwts.builder() // 设置自定义 Claims .setClaims(claims) // 设置主题 .setSubject(user.getUsername()) // 使用预生成的签名密钥和算法签名 .signWith(signingKey, SignatureAlgorithm.HS512) .compact(); } /** * 依据Token 获取鉴权信息 * * @param token / * @return / */ Authentication getAuthentication(String token) { Claims claims = getClaims(token); User principal = new User(claims.getSubject(), "******", new ArrayList<>()); return new UsernamePasswordAuthenticationToken(principal, token, new ArrayList<>()); } public Claims getClaims(String token) { return jwtParser .parseClaimsJws(token) .getBody(); } /** * @param token 需要检查的token */ public void checkRenewal(String token) { // 判断是否续期token,计算token的过期时间 String loginKey = loginKey(token); long time = redisUtils.getExpire(loginKey) * 1000; Date expireDate = DateUtil.offset(new Date(), DateField.MILLISECOND, (int) time); // 判断当前时间与过期时间的时间差 long differ = expireDate.getTime() - System.currentTimeMillis(); // 如果在续期检查的范围内,则续期 if (differ <= properties.getDetect()) { long renew = time + properties.getRenew(); redisUtils.expire(loginKey, renew, TimeUnit.MILLISECONDS); } } public String getToken(HttpServletRequest request) { final String requestHeader = request.getHeader(properties.getHeader()); if (requestHeader != null && requestHeader.startsWith(properties.getTokenStartWith())) { return requestHeader.substring(7); } return null; } /** * 获取登录用户RedisKey * @param token / * @return key */ public String loginKey(String token) { Claims claims = getClaims(token); return properties.getOnlineKey() + claims.getSubject() + ":" + getId(token); } /** * 获取登录用户TokenKey * @param token / * @return / */ public String getId(String token) { Claims claims = getClaims(token); return claims.get(AUTHORITIES_UUID_KEY).toString(); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/service/OnlineUserService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.service; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.zhengjie.modules.security.security.TokenProvider; import me.zhengjie.utils.PageResult; import me.zhengjie.modules.security.config.SecurityProperties; import me.zhengjie.modules.security.service.dto.JwtUserDto; import me.zhengjie.modules.security.service.dto.OnlineUserDto; import me.zhengjie.utils.*; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; /** * @author Zheng Jie * @date 2019年10月26日21:56:27 */ @Service @Slf4j @AllArgsConstructor public class OnlineUserService { private final SecurityProperties properties; private final TokenProvider tokenProvider; private final RedisUtils redisUtils; /** * 保存在线用户信息 * @param jwtUserDto / * @param token / * @param request / */ public void save(JwtUserDto jwtUserDto, String token, HttpServletRequest request){ String dept = jwtUserDto.getUser().getDept().getName(); String ip = StringUtils.getIp(request); String id = tokenProvider.getId(token); String browser = StringUtils.getBrowser(request); String address = StringUtils.getCityInfo(ip); OnlineUserDto onlineUserDto = null; try { onlineUserDto = new OnlineUserDto(id, jwtUserDto.getUsername(), jwtUserDto.getUser().getNickName(), dept, browser , ip, address, EncryptUtils.desEncrypt(token), new Date()); } catch (Exception e) { log.error(e.getMessage(),e); } String loginKey = tokenProvider.loginKey(token); redisUtils.set(loginKey, onlineUserDto, properties.getTokenValidityInSeconds(), TimeUnit.MILLISECONDS); } /** * 查询全部数据 * @param username / * @param pageable / * @return / */ public PageResult getAll(String username, Pageable pageable){ List onlineUserDtos = getAll(username); return PageUtil.toPage( PageUtil.paging(pageable.getPageNumber(),pageable.getPageSize(), onlineUserDtos), onlineUserDtos.size() ); } /** * 查询全部数据,不分页 * @param username / * @return / */ public List getAll(String username){ String loginKey = properties.getOnlineKey() + (StringUtils.isBlank(username) ? "" : "*" + username); List keys = redisUtils.scan(loginKey + "*"); Collections.reverse(keys); List onlineUserDtos = new ArrayList<>(); for (String key : keys) { onlineUserDtos.add(redisUtils.get(key, OnlineUserDto.class)); } onlineUserDtos.sort((o1, o2) -> o2.getLoginTime().compareTo(o1.getLoginTime())); return onlineUserDtos; } /** * 退出登录 * @param token / */ public void logout(String token) { String loginKey = tokenProvider.loginKey(token); redisUtils.del(loginKey); } /** * 导出 * @param all / * @param response / * @throws IOException / */ public void download(List all, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (OnlineUserDto user : all) { Map map = new LinkedHashMap<>(); map.put("用户名", user.getUserName()); map.put("部门", user.getDept()); map.put("登录IP", user.getIp()); map.put("登录地点", user.getAddress()); map.put("浏览器", user.getBrowser()); map.put("登录日期", user.getLoginTime()); list.add(map); } FileUtil.downloadExcel(list, response); } /** * 查询用户 * @param key / * @return / */ public OnlineUserDto getOne(String key) { return redisUtils.get(key, OnlineUserDto.class); } /** * 根据用户名强退用户 * @param username / */ public void kickOutForUsername(String username) { String loginKey = properties.getOnlineKey() + username + "*"; redisUtils.scanDel(loginKey); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserCacheManager.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.service; import cn.hutool.core.util.RandomUtil; import me.zhengjie.modules.security.config.LoginProperties; import me.zhengjie.modules.security.service.dto.JwtUserDto; import me.zhengjie.utils.RedisUtils; import me.zhengjie.utils.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import javax.annotation.Resource; /** * @author Zheng Jie * @description 用户缓存管理 * @date 2022-05-26 **/ @Component public class UserCacheManager { @Resource private RedisUtils redisUtils; @Value("${login.user-cache.idle-time}") private long idleTime; /** * 返回用户缓存 * @param userName 用户名 * @return JwtUserDto */ public JwtUserDto getUserCache(String userName) { // 转小写 userName = StringUtils.lowerCase(userName); if (StringUtils.isNotEmpty(userName)) { // 获取数据 return redisUtils.get(LoginProperties.cacheKey + userName, JwtUserDto.class); } return null; } /** * 添加缓存到Redis * @param userName 用户名 */ @Async public void addUserCache(String userName, JwtUserDto user) { // 转小写 userName = StringUtils.lowerCase(userName); if (StringUtils.isNotEmpty(userName)) { // 添加数据, 避免数据同时过期 long time = idleTime + RandomUtil.randomInt(900, 1800); redisUtils.set(LoginProperties.cacheKey + userName, user, time); } } /** * 清理用户缓存信息 * 用户信息变更时 * @param userName 用户名 */ @Async public void cleanUserCache(String userName) { // 转小写 userName = StringUtils.lowerCase(userName); if (StringUtils.isNotEmpty(userName)) { // 清除数据 redisUtils.del(LoginProperties.cacheKey + userName); } } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserDetailsServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.service; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.security.service.dto.AuthorityDto; import me.zhengjie.modules.security.service.dto.JwtUserDto; import me.zhengjie.modules.system.service.DataService; import me.zhengjie.modules.system.service.RoleService; import me.zhengjie.modules.system.service.UserService; import me.zhengjie.modules.system.service.dto.UserDto; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Service; import java.util.List; /** * @author Zheng Jie * @date 2018-11-22 */ @Slf4j @RequiredArgsConstructor @Service("userDetailsService") public class UserDetailsServiceImpl implements UserDetailsService { private final UserService userService; private final RoleService roleService; private final DataService dataService; private final UserCacheManager userCacheManager; @Override public JwtUserDto loadUserByUsername(String username) { JwtUserDto jwtUserDto = userCacheManager.getUserCache(username); if(jwtUserDto == null){ UserDto user = userService.getLoginData(username); if (user == null) { throw new BadRequestException("用户不存在"); } else { if (!user.getEnabled()) { throw new BadRequestException("账号未激活!"); } // 获取用户的权限 List authorities = roleService.buildPermissions(user); // 初始化JwtUserDto jwtUserDto = new JwtUserDto(user, dataService.getDeptIds(user), authorities); // 添加缓存数据 userCacheManager.addUserCache(username, jwtUserDto); } } return jwtUserDto; } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/AuthUserDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import javax.validation.constraints.NotBlank; /** * @author Zheng Jie * @date 2018-11-30 */ @Getter @Setter public class AuthUserDto { @NotBlank @ApiModelProperty(value = "用户名") private String username; @NotBlank @ApiModelProperty(value = "密码") private String password; @ApiModelProperty(value = "验证码") private String code; @ApiModelProperty(value = "验证码的key") private String uuid = ""; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/AuthorityDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.security.core.GrantedAuthority; /** * 避免序列化问题 * @author Zheng Jie * @date 2018-11-30 */ @Data @NoArgsConstructor @AllArgsConstructor public class AuthorityDto implements GrantedAuthority { @ApiModelProperty(value = "角色名") private String authority; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/JwtUserDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.service.dto; import com.alibaba.fastjson2.annotation.JSONField; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Getter; import me.zhengjie.modules.system.service.dto.UserDto; import org.springframework.security.core.userdetails.UserDetails; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * @author Zheng Jie * @date 2018-11-23 */ @Getter @AllArgsConstructor public class JwtUserDto implements UserDetails { @ApiModelProperty(value = "用户") private final UserDto user; @ApiModelProperty(value = "数据权限") private final List dataScopes; @ApiModelProperty(value = "角色权限") private final List authorities; public Set getRoles() { return authorities.stream().map(AuthorityDto::getAuthority).collect(Collectors.toSet()); } @Override @JSONField(serialize = false) public String getPassword() { return user.getPassword(); } @Override @JSONField(serialize = false) public String getUsername() { return user.getUsername(); } @JSONField(serialize = false) @Override public boolean isAccountNonExpired() { return true; } @JSONField(serialize = false) @Override public boolean isAccountNonLocked() { return true; } @JSONField(serialize = false) @Override public boolean isCredentialsNonExpired() { return true; } @Override @JSONField(serialize = false) public boolean isEnabled() { return user.getEnabled(); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/OnlineUserDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.security.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.Date; /** * 在线用户 * @author Zheng Jie */ @Data @AllArgsConstructor @NoArgsConstructor public class OnlineUserDto { @ApiModelProperty(value = "Token编号") private String uid; @ApiModelProperty(value = "用户名") private String userName; @ApiModelProperty(value = "昵称") private String nickName; @ApiModelProperty(value = "岗位") private String dept; @ApiModelProperty(value = "浏览器") private String browser; @ApiModelProperty(value = "IP") private String ip; @ApiModelProperty(value = "地址") private String address; @ApiModelProperty(value = "token") private String key; @ApiModelProperty(value = "登录时间") private Date loginTime; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Dept.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.domain; import com.alibaba.fastjson2.annotation.JSONField; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseEntity; import javax.persistence.*; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.io.Serializable; import java.util.Objects; import java.util.Set; /** * @author Zheng Jie * @date 2019-03-25 */ @Entity @Getter @Setter @Table(name="sys_dept") public class Dept extends BaseEntity implements Serializable { @Id @Column(name = "dept_id") @NotNull(groups = Update.class) @ApiModelProperty(value = "ID", hidden = true) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @JSONField(serialize = false) @ManyToMany(mappedBy = "depts") @ApiModelProperty(value = "角色") private Set roles; @ApiModelProperty(value = "排序") private Integer deptSort; @NotBlank @ApiModelProperty(value = "部门名称") private String name; @NotNull @ApiModelProperty(value = "是否启用") private Boolean enabled; @ApiModelProperty(value = "上级部门") private Long pid; @ApiModelProperty(value = "子节点数目", hidden = true) private Integer subCount = 0; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Dept dept = (Dept) o; return Objects.equals(id, dept.id) && Objects.equals(name, dept.name); } @Override public int hashCode() { return Objects.hash(id, name); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Dict.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseEntity; import javax.persistence.*; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.io.Serializable; import java.util.List; /** * @author Zheng Jie * @date 2019-04-10 */ @Entity @Getter @Setter @Table(name="sys_dict") public class Dict extends BaseEntity implements Serializable { @Id @Column(name = "dict_id") @NotNull(groups = Update.class) @ApiModelProperty(value = "ID", hidden = true) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @OneToMany(mappedBy = "dict",cascade={CascadeType.PERSIST,CascadeType.REMOVE}) private List dictDetails; @NotBlank @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "描述") private String description; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/domain/DictDetail.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseEntity; import javax.persistence.*; import javax.validation.constraints.NotNull; import java.io.Serializable; /** * @author Zheng Jie * @date 2019-04-10 */ @Entity @Getter @Setter @Table(name="sys_dict_detail") public class DictDetail extends BaseEntity implements Serializable { @Id @Column(name = "detail_id") @NotNull(groups = Update.class) @ApiModelProperty(value = "ID", hidden = true) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @JoinColumn(name = "dict_id") @ManyToOne(fetch=FetchType.LAZY) @ApiModelProperty(value = "字典", hidden = true) private Dict dict; @ApiModelProperty(value = "字典标签") private String label; @ApiModelProperty(value = "字典值") private String value; @ApiModelProperty(value = "排序") private Integer dictSort = 999; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Job.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseEntity; import javax.persistence.*; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.io.Serializable; import java.util.Objects; /** * @author Zheng Jie * @date 2019-03-29 */ @Entity @Getter @Setter @Table(name="sys_job") public class Job extends BaseEntity implements Serializable { @Id @Column(name = "job_id") @NotNull(groups = Update.class) @ApiModelProperty(value = "ID", hidden = true) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotBlank @ApiModelProperty(value = "岗位名称") private String name; @NotNull @ApiModelProperty(value = "岗位排序") private Long jobSort; @NotNull @ApiModelProperty(value = "是否启用") private Boolean enabled; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Job job = (Job) o; return Objects.equals(id, job.id); } @Override public int hashCode() { return Objects.hash(id); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Menu.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.domain; import com.alibaba.fastjson2.annotation.JSONField; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseEntity; import javax.persistence.*; import javax.validation.constraints.NotNull; import java.io.Serializable; import java.util.Objects; import java.util.Set; /** * @author Zheng Jie * @date 2018-12-17 */ @Entity @Getter @Setter @Table(name = "sys_menu") public class Menu extends BaseEntity implements Serializable { @Id @Column(name = "menu_id") @NotNull(groups = {Update.class}) @ApiModelProperty(value = "ID", hidden = true) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @JSONField(serialize = false) @ManyToMany(mappedBy = "menus") @ApiModelProperty(value = "菜单角色") private Set roles; @ApiModelProperty(value = "菜单标题") private String title; @Column(name = "name") @ApiModelProperty(value = "菜单组件名称") private String componentName; @ApiModelProperty(value = "排序") private Integer menuSort = 999; @ApiModelProperty(value = "组件路径") private String component; @ApiModelProperty(value = "路由地址") private String path; @ApiModelProperty(value = "菜单类型,目录、菜单、按钮") private Integer type; @ApiModelProperty(value = "权限标识") private String permission; @ApiModelProperty(value = "菜单图标") private String icon; @Column(columnDefinition = "bit(1) default 0") @ApiModelProperty(value = "缓存") private Boolean cache; @Column(columnDefinition = "bit(1) default 0") @ApiModelProperty(value = "是否隐藏") private Boolean hidden; @ApiModelProperty(value = "上级菜单") private Long pid; @ApiModelProperty(value = "子节点数目", hidden = true) private Integer subCount = 0; @ApiModelProperty(value = "外链菜单") private Boolean iFrame; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Menu menu = (Menu) o; return Objects.equals(id, menu.id); } @Override public int hashCode() { return Objects.hash(id); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Role.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.domain; import com.alibaba.fastjson2.annotation.JSONField; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseEntity; import me.zhengjie.utils.enums.DataScopeEnum; import javax.persistence.*; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.io.Serializable; import java.util.Objects; import java.util.Set; /** * 角色 * @author Zheng Jie * @date 2018-11-22 */ @Getter @Setter @Entity @Table(name = "sys_role") public class Role extends BaseEntity implements Serializable { @Id @Column(name = "role_id") @NotNull(groups = {Update.class}) @GeneratedValue(strategy = GenerationType.IDENTITY) @ApiModelProperty(value = "ID", hidden = true) private Long id; @JSONField(serialize = false) @ManyToMany(mappedBy = "roles") @ApiModelProperty(value = "用户", hidden = true) private Set users; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "sys_roles_menus", joinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "role_id")}, inverseJoinColumns = {@JoinColumn(name = "menu_id",referencedColumnName = "menu_id")}) @ApiModelProperty(value = "菜单", hidden = true) private Set menus; @ManyToMany @JoinTable(name = "sys_roles_depts", joinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "role_id")}, inverseJoinColumns = {@JoinColumn(name = "dept_id",referencedColumnName = "dept_id")}) @ApiModelProperty(value = "部门", hidden = true) private Set depts; @NotBlank @ApiModelProperty(value = "名称", hidden = true) private String name; @ApiModelProperty(value = "数据权限,全部 、 本级 、 自定义") private String dataScope = DataScopeEnum.THIS_LEVEL.getValue(); @Column(name = "level") @ApiModelProperty(value = "级别,数值越小,级别越大") private Integer level = 3; @ApiModelProperty(value = "描述") private String description; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Role role = (Role) o; return Objects.equals(id, role.id); } @Override public int hashCode() { return Objects.hash(id); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/domain/User.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseEntity; import javax.persistence.*; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.io.Serializable; import java.util.Date; import java.util.Objects; import java.util.Set; /** * @author Zheng Jie * @date 2018-11-22 */ @Entity @Getter @Setter @Table(name="sys_user") public class User extends BaseEntity implements Serializable { @Id @Column(name = "user_id") @NotNull(groups = Update.class) @GeneratedValue(strategy = GenerationType.IDENTITY) @ApiModelProperty(value = "ID", hidden = true) private Long id; @ManyToMany(fetch = FetchType.EAGER) @ApiModelProperty(value = "用户角色") @JoinTable(name = "sys_users_roles", joinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "role_id")}) private Set roles; @ManyToMany(fetch = FetchType.EAGER) @ApiModelProperty(value = "用户岗位") @JoinTable(name = "sys_users_jobs", joinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "job_id",referencedColumnName = "job_id")}) private Set jobs; @OneToOne @JoinColumn(name = "dept_id") @ApiModelProperty(value = "用户部门") private Dept dept; @NotBlank @Column(unique = true) @ApiModelProperty(value = "用户名称") private String username; @NotBlank @ApiModelProperty(value = "用户昵称") private String nickName; @Email @NotBlank @ApiModelProperty(value = "邮箱") private String email; @NotBlank @ApiModelProperty(value = "电话号码") private String phone; @ApiModelProperty(value = "用户性别") private String gender; @ApiModelProperty(value = "头像真实名称",hidden = true) private String avatarName; @ApiModelProperty(value = "头像存储的路径", hidden = true) private String avatarPath; @ApiModelProperty(value = "密码") private String password; @NotNull @ApiModelProperty(value = "是否启用") private Boolean enabled; @ApiModelProperty(value = "是否为admin账号", hidden = true) private Boolean isAdmin = false; @Column(name = "pwd_reset_time") @ApiModelProperty(value = "最后修改密码的时间", hidden = true) private Date pwdResetTime; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } User user = (User) o; return Objects.equals(id, user.id) && Objects.equals(username, user.username); } @Override public int hashCode() { return Objects.hash(id, username); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/domain/vo/MenuMetaVo.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.domain.vo; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; import java.io.Serializable; /** * @author Zheng Jie * @date 2018-12-20 */ @Data @AllArgsConstructor public class MenuMetaVo implements Serializable { @ApiModelProperty(value = "菜单标题") private String title; @ApiModelProperty(value = "菜单图标") private String icon; @ApiModelProperty(value = "缓存") private Boolean noCache; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/domain/vo/MenuVo.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.domain.vo; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; import java.util.List; /** * 构建前端路由时用到 * @author Zheng Jie * @date 2018-12-20 */ @Data public class MenuVo implements Serializable { @ApiModelProperty(value = "菜单名称") private String name; @ApiModelProperty(value = "路径") private String path; @ApiModelProperty(value = "隐藏状态") private Boolean hidden; @ApiModelProperty(value = "重定向") private String redirect; @ApiModelProperty(value = "组件") private String component; @ApiModelProperty(value = "总是显示") private Boolean alwaysShow; @ApiModelProperty(value = "元数据") private MenuMetaVo meta; @ApiModelProperty(value = "子路由") private List children; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/domain/vo/UserPassVo.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.domain.vo; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * 修改密码的 Vo 类 * @author Zheng Jie * @date 2019年7月11日13:59:49 */ @Data public class UserPassVo { @ApiModelProperty(value = "旧密码") private String oldPass; @ApiModelProperty(value = "新密码") private String newPass; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/repository/DeptRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.repository; import me.zhengjie.modules.system.domain.Dept; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import java.util.List; import java.util.Set; /** * @author Zheng Jie * @date 2019-03-25 */ public interface DeptRepository extends JpaRepository, JpaSpecificationExecutor { /** * 根据 PID 查询 * @param id pid * @return / */ List findByPid(Long id); /** * 获取顶级部门 * @return / */ List findByPidIsNull(); /** * 根据角色ID 查询 * @param roleId 角色ID * @return / */ @Query(value = "select d.* from sys_dept d, sys_roles_depts r where " + "d.dept_id = r.dept_id and r.role_id = ?1", nativeQuery = true) Set findByRoleId(Long roleId); /** * 判断是否存在子节点 * @param pid / * @return / */ int countByPid(Long pid); /** * 根据ID更新sub_count * @param count / * @param id / */ @Modifying @Query(value = " update sys_dept set sub_count = ?1 where dept_id = ?2 ",nativeQuery = true) void updateSubCntById(Integer count, Long id); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/repository/DictDetailRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.repository; import me.zhengjie.modules.system.domain.DictDetail; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import java.util.List; /** * @author Zheng Jie * @date 2019-04-10 */ public interface DictDetailRepository extends JpaRepository, JpaSpecificationExecutor { /** * 根据字典名称查询 * @param name / * @return / */ List findByDictName(String name); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/repository/DictRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.repository; import me.zhengjie.modules.system.domain.Dict; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import java.util.List; import java.util.Set; /** * @author Zheng Jie * @date 2019-04-10 */ public interface DictRepository extends JpaRepository, JpaSpecificationExecutor { /** * 删除 * @param ids / */ void deleteByIdIn(Set ids); /** * 查询 * @param ids / * @return / */ List findByIdIn(Set ids); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/repository/JobRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.repository; import me.zhengjie.modules.system.domain.Job; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import java.util.Set; /** * @author Zheng Jie * @date 2019-03-29 */ public interface JobRepository extends JpaRepository, JpaSpecificationExecutor { /** * 根据名称查询 * @param name 名称 * @return / */ Job findByName(String name); /** * 根据Id删除 * @param ids / */ void deleteAllByIdIn(Set ids); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/repository/MenuRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.repository; import me.zhengjie.modules.system.domain.Menu; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; /** * @author Zheng Jie * @date 2018-12-17 */ public interface MenuRepository extends JpaRepository, JpaSpecificationExecutor { /** * 根据菜单标题查询 * @param title 菜单标题 * @return / */ Menu findByTitle(String title); /** * 根据组件名称查询 * @param name 组件名称 * @return / */ Menu findByComponentName(String name); /** * 根据菜单的 PID 查询 * @param pid / * @return / */ List findByPidOrderByMenuSort(long pid); /** * 查询顶级菜单 * @return / */ List findByPidIsNullOrderByMenuSort(); /** * 根据角色ID与菜单类型查询菜单 * @param roleIds roleIDs * @param type 类型 * @return / */ @Query(value = "SELECT m.* FROM sys_menu m, sys_roles_menus r WHERE " + "m.menu_id = r.menu_id AND r.role_id IN ?1 AND type != ?2 order by m.menu_sort asc",nativeQuery = true) LinkedHashSet findByRoleIdsAndTypeNot(Set roleIds, int type); /** * 获取节点数量 * @param id / * @return / */ int countByPid(Long id); /** * 更新节点数目 * @param count / * @param menuId / */ @Modifying @Query(value = " update sys_menu set sub_count = ?1 where menu_id = ?2 ",nativeQuery = true) void updateSubCntById(int count, Long menuId); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/repository/RoleRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.repository; import me.zhengjie.modules.system.domain.Role; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import java.util.List; import java.util.Set; /** * @author Zheng Jie * @date 2018-12-03 */ public interface RoleRepository extends JpaRepository, JpaSpecificationExecutor { /** * 根据名称查询 * @param name / * @return / */ Role findByName(String name); /** * 删除多个角色 * @param ids / */ void deleteAllByIdIn(Set ids); /** * 根据用户ID查询 * @param id 用户ID * @return / */ @Query(value = "SELECT r.* FROM sys_role r, sys_users_roles u WHERE " + "r.role_id = u.role_id AND u.user_id = ?1",nativeQuery = true) Set findByUserId(Long id); /** * 解绑角色菜单 * @param id 菜单ID */ @Modifying @Query(value = "delete from sys_roles_menus where menu_id = ?1",nativeQuery = true) void untiedMenu(Long id); /** * 根据部门查询 * @param deptIds / * @return / */ @Query(value = "select count(1) from sys_role r, sys_roles_depts d where " + "r.role_id = d.role_id and d.dept_id in ?1",nativeQuery = true) int countByDepts(Set deptIds); /** * 根据菜单Id查询 * @param menuIds / * @return / */ @Query(value = "SELECT r.* FROM sys_role r, sys_roles_menus m WHERE " + "r.role_id = m.role_id AND m.menu_id in ?1",nativeQuery = true) List findInMenuId(List menuIds); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/repository/UserRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.repository; import me.zhengjie.modules.system.domain.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import java.util.Date; import java.util.List; import java.util.Set; /** * @author Zheng Jie * @date 2018-11-22 */ public interface UserRepository extends JpaRepository, JpaSpecificationExecutor { /** * 根据用户名查询 * @param username 用户名 * @return / */ User findByUsername(String username); /** * 根据邮箱查询 * @param email 邮箱 * @return / */ User findByEmail(String email); /** * 根据手机号查询 * @param phone 手机号 * @return / */ User findByPhone(String phone); /** * 修改密码 * @param username 用户名 * @param pass 密码 * @param lastPasswordResetTime / */ @Modifying @Query(value = "update sys_user set password = ?2 , pwd_reset_time = ?3 where username = ?1",nativeQuery = true) void updatePass(String username, String pass, Date lastPasswordResetTime); /** * 修改邮箱 * @param username 用户名 * @param email 邮箱 */ @Modifying @Query(value = "update sys_user set email = ?2 where username = ?1",nativeQuery = true) void updateEmail(String username, String email); /** * 根据角色查询用户 * @param roleId / * @return / */ @Query(value = "SELECT u.* FROM sys_user u, sys_users_roles r WHERE" + " u.user_id = r.user_id AND r.role_id = ?1", nativeQuery = true) List findByRoleId(Long roleId); /** * 根据角色中的部门查询 * @param deptId / * @return / */ @Query(value = "SELECT u.* FROM sys_user u, sys_users_roles r, sys_roles_depts d WHERE " + "u.user_id = r.user_id AND r.role_id = d.role_id AND d.dept_id = ?1 group by u.user_id", nativeQuery = true) List findByRoleDeptId(Long deptId); /** * 根据菜单查询 * @param id 菜单ID * @return / */ @Query(value = "SELECT u.* FROM sys_user u, sys_users_roles ur, sys_roles_menus rm WHERE\n" + "u.user_id = ur.user_id AND ur.role_id = rm.role_id AND rm.menu_id = ?1 group by u.user_id", nativeQuery = true) List findByMenuId(Long id); /** * 根据Id删除 * @param ids / */ void deleteAllByIdIn(Set ids); /** * 根据岗位查询 * @param ids / * @return / */ @Query(value = "SELECT count(1) FROM sys_user u, sys_users_jobs j WHERE u.user_id = j.user_id AND j.job_id IN ?1", nativeQuery = true) int countByJobs(Set ids); /** * 根据部门查询 * @param deptIds / * @return / */ @Query(value = "SELECT count(1) FROM sys_user u WHERE u.dept_id IN ?1", nativeQuery = true) int countByDepts(Set deptIds); /** * 根据角色查询 * @param ids / * @return / */ @Query(value = "SELECT count(1) FROM sys_user u, sys_users_roles r WHERE " + "u.user_id = r.user_id AND r.role_id in ?1", nativeQuery = true) int countByRoles(Set ids); /** * 重置密码 * @param ids 、 * @param pwd 、 */ @Modifying @Query(value = "update sys_user set password = ?2 where user_id in ?1",nativeQuery = true) void resetPwd(Set ids, String pwd); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/rest/DeptController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.rest; import cn.hutool.core.collection.CollectionUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.annotation.Log; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.system.domain.Dept; import me.zhengjie.modules.system.service.DeptService; import me.zhengjie.modules.system.service.dto.DeptDto; import me.zhengjie.modules.system.service.dto.DeptQueryCriteria; import me.zhengjie.utils.PageResult; import me.zhengjie.utils.PageUtil; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.util.*; import java.util.stream.Collectors; /** * @author Zheng Jie * @date 2019-03-25 */ @RestController @RequiredArgsConstructor @Api(tags = "系统:部门管理") @RequestMapping("/api/dept") public class DeptController { private final DeptService deptService; private static final String ENTITY_NAME = "dept"; @ApiOperation("导出部门数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('dept:list')") public void exportDept(HttpServletResponse response, DeptQueryCriteria criteria) throws Exception { deptService.download(deptService.queryAll(criteria, false), response); } @ApiOperation("查询部门") @GetMapping @PreAuthorize("@el.check('user:list','dept:list')") public ResponseEntity> queryDept(DeptQueryCriteria criteria) throws Exception { List depts = deptService.queryAll(criteria, true); return new ResponseEntity<>(PageUtil.toPage(depts, depts.size()),HttpStatus.OK); } @ApiOperation("查询部门:根据ID获取同级与上级数据") @PostMapping("/superior") @PreAuthorize("@el.check('user:list','dept:list')") public ResponseEntity getDeptSuperior(@RequestBody List ids, @RequestParam(defaultValue = "false") Boolean exclude) { Set deptSet = new LinkedHashSet<>(); for (Long id : ids) { DeptDto deptDto = deptService.findById(id); List depts = deptService.getSuperior(deptDto, new ArrayList<>()); if(exclude){ for (DeptDto dept : depts) { if(dept.getId().equals(deptDto.getPid())) { dept.setSubCount(dept.getSubCount() - 1); } } // 编辑部门时不显示自己以及自己下级的数据,避免出现PID数据环形问题 depts = depts.stream().filter(i -> !ids.contains(i.getId())).collect(Collectors.toList()); } deptSet.addAll(depts); } return new ResponseEntity<>(deptService.buildTree(new ArrayList<>(deptSet)),HttpStatus.OK); } @Log("新增部门") @ApiOperation("新增部门") @PostMapping @PreAuthorize("@el.check('dept:add')") public ResponseEntity createDept(@Validated @RequestBody Dept resources){ if (resources.getId() != null) { throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); } deptService.create(resources); return new ResponseEntity<>(HttpStatus.CREATED); } @Log("修改部门") @ApiOperation("修改部门") @PutMapping @PreAuthorize("@el.check('dept:edit')") public ResponseEntity updateDept(@Validated(Dept.Update.class) @RequestBody Dept resources){ deptService.update(resources); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("删除部门") @ApiOperation("删除部门") @DeleteMapping @PreAuthorize("@el.check('dept:del')") public ResponseEntity deleteDept(@RequestBody Set ids){ Set deptDtos = new HashSet<>(); for (Long id : ids) { List deptList = deptService.findByPid(id); deptDtos.add(deptService.findById(id)); if(CollectionUtil.isNotEmpty(deptList)){ deptDtos = deptService.getDeleteDepts(deptList, deptDtos); } } // 验证是否被角色或用户关联 deptService.verification(deptDtos); deptService.delete(deptDtos); return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/rest/DictController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.annotation.Log; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.system.domain.Dict; import me.zhengjie.modules.system.service.DictService; import me.zhengjie.modules.system.service.dto.DictDto; import me.zhengjie.modules.system.service.dto.DictQueryCriteria; import me.zhengjie.utils.PageResult; import me.zhengjie.utils.PageUtil; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Set; /** * @author Zheng Jie * @date 2019-04-10 */ @RestController @RequiredArgsConstructor @Api(tags = "系统:字典管理") @RequestMapping("/api/dict") public class DictController { private final DictService dictService; private static final String ENTITY_NAME = "dict"; @ApiOperation("导出字典数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('dict:list')") public void exportDict(HttpServletResponse response, DictQueryCriteria criteria) throws IOException { dictService.download(dictService.queryAll(criteria), response); } @ApiOperation("查询字典") @GetMapping(value = "/all") @PreAuthorize("@el.check('dict:list')") public ResponseEntity> queryAllDict(){ return new ResponseEntity<>(dictService.queryAll(new DictQueryCriteria()),HttpStatus.OK); } @ApiOperation("查询字典") @GetMapping @PreAuthorize("@el.check('dict:list')") public ResponseEntity> queryDict(DictQueryCriteria resources, Pageable pageable){ return new ResponseEntity<>(dictService.queryAll(resources,pageable),HttpStatus.OK); } @Log("新增字典") @ApiOperation("新增字典") @PostMapping @PreAuthorize("@el.check('dict:add')") public ResponseEntity createDict(@Validated @RequestBody Dict resources){ if (resources.getId() != null) { throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); } dictService.create(resources); return new ResponseEntity<>(HttpStatus.CREATED); } @Log("修改字典") @ApiOperation("修改字典") @PutMapping @PreAuthorize("@el.check('dict:edit')") public ResponseEntity updateDict(@Validated(Dict.Update.class) @RequestBody Dict resources){ dictService.update(resources); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("删除字典") @ApiOperation("删除字典") @DeleteMapping @PreAuthorize("@el.check('dict:del')") public ResponseEntity deleteDict(@RequestBody Set ids){ dictService.delete(ids); return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/rest/DictDetailController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.annotation.Log; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.system.domain.DictDetail; import me.zhengjie.modules.system.service.DictDetailService; import me.zhengjie.modules.system.service.dto.DictDetailDto; import me.zhengjie.modules.system.service.dto.DictDetailQueryCriteria; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author Zheng Jie * @date 2019-04-10 */ @RestController @RequiredArgsConstructor @Api(tags = "系统:字典详情管理") @RequestMapping("/api/dictDetail") public class DictDetailController { private final DictDetailService dictDetailService; private static final String ENTITY_NAME = "dictDetail"; @ApiOperation("查询字典详情") @GetMapping public ResponseEntity> queryDictDetail(DictDetailQueryCriteria criteria, @PageableDefault(sort = {"dictSort"}, direction = Sort.Direction.ASC) Pageable pageable){ return new ResponseEntity<>(dictDetailService.queryAll(criteria,pageable),HttpStatus.OK); } @ApiOperation("查询多个字典详情") @GetMapping(value = "/map") public ResponseEntity getDictDetailMaps(@RequestParam String dictName){ String[] names = dictName.split("[,,]"); Map> dictMap = new HashMap<>(16); for (String name : names) { dictMap.put(name, dictDetailService.getDictByName(name)); } return new ResponseEntity<>(dictMap, HttpStatus.OK); } @Log("新增字典详情") @ApiOperation("新增字典详情") @PostMapping @PreAuthorize("@el.check('dict:add')") public ResponseEntity createDictDetail(@Validated @RequestBody DictDetail resources){ if (resources.getId() != null) { throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); } dictDetailService.create(resources); return new ResponseEntity<>(HttpStatus.CREATED); } @Log("修改字典详情") @ApiOperation("修改字典详情") @PutMapping @PreAuthorize("@el.check('dict:edit')") public ResponseEntity updateDictDetail(@Validated(DictDetail.Update.class) @RequestBody DictDetail resources){ dictDetailService.update(resources); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("删除字典详情") @ApiOperation("删除字典详情") @DeleteMapping(value = "/{id}") @PreAuthorize("@el.check('dict:del')") public ResponseEntity deleteDictDetail(@PathVariable Long id){ dictDetailService.delete(id); return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/rest/JobController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.annotation.Log; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.system.domain.Job; import me.zhengjie.modules.system.service.JobService; import me.zhengjie.modules.system.service.dto.JobDto; import me.zhengjie.modules.system.service.dto.JobQueryCriteria; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Set; /** * @author Zheng Jie * @date 2019-03-29 */ @RestController @RequiredArgsConstructor @Api(tags = "系统:岗位管理") @RequestMapping("/api/job") public class JobController { private final JobService jobService; private static final String ENTITY_NAME = "job"; @ApiOperation("导出岗位数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('job:list')") public void exportJob(HttpServletResponse response, JobQueryCriteria criteria) throws IOException { jobService.download(jobService.queryAll(criteria), response); } @ApiOperation("查询岗位") @GetMapping @PreAuthorize("@el.check('job:list','user:list')") public ResponseEntity> queryJob(JobQueryCriteria criteria, Pageable pageable){ return new ResponseEntity<>(jobService.queryAll(criteria, pageable),HttpStatus.OK); } @Log("新增岗位") @ApiOperation("新增岗位") @PostMapping @PreAuthorize("@el.check('job:add')") public ResponseEntity createJob(@Validated @RequestBody Job resources){ if (resources.getId() != null) { throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); } jobService.create(resources); return new ResponseEntity<>(HttpStatus.CREATED); } @Log("修改岗位") @ApiOperation("修改岗位") @PutMapping @PreAuthorize("@el.check('job:edit')") public ResponseEntity updateJob(@Validated(Job.Update.class) @RequestBody Job resources){ jobService.update(resources); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("删除岗位") @ApiOperation("删除岗位") @DeleteMapping @PreAuthorize("@el.check('job:del')") public ResponseEntity deleteJob(@RequestBody Set ids){ // 验证是否被用户关联 jobService.verification(ids); jobService.delete(ids); return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/rest/LimitController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import me.zhengjie.annotation.Limit; import me.zhengjie.annotation.rest.AnonymousGetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.atomic.AtomicInteger; /** * @author / * 接口限流测试类 */ @RestController @RequestMapping("/api/limit") @Api(tags = "系统:限流测试管理") public class LimitController { private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger(); /** * 测试限流注解,下面配置说明该接口 60秒内最多只能访问 10次,保存到redis的键名为 limit_test, */ @AnonymousGetMapping @ApiOperation("测试") @Limit(key = "test", period = 60, count = 10, name = "testLimit", prefix = "limit") public int testLimit() { return ATOMIC_INTEGER.incrementAndGet(); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/rest/MenuController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.rest; import cn.hutool.core.collection.CollectionUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.annotation.Log; import me.zhengjie.modules.system.domain.Menu; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.system.domain.vo.MenuVo; import me.zhengjie.modules.system.service.MenuService; import me.zhengjie.modules.system.service.dto.DeptDto; import me.zhengjie.modules.system.service.dto.MenuDto; import me.zhengjie.modules.system.service.dto.MenuQueryCriteria; import me.zhengjie.modules.system.service.mapstruct.MenuMapper; import me.zhengjie.utils.PageResult; import me.zhengjie.utils.PageUtil; import me.zhengjie.utils.SecurityUtils; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.util.*; import java.util.stream.Collectors; /** * @author Zheng Jie * @date 2018-12-03 */ @RestController @RequiredArgsConstructor @Api(tags = "系统:菜单管理") @RequestMapping("/api/menus") public class MenuController { private final MenuService menuService; private final MenuMapper menuMapper; private static final String ENTITY_NAME = "menu"; @ApiOperation("导出菜单数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('menu:list')") public void exportMenu(HttpServletResponse response, MenuQueryCriteria criteria) throws Exception { menuService.download(menuService.queryAll(criteria, false), response); } @GetMapping(value = "/build") @ApiOperation("获取前端所需菜单") public ResponseEntity> buildMenus(){ List menuDtoList = menuService.findByUser(SecurityUtils.getCurrentUserId()); List menus = menuService.buildTree(menuDtoList); return new ResponseEntity<>(menuService.buildMenus(menus),HttpStatus.OK); } @ApiOperation("返回全部的菜单") @GetMapping(value = "/lazy") @PreAuthorize("@el.check('menu:list','roles:list')") public ResponseEntity> queryAllMenu(@RequestParam Long pid){ return new ResponseEntity<>(menuService.getMenus(pid),HttpStatus.OK); } @ApiOperation("根据菜单ID返回所有子节点ID,包含自身ID") @GetMapping(value = "/child") @PreAuthorize("@el.check('menu:list','roles:list')") public ResponseEntity childMenu(@RequestParam Long id){ Set menuSet = new HashSet<>(); List menuList = menuService.getMenus(id); menuSet.add(menuService.findOne(id)); menuSet = menuService.getChildMenus(menuMapper.toEntity(menuList), menuSet); Set ids = menuSet.stream().map(Menu::getId).collect(Collectors.toSet()); return new ResponseEntity<>(ids,HttpStatus.OK); } @GetMapping @ApiOperation("查询菜单") @PreAuthorize("@el.check('menu:list')") public ResponseEntity> queryMenu(MenuQueryCriteria criteria) throws Exception { List menuDtoList = menuService.queryAll(criteria, true); return new ResponseEntity<>(PageUtil.toPage(menuDtoList, menuDtoList.size()),HttpStatus.OK); } @ApiOperation("查询菜单:根据ID获取同级与上级数据") @PostMapping("/superior") @PreAuthorize("@el.check('menu:list')") public ResponseEntity> getMenuSuperior(@RequestBody List ids) { Set menuDtos = new LinkedHashSet<>(); if(CollectionUtil.isNotEmpty(ids)){ for (Long id : ids) { MenuDto menuDto = menuService.findById(id); List menuDtoList = menuService.getSuperior(menuDto, new ArrayList<>()); for (MenuDto menu : menuDtoList) { if(menu.getId().equals(menuDto.getPid())) { menu.setSubCount(menu.getSubCount() - 1); } } menuDtos.addAll(menuDtoList); } // 编辑菜单时不显示自己以及自己下级的数据,避免出现PID数据环形问题 menuDtos = menuDtos.stream().filter(i -> !ids.contains(i.getId())).collect(Collectors.toSet()); return new ResponseEntity<>(menuService.buildTree(new ArrayList<>(menuDtos)),HttpStatus.OK); } return new ResponseEntity<>(menuService.getMenus(null),HttpStatus.OK); } @Log("新增菜单") @ApiOperation("新增菜单") @PostMapping @PreAuthorize("@el.check('menu:add')") public ResponseEntity createMenu(@Validated @RequestBody Menu resources){ if (resources.getId() != null) { throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); } menuService.create(resources); return new ResponseEntity<>(HttpStatus.CREATED); } @Log("修改菜单") @ApiOperation("修改菜单") @PutMapping @PreAuthorize("@el.check('menu:edit')") public ResponseEntity updateMenu(@Validated(Menu.Update.class) @RequestBody Menu resources){ menuService.update(resources); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("删除菜单") @ApiOperation("删除菜单") @DeleteMapping @PreAuthorize("@el.check('menu:del')") public ResponseEntity deleteMenu(@RequestBody Set ids){ Set menuSet = new HashSet<>(); for (Long id : ids) { List menuList = menuService.getMenus(id); menuSet.add(menuService.findOne(id)); menuSet = menuService.getChildMenus(menuMapper.toEntity(menuList), menuSet); } menuService.delete(menuSet); return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/rest/MonitorController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.modules.system.service.MonitorService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; /** * @author Zheng Jie * @date 2020-05-02 */ @RestController @RequiredArgsConstructor @Api(tags = "系统-服务监控管理") @RequestMapping("/api/monitor") public class MonitorController { private final MonitorService serverService; @GetMapping @ApiOperation("查询服务监控") @PreAuthorize("@el.check('monitor:list')") public ResponseEntity queryMonitor(){ return new ResponseEntity<>(serverService.getServers(),HttpStatus.OK); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/rest/RoleController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.rest; import cn.hutool.core.lang.Dict; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.annotation.Log; import me.zhengjie.modules.system.domain.Role; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.system.service.RoleService; import me.zhengjie.modules.system.service.dto.RoleDto; import me.zhengjie.modules.system.service.dto.RoleQueryCriteria; import me.zhengjie.modules.system.service.dto.RoleSmallDto; import me.zhengjie.utils.PageResult; import me.zhengjie.utils.SecurityUtils; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * @author Zheng Jie * @date 2018-12-03 */ @RestController @RequiredArgsConstructor @Api(tags = "系统:角色管理") @RequestMapping("/api/roles") public class RoleController { private final RoleService roleService; private static final String ENTITY_NAME = "role"; @ApiOperation("获取单个role") @GetMapping(value = "/{id}") @PreAuthorize("@el.check('roles:list')") public ResponseEntity findRoleById(@PathVariable Long id){ return new ResponseEntity<>(roleService.findById(id), HttpStatus.OK); } @ApiOperation("导出角色数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('role:list')") public void exportRole(HttpServletResponse response, RoleQueryCriteria criteria) throws IOException { roleService.download(roleService.queryAll(criteria), response); } @ApiOperation("返回全部的角色") @GetMapping(value = "/all") @PreAuthorize("@el.check('roles:list','user:add','user:edit')") public ResponseEntity> queryAllRole(){ return new ResponseEntity<>(roleService.queryAll(),HttpStatus.OK); } @ApiOperation("查询角色") @GetMapping @PreAuthorize("@el.check('roles:list')") public ResponseEntity> queryRole(RoleQueryCriteria criteria, Pageable pageable){ return new ResponseEntity<>(roleService.queryAll(criteria,pageable),HttpStatus.OK); } @ApiOperation("获取用户级别") @GetMapping(value = "/level") public ResponseEntity getRoleLevel(){ return new ResponseEntity<>(Dict.create().set("level", getLevels(null)),HttpStatus.OK); } @Log("新增角色") @ApiOperation("新增角色") @PostMapping @PreAuthorize("@el.check('roles:add')") public ResponseEntity createRole(@Validated @RequestBody Role resources){ if (resources.getId() != null) { throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); } getLevels(resources.getLevel()); roleService.create(resources); return new ResponseEntity<>(HttpStatus.CREATED); } @Log("修改角色") @ApiOperation("修改角色") @PutMapping @PreAuthorize("@el.check('roles:edit')") public ResponseEntity updateRole(@Validated(Role.Update.class) @RequestBody Role resources){ getLevels(resources.getLevel()); roleService.update(resources); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("修改角色菜单") @ApiOperation("修改角色菜单") @PutMapping(value = "/menu") @PreAuthorize("@el.check('roles:edit')") public ResponseEntity updateRoleMenu(@RequestBody Role resources){ RoleDto role = roleService.findById(resources.getId()); getLevels(role.getLevel()); roleService.updateMenu(resources,role); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("删除角色") @ApiOperation("删除角色") @DeleteMapping @PreAuthorize("@el.check('roles:del')") public ResponseEntity deleteRole(@RequestBody Set ids){ for (Long id : ids) { RoleDto role = roleService.findById(id); getLevels(role.getLevel()); } // 验证是否被用户关联 roleService.verification(ids); roleService.delete(ids); return new ResponseEntity<>(HttpStatus.OK); } /** * 获取用户的角色级别 * @return / */ private int getLevels(Integer level){ List levels = roleService.findByUsersId(SecurityUtils.getCurrentUserId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()); int min = Collections.min(levels); if(level != null){ if(level < min){ throw new BadRequestException("权限不足,你的角色级别:" + min + ",低于操作的角色级别:" + level); } } return min; } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/rest/UserController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.rest; import cn.hutool.core.collection.CollectionUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.annotation.Log; import me.zhengjie.utils.PageResult; import me.zhengjie.config.properties.RsaProperties; import me.zhengjie.modules.system.domain.Dept; import me.zhengjie.modules.system.service.DataService; import me.zhengjie.modules.system.domain.User; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.system.domain.vo.UserPassVo; import me.zhengjie.modules.system.service.DeptService; import me.zhengjie.modules.system.service.RoleService; import me.zhengjie.modules.system.service.dto.RoleSmallDto; import me.zhengjie.modules.system.service.dto.UserDto; import me.zhengjie.modules.system.service.dto.UserQueryCriteria; import me.zhengjie.modules.system.service.VerifyService; import me.zhengjie.utils.*; import me.zhengjie.modules.system.service.UserService; import me.zhengjie.utils.enums.CodeEnum; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; import java.util.stream.Collectors; /** * @author Zheng Jie * @date 2018-11-23 */ @Api(tags = "系统:用户管理") @RestController @RequestMapping("/api/users") @RequiredArgsConstructor public class UserController { private final PasswordEncoder passwordEncoder; private final UserService userService; private final DataService dataService; private final DeptService deptService; private final RoleService roleService; private final VerifyService verificationCodeService; @ApiOperation("导出用户数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('user:list')") public void exportUser(HttpServletResponse response, UserQueryCriteria criteria) throws IOException { userService.download(userService.queryAll(criteria), response); } @ApiOperation("查询用户") @GetMapping @PreAuthorize("@el.check('user:list')") public ResponseEntity> queryUser(UserQueryCriteria criteria, Pageable pageable){ if (!ObjectUtils.isEmpty(criteria.getDeptId())) { criteria.getDeptIds().add(criteria.getDeptId()); // 先查找是否存在子节点 List data = deptService.findByPid(criteria.getDeptId()); // 然后把子节点的ID都加入到集合中 criteria.getDeptIds().addAll(deptService.getDeptChildren(data)); } // 数据权限 List dataScopes = dataService.getDeptIds(userService.findByName(SecurityUtils.getCurrentUsername())); // criteria.getDeptIds() 不为空并且数据权限不为空则取交集 if (!CollectionUtils.isEmpty(criteria.getDeptIds()) && !CollectionUtils.isEmpty(dataScopes)){ // 取交集 criteria.getDeptIds().retainAll(dataScopes); if(!CollectionUtil.isEmpty(criteria.getDeptIds())){ return new ResponseEntity<>(userService.queryAll(criteria,pageable),HttpStatus.OK); } } else { // 否则取并集 criteria.getDeptIds().addAll(dataScopes); return new ResponseEntity<>(userService.queryAll(criteria,pageable),HttpStatus.OK); } return new ResponseEntity<>(PageUtil.noData(),HttpStatus.OK); } @Log("新增用户") @ApiOperation("新增用户") @PostMapping @PreAuthorize("@el.check('user:add')") public ResponseEntity createUser(@Validated @RequestBody User resources){ checkLevel(resources); // 默认密码 123456 resources.setPassword(passwordEncoder.encode("123456")); userService.create(resources); return new ResponseEntity<>(HttpStatus.CREATED); } @Log("修改用户") @ApiOperation("修改用户") @PutMapping @PreAuthorize("@el.check('user:edit')") public ResponseEntity updateUser(@Validated(User.Update.class) @RequestBody User resources) throws Exception { checkLevel(resources); userService.update(resources); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("修改用户:个人中心") @ApiOperation("修改用户:个人中心") @PutMapping(value = "center") public ResponseEntity centerUser(@Validated(User.Update.class) @RequestBody User resources){ if(!resources.getId().equals(SecurityUtils.getCurrentUserId())){ throw new BadRequestException("不能修改他人资料"); } userService.updateCenter(resources); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("删除用户") @ApiOperation("删除用户") @DeleteMapping @PreAuthorize("@el.check('user:del')") public ResponseEntity deleteUser(@RequestBody Set ids){ for (Long id : ids) { Integer currentLevel = Collections.min(roleService.findByUsersId(SecurityUtils.getCurrentUserId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList())); Integer optLevel = Collections.min(roleService.findByUsersId(id).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList())); if (currentLevel > optLevel) { throw new BadRequestException("角色权限不足,不能删除:" + userService.findById(id).getUsername()); } } userService.delete(ids); return new ResponseEntity<>(HttpStatus.OK); } @ApiOperation("修改密码") @PostMapping(value = "/updatePass") public ResponseEntity updateUserPass(@RequestBody UserPassVo passVo) throws Exception { String oldPass = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey,passVo.getOldPass()); String newPass = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey,passVo.getNewPass()); UserDto user = userService.findByName(SecurityUtils.getCurrentUsername()); if(!passwordEncoder.matches(oldPass, user.getPassword())){ throw new BadRequestException("修改失败,旧密码错误"); } if(passwordEncoder.matches(newPass, user.getPassword())){ throw new BadRequestException("新密码不能与旧密码相同"); } userService.updatePass(user.getUsername(),passwordEncoder.encode(newPass)); return new ResponseEntity<>(HttpStatus.OK); } @ApiOperation("重置密码") @PutMapping(value = "/resetPwd") public ResponseEntity resetPwd(@RequestBody Set ids) { String pwd = passwordEncoder.encode("123456"); userService.resetPwd(ids, pwd); return new ResponseEntity<>(HttpStatus.OK); } @ApiOperation("修改头像") @PostMapping(value = "/updateAvatar") public ResponseEntity updateUserAvatar(@RequestParam MultipartFile avatar){ return new ResponseEntity<>(userService.updateAvatar(avatar), HttpStatus.OK); } @Log("修改邮箱") @ApiOperation("修改邮箱") @PostMapping(value = "/updateEmail/{code}") public ResponseEntity updateUserEmail(@PathVariable String code,@RequestBody User user) throws Exception { String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey,user.getPassword()); UserDto userDto = userService.findByName(SecurityUtils.getCurrentUsername()); if(!passwordEncoder.matches(password, userDto.getPassword())){ throw new BadRequestException("密码错误"); } verificationCodeService.validated(CodeEnum.EMAIL_RESET_EMAIL_CODE.getKey() + user.getEmail(), code); userService.updateEmail(userDto.getUsername(),user.getEmail()); return new ResponseEntity<>(HttpStatus.OK); } /** * 如果当前用户的角色级别低于创建用户的角色级别,则抛出权限不足的错误 * @param resources / */ private void checkLevel(User resources) { Integer currentLevel = Collections.min(roleService.findByUsersId(SecurityUtils.getCurrentUserId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList())); Integer optLevel = roleService.findByRoles(resources.getRoles()); if (currentLevel > optLevel) { throw new BadRequestException("角色权限不足"); } } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/rest/VerifyController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.domain.vo.EmailVo; import me.zhengjie.service.EmailService; import me.zhengjie.modules.system.service.VerifyService; import me.zhengjie.utils.enums.CodeBiEnum; import me.zhengjie.utils.enums.CodeEnum; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.Objects; /** * @author Zheng Jie * @date 2018-12-26 */ @RestController @RequiredArgsConstructor @RequestMapping("/api/code") @Api(tags = "系统:验证码管理") public class VerifyController { private final VerifyService verificationCodeService; private final EmailService emailService; @PostMapping(value = "/resetEmail") @ApiOperation("重置邮箱,发送验证码") public ResponseEntity resetEmail(@RequestParam String email){ EmailVo emailVo = verificationCodeService.sendEmail(email, CodeEnum.EMAIL_RESET_EMAIL_CODE.getKey()); emailService.send(emailVo,emailService.find()); return new ResponseEntity<>(HttpStatus.OK); } @PostMapping(value = "/email/resetPass") @ApiOperation("重置密码,发送验证码") public ResponseEntity resetPass(@RequestParam String email){ EmailVo emailVo = verificationCodeService.sendEmail(email, CodeEnum.EMAIL_RESET_PWD_CODE.getKey()); emailService.send(emailVo,emailService.find()); return new ResponseEntity<>(HttpStatus.OK); } @GetMapping(value = "/validated") @ApiOperation("验证码验证") public ResponseEntity validated(@RequestParam String email, @RequestParam String code, @RequestParam Integer codeBi){ CodeBiEnum biEnum = CodeBiEnum.find(codeBi); switch (Objects.requireNonNull(biEnum)){ case ONE: verificationCodeService.validated(CodeEnum.EMAIL_RESET_EMAIL_CODE.getKey() + email ,code); break; case TWO: verificationCodeService.validated(CodeEnum.EMAIL_RESET_PWD_CODE.getKey() + email ,code); break; default: break; } return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/DataService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service; import me.zhengjie.modules.system.service.dto.UserDto; import java.util.List; /** * 数据权限服务类 * @author Zheng Jie * @date 2020-05-07 */ public interface DataService { /** * 获取数据权限 * @param user / * @return / */ List getDeptIds(UserDto user); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/DeptService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service; import me.zhengjie.modules.system.domain.Dept; import me.zhengjie.modules.system.service.dto.DeptDto; import me.zhengjie.modules.system.service.dto.DeptQueryCriteria; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Set; /** * @author Zheng Jie * @date 2019-03-25 */ public interface DeptService { /** * 查询所有数据 * @param criteria 条件 * @param isQuery / * @throws Exception / * @return / */ List queryAll(DeptQueryCriteria criteria, Boolean isQuery) throws Exception; /** * 根据ID查询 * @param id / * @return / */ DeptDto findById(Long id); /** * 创建 * @param resources / */ void create(Dept resources); /** * 编辑 * @param resources / */ void update(Dept resources); /** * 删除 * @param deptDtos / * */ void delete(Set deptDtos); /** * 根据PID查询 * @param pid / * @return / */ List findByPid(long pid); /** * 根据角色ID查询 * @param id / * @return / */ Set findByRoleId(Long id); /** * 导出数据 * @param queryAll 待导出的数据 * @param response / * @throws IOException / */ void download(List queryAll, HttpServletResponse response) throws IOException; /** * 获取待删除的部门 * @param deptList / * @param deptDtos / * @return / */ Set getDeleteDepts(List deptList, Set deptDtos); /** * 根据ID获取同级与上级数据 * @param deptDto / * @param depts / * @return / */ List getSuperior(DeptDto deptDto, List depts); /** * 构建树形数据 * @param deptDtos / * @return / */ Object buildTree(List deptDtos); /** * 获取 * @param deptList / * @return / */ List getDeptChildren(List deptList); /** * 验证是否被角色或用户关联 * @param deptDtos / */ void verification(Set deptDtos); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/DictDetailService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service; import me.zhengjie.utils.PageResult; import me.zhengjie.modules.system.domain.DictDetail; import me.zhengjie.modules.system.service.dto.DictDetailDto; import me.zhengjie.modules.system.service.dto.DictDetailQueryCriteria; import org.springframework.data.domain.Pageable; import java.util.List; /** * @author Zheng Jie * @date 2019-04-10 */ public interface DictDetailService { /** * 创建 * @param resources / */ void create(DictDetail resources); /** * 编辑 * @param resources / */ void update(DictDetail resources); /** * 删除 * @param id / */ void delete(Long id); /** * 分页查询 * @param criteria 条件 * @param pageable 分页参数 * @return / */ PageResult queryAll(DictDetailQueryCriteria criteria, Pageable pageable); /** * 根据字典名称获取字典详情 * @param name 字典名称 * @return / */ List getDictByName(String name); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/DictService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service; import me.zhengjie.utils.PageResult; import me.zhengjie.modules.system.domain.Dict; import me.zhengjie.modules.system.service.dto.DictDto; import me.zhengjie.modules.system.service.dto.DictQueryCriteria; import org.springframework.data.domain.Pageable; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Set; /** * @author Zheng Jie * @date 2019-04-10 */ public interface DictService { /** * 分页查询 * @param criteria 条件 * @param pageable 分页参数 * @return / */ PageResult queryAll(DictQueryCriteria criteria, Pageable pageable); /** * 查询全部数据 * @param dict / * @return / */ List queryAll(DictQueryCriteria dict); /** * 创建 * @param resources / * @return / */ void create(Dict resources); /** * 编辑 * @param resources / */ void update(Dict resources); /** * 删除 * @param ids / */ void delete(Set ids); /** * 导出数据 * @param queryAll 待导出的数据 * @param response / * @throws IOException / */ void download(List queryAll, HttpServletResponse response) throws IOException; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/JobService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service; import me.zhengjie.utils.PageResult; import me.zhengjie.modules.system.domain.Job; import me.zhengjie.modules.system.service.dto.JobDto; import me.zhengjie.modules.system.service.dto.JobQueryCriteria; import org.springframework.data.domain.Pageable; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Set; /** * @author Zheng Jie * @date 2019-03-29 */ public interface JobService { /** * 根据ID查询 * @param id / * @return / */ JobDto findById(Long id); /** * 创建 * @param resources / * @return / */ void create(Job resources); /** * 编辑 * @param resources / */ void update(Job resources); /** * 删除 * @param ids / */ void delete(Set ids); /** * 分页查询 * @param criteria 条件 * @param pageable 分页参数 * @return / */ PageResult queryAll(JobQueryCriteria criteria, Pageable pageable); /** * 查询全部数据 * @param criteria / * @return / */ List queryAll(JobQueryCriteria criteria); /** * 导出数据 * @param queryAll 待导出的数据 * @param response / * @throws IOException / */ void download(List queryAll, HttpServletResponse response) throws IOException; /** * 验证是否被用户关联 * @param ids / */ void verification(Set ids); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/MenuService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service; import me.zhengjie.modules.system.domain.Menu; import me.zhengjie.modules.system.domain.vo.MenuVo; import me.zhengjie.modules.system.service.dto.MenuDto; import me.zhengjie.modules.system.service.dto.MenuQueryCriteria; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Set; /** * @author Zheng Jie * @date 2018-12-17 */ public interface MenuService { /** * 查询全部数据 * @param criteria 条件 * @param isQuery / * @throws Exception / * @return / */ List queryAll(MenuQueryCriteria criteria, Boolean isQuery) throws Exception; /** * 根据ID查询 * @param id / * @return / */ MenuDto findById(long id); /** * 创建 * @param resources / */ void create(Menu resources); /** * 编辑 * @param resources / */ void update(Menu resources); /** * 获取所有子节点,包含自身ID * @param menuList / * @param menuSet / * @return / */ Set getChildMenus(List menuList, Set menuSet); /** * 构建菜单树 * @param menuDtos 原始数据 * @return / */ List buildTree(List menuDtos); /** * 构建菜单树 * @param menuDtos / * @return / */ List buildMenus(List menuDtos); /** * 根据ID查询 * @param id / * @return / */ Menu findOne(Long id); /** * 删除 * @param menuSet / */ void delete(Set menuSet); /** * 导出 * @param queryAll 待导出的数据 * @param response / * @throws IOException / */ void download(List queryAll, HttpServletResponse response) throws IOException; /** * 懒加载菜单数据 * @param pid / * @return / */ List getMenus(Long pid); /** * 根据ID获取同级与上级数据 * @param menuDto / * @param objects / * @return / */ List getSuperior(MenuDto menuDto, List objects); /** * 根据当前用户获取菜单 * @param currentUserId / * @return / */ List findByUser(Long currentUserId); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/MonitorService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service; import java.util.Map; /** * @author Zheng Jie * @date 2020-05-02 */ public interface MonitorService { /** * 查询数据分页 * @return Map */ Map getServers(); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/RoleService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service; import me.zhengjie.modules.security.service.dto.AuthorityDto; import me.zhengjie.modules.system.domain.Role; import me.zhengjie.modules.system.service.dto.RoleDto; import me.zhengjie.modules.system.service.dto.RoleQueryCriteria; import me.zhengjie.modules.system.service.dto.RoleSmallDto; import me.zhengjie.modules.system.service.dto.UserDto; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Set; /** * @author Zheng Jie * @date 2018-12-03 */ public interface RoleService { /** * 查询全部数据 * @return / */ List queryAll(); /** * 根据ID查询 * @param id / * @return / */ RoleDto findById(long id); /** * 创建 * @param resources / */ void create(Role resources); /** * 编辑 * @param resources / */ void update(Role resources); /** * 删除 * @param ids / */ void delete(Set ids); /** * 根据用户ID查询 * @param userId 用户ID * @return / */ List findByUsersId(Long userId); /** * 根据角色查询角色级别 * @param roles / * @return / */ Integer findByRoles(Set roles); /** * 修改绑定的菜单 * @param resources / * @param roleDTO / */ void updateMenu(Role resources, RoleDto roleDTO); /** * 解绑菜单 * @param id / */ void untiedMenu(Long id); /** * 待条件分页查询 * @param criteria 条件 * @param pageable 分页参数 * @return / */ PageResult queryAll(RoleQueryCriteria criteria, Pageable pageable); /** * 查询全部 * @param criteria 条件 * @return / */ List queryAll(RoleQueryCriteria criteria); /** * 导出数据 * @param queryAll 待导出的数据 * @param response / * @throws IOException / */ void download(List queryAll, HttpServletResponse response) throws IOException; /** * 获取用户权限信息 * @param user 用户信息 * @return 权限信息 */ List buildPermissions(UserDto user); /** * 验证是否被用户关联 * @param ids / */ void verification(Set ids); /** * 根据菜单Id查询 * @param menuIds / * @return / */ List findInMenuId(List menuIds); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/UserService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service; import me.zhengjie.utils.PageResult; import me.zhengjie.modules.system.domain.User; import me.zhengjie.modules.system.service.dto.UserDto; import me.zhengjie.modules.system.service.dto.UserQueryCriteria; import org.springframework.data.domain.Pageable; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Set; /** * @author Zheng Jie * @date 2018-11-23 */ public interface UserService { /** * 根据ID查询 * @param id ID * @return / */ UserDto findById(long id); /** * 新增用户 * @param resources / */ void create(User resources); /** * 编辑用户 * @param resources / * @throws Exception / */ void update(User resources) throws Exception; /** * 删除用户 * @param ids / */ void delete(Set ids); /** * 根据用户名查询 * @param userName / * @return / */ UserDto findByName(String userName); /** * 根据用户名查询 * @param userName / * @return / */ UserDto getLoginData(String userName); /** * 修改密码 * @param username 用户名 * @param encryptPassword 密码 */ void updatePass(String username, String encryptPassword); /** * 修改头像 * @param file 文件 * @return / */ Map updateAvatar(MultipartFile file); /** * 修改邮箱 * @param username 用户名 * @param email 邮箱 */ void updateEmail(String username, String email); /** * 查询全部 * @param criteria 条件 * @param pageable 分页参数 * @return / */ PageResult queryAll(UserQueryCriteria criteria, Pageable pageable); /** * 查询全部不分页 * @param criteria 条件 * @return / */ List queryAll(UserQueryCriteria criteria); /** * 导出数据 * @param queryAll 待导出的数据 * @param response / * @throws IOException / */ void download(List queryAll, HttpServletResponse response) throws IOException; /** * 用户自助修改资料 * @param resources / */ void updateCenter(User resources); /** * 重置密码 * @param ids 用户id * @param pwd 密码 */ void resetPwd(Set ids, String pwd); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/VerifyService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service; import me.zhengjie.domain.vo.EmailVo; /** * @author Zheng Jie * @date 2018-12-26 */ public interface VerifyService { /** * 发送验证码 * @param email / * @param key / * @return / */ EmailVo sendEmail(String email, String key); /** * 验证 * @param code / * @param key / */ void validated(String key, String code); } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DeptDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseDTO; import java.io.Serializable; import java.util.List; import java.util.Objects; /** * @author Zheng Jie * @date 2019-03-25 */ @Getter @Setter public class DeptDto extends BaseDTO implements Serializable { @ApiModelProperty(value = "ID") private Long id; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "是否启用") private Boolean enabled; @ApiModelProperty(value = "排序") private Integer deptSort; @ApiModelProperty(value = "子部门") private List children; @ApiModelProperty(value = "上级部门") private Long pid; @ApiModelProperty(value = "子部门数量", hidden = true) private Integer subCount; @ApiModelProperty(value = "是否有子节点") public Boolean getHasChildren() { return subCount > 0; } @ApiModelProperty(value = "是否为叶子") public Boolean getLeaf() { return subCount <= 0; } @ApiModelProperty(value = "部门全名") public String getLabel() { return name; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } DeptDto deptDto = (DeptDto) o; return Objects.equals(id, deptDto.id) && Objects.equals(name, deptDto.name); } @Override public int hashCode() { return Objects.hash(id, name); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DeptQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import me.zhengjie.annotation.DataPermission; import me.zhengjie.annotation.Query; import java.sql.Timestamp; import java.util.List; /** * @author Zheng Jie * @date 2019-03-25 */ @Data @DataPermission(fieldName = "id") public class DeptQueryCriteria{ @ApiModelProperty(value = "名称") @Query(type = Query.Type.INNER_LIKE) private String name; @Query @ApiModelProperty(value = "是否启用") private Boolean enabled; @Query @ApiModelProperty(value = "上级部门") private Long pid; @ApiModelProperty(value = "PID空查询", hidden = true) @Query(type = Query.Type.IS_NULL, propName = "pid") private Boolean pidIsNull; @ApiModelProperty(value = "创建时间") @Query(type = Query.Type.BETWEEN) private List createTime; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DeptSmallDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; /** * @author Zheng Jie * @date 2019-6-10 16:32:18 */ @Data public class DeptSmallDto implements Serializable { @ApiModelProperty(value = "ID") private Long id; @ApiModelProperty(value = "名称") private String name; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictDetailDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseDTO; import java.io.Serializable; /** * @author Zheng Jie * @date 2019-04-10 */ @Getter @Setter public class DictDetailDto extends BaseDTO implements Serializable { @ApiModelProperty(value = "ID") private Long id; @ApiModelProperty(value = "字典ID") private DictSmallDto dict; @ApiModelProperty(value = "字典标签") private String label; @ApiModelProperty(value = "字典值") private String value; @ApiModelProperty(value = "排序") private Integer dictSort; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictDetailQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import me.zhengjie.annotation.Query; /** * @author Zheng Jie * @date 2019-04-10 */ @Data public class DictDetailQueryCriteria { @ApiModelProperty(value = "字典标签") @Query(type = Query.Type.INNER_LIKE) private String label; @ApiModelProperty(value = "字典名称") @Query(propName = "name",joinName = "dict") private String dictName; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseDTO; import java.io.Serializable; import java.util.List; /** * @author Zheng Jie * @date 2019-04-10 */ @Getter @Setter public class DictDto extends BaseDTO implements Serializable { @ApiModelProperty(value = "ID") private Long id; @ApiModelProperty(value = "字典详情") private List dictDetails; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "描述") private String description; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import me.zhengjie.annotation.Query; /** * @author Zheng Jie * 公共查询类 */ @Data public class DictQueryCriteria { @ApiModelProperty(value = "模糊查询") @Query(blurry = "name,description") private String blurry; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictSmallDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import java.io.Serializable; /** * @author Zheng Jie * @date 2019-04-10 */ @Getter @Setter public class DictSmallDto implements Serializable { @ApiModelProperty(value = "ID") private Long id; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/JobDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import me.zhengjie.base.BaseDTO; import java.io.Serializable; /** * @author Zheng Jie * @date 2019-03-29 */ @Getter @Setter @NoArgsConstructor public class JobDto extends BaseDTO implements Serializable { @ApiModelProperty(value = "ID") private Long id; @ApiModelProperty(value = "岗位排序") private Integer jobSort; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "是否启用") private Boolean enabled; public JobDto(String name, Boolean enabled) { this.name = name; this.enabled = enabled; } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/JobQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.NoArgsConstructor; import me.zhengjie.annotation.Query; import java.sql.Timestamp; import java.util.List; /** * @author Zheng Jie * @date 2019-6-4 14:49:34 */ @Data @NoArgsConstructor public class JobQueryCriteria { @ApiModelProperty(value = "岗位名称") @Query(type = Query.Type.INNER_LIKE) private String name; @Query @ApiModelProperty(value = "岗位状态") private Boolean enabled; @ApiModelProperty(value = "创建时间") @Query(type = Query.Type.BETWEEN) private List createTime; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/JobSmallDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; /** * @author Zheng Jie * @date 2019-6-10 16:32:18 */ @Data @NoArgsConstructor public class JobSmallDto implements Serializable { @ApiModelProperty(value = "ID") private Long id; @ApiModelProperty(value = "名称") private String name; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/MenuDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseDTO; import java.io.Serializable; import java.util.List; import java.util.Objects; /** * @author Zheng Jie * @date 2018-12-17 */ @Getter @Setter public class MenuDto extends BaseDTO implements Serializable { @ApiModelProperty(value = "ID") private Long id; @ApiModelProperty(value = "子节点") private List children; @ApiModelProperty(value = "类型") private Integer type; @ApiModelProperty(value = "权限") private String permission; @ApiModelProperty(value = "菜单标题") private String title; @ApiModelProperty(value = "排序") private Integer menuSort; @ApiModelProperty(value = "路径") private String path; @ApiModelProperty(value = "组件") private String component; @ApiModelProperty(value = "PID") private Long pid; @ApiModelProperty(value = "子节点数目") private Integer subCount; @ApiModelProperty(value = "是否为Iframe") @JsonProperty("iFrame") private Boolean iFrame; @ApiModelProperty(value = "是否缓存") private Boolean cache; @ApiModelProperty(value = "是否隐藏") private Boolean hidden; @ApiModelProperty(value = "组件名称") private String componentName; @ApiModelProperty(value = "图标") private String icon; @ApiModelProperty(value = "是否存在子节点") public Boolean getHasChildren() { return subCount > 0; } @ApiModelProperty(value = "是否叶子节点") public Boolean getLeaf() { return subCount <= 0; } @ApiModelProperty(value = "标题") public String getLabel() { return title; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MenuDto menuDto = (MenuDto) o; return Objects.equals(id, menuDto.id); } @Override public int hashCode() { return Objects.hash(id); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/MenuQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import me.zhengjie.annotation.Query; import java.sql.Timestamp; import java.util.List; /** * @author Zheng Jie * 公共查询类 */ @Data public class MenuQueryCriteria { @ApiModelProperty(value = "模糊查询") @Query(blurry = "title,component,permission") private String blurry; @ApiModelProperty(value = "创建时间") @Query(type = Query.Type.BETWEEN) private List createTime; @ApiModelProperty(value = "PID空查询", hidden = true) @Query(type = Query.Type.IS_NULL, propName = "pid") private Boolean pidIsNull; @Query @ApiModelProperty(value = "PID") private Long pid; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/RoleDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseDTO; import java.io.Serializable; import java.util.Objects; import java.util.Set; /** * @author Zheng Jie * @date 2018-11-23 */ @Getter @Setter public class RoleDto extends BaseDTO implements Serializable { @ApiModelProperty(value = "ID") private Long id; @ApiModelProperty(value = "菜单") private Set menus; @ApiModelProperty(value = "部门") private Set depts; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "数据权限") private String dataScope; @ApiModelProperty(value = "级别") private Integer level; @ApiModelProperty(value = "描述") private String description; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } RoleDto roleDto = (RoleDto) o; return Objects.equals(id, roleDto.id); } @Override public int hashCode() { return Objects.hash(id); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/RoleQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import me.zhengjie.annotation.Query; import java.sql.Timestamp; import java.util.List; /** * @author Zheng Jie * 公共查询类 */ @Data public class RoleQueryCriteria { @ApiModelProperty(value = "模糊查询") @Query(blurry = "name,description") private String blurry; @ApiModelProperty(value = "创建时间") @Query(type = Query.Type.BETWEEN) private List createTime; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/RoleSmallDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; /** * @author Zheng Jie * @date 2018-11-23 */ @Data public class RoleSmallDto implements Serializable { @ApiModelProperty(value = "ID") private Long id; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "级别") private Integer level; @ApiModelProperty(value = "数据权限") private String dataScope; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import com.alibaba.fastjson2.annotation.JSONField; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseDTO; import java.io.Serializable; import java.util.Date; import java.util.Set; /** * @author Zheng Jie * @date 2018-11-23 */ @Getter @Setter public class UserDto extends BaseDTO implements Serializable { @ApiModelProperty(value = "ID") private Long id; @ApiModelProperty(value = "角色") private Set roles; @ApiModelProperty(value = "岗位") private Set jobs; @ApiModelProperty(value = "部门") private DeptSmallDto dept; @ApiModelProperty(value = "部门ID") private Long deptId; @ApiModelProperty(value = "用户名") private String username; @ApiModelProperty(value = "昵称") private String nickName; @ApiModelProperty(value = "邮箱") private String email; @ApiModelProperty(value = "电话") private String phone; @ApiModelProperty(value = "性别") private String gender; @ApiModelProperty(value = "头像") private String avatarName; @ApiModelProperty(value = "头像路径") private String avatarPath; @ApiModelProperty(value = "密码") private String password; @ApiModelProperty(value = "是否启用") private Boolean enabled; @ApiModelProperty(value = "管理员") @JSONField(serialize = false) private Boolean isAdmin = false; @ApiModelProperty(value = "密码重置时间") private Date pwdResetTime; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import me.zhengjie.annotation.Query; import java.io.Serializable; import java.sql.Timestamp; import java.util.HashSet; import java.util.List; import java.util.Set; /** * @author Zheng Jie * @date 2018-11-23 */ @Data public class UserQueryCriteria implements Serializable { @Query @ApiModelProperty(value = "ID") private Long id; @ApiModelProperty(value = "部门ID集合") @Query(propName = "id", type = Query.Type.IN, joinName = "dept") private Set deptIds = new HashSet<>(); @ApiModelProperty(value = "模糊查询") @Query(blurry = "email,username,nickName") private String blurry; @Query @ApiModelProperty(value = "是否启用") private Boolean enabled; @ApiModelProperty(value = "部门ID") private Long deptId; @ApiModelProperty(value = "创建时间") @Query(type = Query.Type.BETWEEN) private List createTime; } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DataServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.impl; import cn.hutool.core.collection.CollUtil; import lombok.RequiredArgsConstructor; import me.zhengjie.modules.system.domain.Dept; import me.zhengjie.modules.system.service.DataService; import me.zhengjie.modules.system.service.DeptService; import me.zhengjie.modules.system.service.RoleService; import me.zhengjie.modules.system.service.dto.RoleSmallDto; import me.zhengjie.modules.system.service.dto.UserDto; import me.zhengjie.utils.CacheKey; import me.zhengjie.utils.RedisUtils; import me.zhengjie.utils.enums.DataScopeEnum; import org.springframework.stereotype.Service; import java.util.*; import java.util.concurrent.TimeUnit; /** * @author Zheng Jie * @description 数据权限服务实现 * @date 2020-05-07 **/ @Service @RequiredArgsConstructor public class DataServiceImpl implements DataService { private final RedisUtils redisUtils; private final RoleService roleService; private final DeptService deptService; /** * 用户角色和用户部门改变时需清理缓存 * @param user / * @return / */ @Override public List getDeptIds(UserDto user) { String key = CacheKey.DATA_USER + user.getId(); List ids = redisUtils.getList(key, Long.class); if (CollUtil.isEmpty(ids)) { // 用于存储部门id Set deptIds = new HashSet<>(); // 查询用户角色 List roleSet = roleService.findByUsersId(user.getId()); // 获取对应的部门ID for (RoleSmallDto role : roleSet) { DataScopeEnum dataScopeEnum = DataScopeEnum.find(role.getDataScope()); switch (Objects.requireNonNull(dataScopeEnum)) { case THIS_LEVEL: deptIds.add(user.getDept().getId()); break; case CUSTOMIZE: deptIds.addAll(getCustomize(deptIds, role)); break; default: return new ArrayList<>(); } } ids = new ArrayList<>(deptIds); redisUtils.set(key, ids, 1, TimeUnit.DAYS); } return new ArrayList<>(ids); } /** * 获取自定义的数据权限 * @param deptIds 部门ID * @param role 角色 * @return 数据权限ID */ public Set getCustomize(Set deptIds, RoleSmallDto role){ Set depts = deptService.findByRoleId(role.getId()); for (Dept dept : depts) { deptIds.add(dept.getId()); List deptChildren = deptService.findByPid(dept.getId()); if (CollUtil.isNotEmpty(deptChildren)) { deptIds.addAll(deptService.getDeptChildren(deptChildren)); } } return deptIds; } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DeptServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import lombok.RequiredArgsConstructor; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.system.domain.Dept; import me.zhengjie.modules.system.domain.User; import me.zhengjie.modules.system.repository.RoleRepository; import me.zhengjie.modules.system.repository.UserRepository; import me.zhengjie.modules.system.service.dto.DeptDto; import me.zhengjie.modules.system.service.dto.DeptQueryCriteria; import me.zhengjie.utils.*; import me.zhengjie.modules.system.repository.DeptRepository; import me.zhengjie.modules.system.service.DeptService; import me.zhengjie.modules.system.service.mapstruct.DeptMapper; import me.zhengjie.utils.enums.DataScopeEnum; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.Field; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * @author Zheng Jie * @date 2019-03-25 */ @Service @RequiredArgsConstructor public class DeptServiceImpl implements DeptService { private final DeptRepository deptRepository; private final DeptMapper deptMapper; private final UserRepository userRepository; private final RedisUtils redisUtils; private final RoleRepository roleRepository; @Override public List queryAll(DeptQueryCriteria criteria, Boolean isQuery) throws Exception { Sort sort = Sort.by(Sort.Direction.ASC, "deptSort"); String dataScopeType = SecurityUtils.getDataScopeType(); if (isQuery) { if(dataScopeType.equals(DataScopeEnum.ALL.getValue())){ criteria.setPidIsNull(true); } List fields = QueryHelp.getAllFields(criteria.getClass(), new ArrayList<>()); List fieldNames = new ArrayList(){{ add("pidIsNull");add("enabled");}}; for (Field field : fields) { //设置对象的访问权限,保证对private的属性的访问 field.setAccessible(true); Object val = field.get(criteria); if(fieldNames.contains(field.getName())){ continue; } if (ObjectUtil.isNotNull(val)) { criteria.setPidIsNull(null); break; } } } List list = deptMapper.toDto(deptRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),sort)); // 如果为空,就代表为自定义权限或者本级权限,就需要去重,不理解可以注释掉,看查询结果 if(StringUtils.isBlank(dataScopeType)){ return deduplication(list); } return list; } @Override public DeptDto findById(Long id) { String key = CacheKey.DEPT_ID + id; Dept dept = redisUtils.get(key, Dept.class); if(dept == null){ dept = deptRepository.findById(id).orElseGet(Dept::new); ValidationUtil.isNull(dept.getId(),"Dept","id",id); redisUtils.set(key, dept, 1, TimeUnit.DAYS); } return deptMapper.toDto(dept); } @Override public List findByPid(long pid) { return deptRepository.findByPid(pid); } @Override public Set findByRoleId(Long id) { return deptRepository.findByRoleId(id); } @Override @Transactional(rollbackFor = Exception.class) public void create(Dept resources) { deptRepository.save(resources); // 计算子节点数目 resources.setSubCount(0); // 清理缓存 updateSubCnt(resources.getPid()); // 清理自定义角色权限的datascope缓存 delCaches(resources.getPid()); } @Override @Transactional(rollbackFor = Exception.class) public void update(Dept resources) { // 旧的部门 Long oldPid = findById(resources.getId()).getPid(); Long newPid = resources.getPid(); if(resources.getPid() != null && resources.getId().equals(resources.getPid())) { throw new BadRequestException("上级不能为自己"); } Dept dept = deptRepository.findById(resources.getId()).orElseGet(Dept::new); ValidationUtil.isNull( dept.getId(),"Dept","id",resources.getId()); resources.setId(dept.getId()); deptRepository.save(resources); // 更新父节点中子节点数目 updateSubCnt(oldPid); updateSubCnt(newPid); // 清理缓存 delCaches(resources.getId()); } @Override @Transactional(rollbackFor = Exception.class) public void delete(Set deptDtos) { for (DeptDto deptDto : deptDtos) { // 清理缓存 delCaches(deptDto.getId()); deptRepository.deleteById(deptDto.getId()); updateSubCnt(deptDto.getPid()); } } @Override public void download(List deptDtos, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (DeptDto deptDTO : deptDtos) { Map map = new LinkedHashMap<>(); map.put("部门名称", deptDTO.getName()); map.put("部门状态", deptDTO.getEnabled() ? "启用" : "停用"); map.put("创建日期", deptDTO.getCreateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } @Override public Set getDeleteDepts(List menuList, Set deptDtos) { for (Dept dept : menuList) { deptDtos.add(deptMapper.toDto(dept)); List depts = deptRepository.findByPid(dept.getId()); if(CollUtil.isNotEmpty(depts)){ getDeleteDepts(depts, deptDtos); } } return deptDtos; } @Override public List getDeptChildren(List deptList) { List list = new ArrayList<>(); deptList.forEach(dept -> { if (dept!=null && dept.getEnabled()) { List depts = deptRepository.findByPid(dept.getId()); if (CollUtil.isNotEmpty(depts)) { list.addAll(getDeptChildren(depts)); } list.add(dept.getId()); } } ); return list; } @Override public List getSuperior(DeptDto deptDto, List depts) { if(deptDto.getPid() == null){ depts.addAll(deptRepository.findByPidIsNull()); return deptMapper.toDto(depts); } depts.addAll(deptRepository.findByPid(deptDto.getPid())); return getSuperior(findById(deptDto.getPid()), depts); } @Override public Object buildTree(List deptDtos) { Set trees = new LinkedHashSet<>(); Set depts= new LinkedHashSet<>(); List deptNames = deptDtos.stream().map(DeptDto::getName).collect(Collectors.toList()); boolean isChild; for (DeptDto deptDTO : deptDtos) { isChild = false; if (deptDTO.getPid() == null) { trees.add(deptDTO); } for (DeptDto it : deptDtos) { if (it.getPid() != null && deptDTO.getId().equals(it.getPid())) { isChild = true; if (deptDTO.getChildren() == null) { deptDTO.setChildren(new ArrayList<>()); } deptDTO.getChildren().add(it); } } if(isChild) { depts.add(deptDTO); } else if(deptDTO.getPid() != null && !deptNames.contains(findById(deptDTO.getPid()).getName())) { depts.add(deptDTO); } } if (CollectionUtil.isEmpty(trees)) { trees = depts; } Map map = new HashMap<>(2); map.put("totalElements",deptDtos.size()); map.put("content",CollectionUtil.isEmpty(trees)? deptDtos :trees); return map; } @Override public void verification(Set deptDtos) { Set deptIds = deptDtos.stream().map(DeptDto::getId).collect(Collectors.toSet()); if(userRepository.countByDepts(deptIds) > 0){ throw new BadRequestException("所选部门存在用户关联,请解除后再试!"); } if(roleRepository.countByDepts(deptIds) > 0){ throw new BadRequestException("所选部门存在角色关联,请解除后再试!"); } } private void updateSubCnt(Long deptId){ if(deptId != null){ int count = deptRepository.countByPid(deptId); deptRepository.updateSubCntById(count, deptId); } } private List deduplication(List list) { List deptDtos = new ArrayList<>(); for (DeptDto deptDto : list) { boolean flag = true; for (DeptDto dto : list) { if (dto.getId().equals(deptDto.getPid())) { flag = false; break; } } if (flag){ deptDtos.add(deptDto); } } return deptDtos; } /** * 清理缓存 * @param id / */ public void delCaches(Long id){ List users = userRepository.findByRoleDeptId(id); // 删除数据权限 redisUtils.delByKeys(CacheKey.DATA_USER, users.stream().map(User::getId).collect(Collectors.toSet())); redisUtils.del(CacheKey.DEPT_ID + id); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DictDetailServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.impl; import cn.hutool.core.collection.CollUtil; import lombok.RequiredArgsConstructor; import me.zhengjie.utils.PageResult; import me.zhengjie.modules.system.domain.Dict; import me.zhengjie.modules.system.domain.DictDetail; import me.zhengjie.modules.system.repository.DictRepository; import me.zhengjie.modules.system.service.dto.DictDetailQueryCriteria; import me.zhengjie.utils.*; import me.zhengjie.modules.system.repository.DictDetailRepository; import me.zhengjie.modules.system.service.DictDetailService; import me.zhengjie.modules.system.service.dto.DictDetailDto; import me.zhengjie.modules.system.service.mapstruct.DictDetailMapper; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.concurrent.TimeUnit; /** * @author Zheng Jie * @date 2019-04-10 */ @Service @RequiredArgsConstructor public class DictDetailServiceImpl implements DictDetailService { private final DictRepository dictRepository; private final DictDetailRepository dictDetailRepository; private final DictDetailMapper dictDetailMapper; private final RedisUtils redisUtils; @Override public PageResult queryAll(DictDetailQueryCriteria criteria, Pageable pageable) { Page page = dictDetailRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); return PageUtil.toPage(page.map(dictDetailMapper::toDto)); } @Override @Transactional(rollbackFor = Exception.class) public void create(DictDetail resources) { dictDetailRepository.save(resources); // 清理缓存 delCaches(resources); } @Override @Transactional(rollbackFor = Exception.class) public void update(DictDetail resources) { DictDetail dictDetail = dictDetailRepository.findById(resources.getId()).orElseGet(DictDetail::new); ValidationUtil.isNull( dictDetail.getId(),"DictDetail","id",resources.getId()); resources.setId(dictDetail.getId()); dictDetailRepository.save(resources); // 清理缓存 delCaches(resources); } @Override public List getDictByName(String name) { String key = CacheKey.DICT_NAME + name; List dictDetails = redisUtils.getList(key, DictDetail.class); if(CollUtil.isEmpty(dictDetails)){ dictDetails = dictDetailRepository.findByDictName(name); redisUtils.set(key, dictDetails, 1 , TimeUnit.DAYS); } return dictDetailMapper.toDto(dictDetails); } @Override @Transactional(rollbackFor = Exception.class) public void delete(Long id) { DictDetail dictDetail = dictDetailRepository.findById(id).orElseGet(DictDetail::new); // 清理缓存 delCaches(dictDetail); dictDetailRepository.deleteById(id); } public void delCaches(DictDetail dictDetail){ Dict dict = dictRepository.findById(dictDetail.getDict().getId()).orElseGet(Dict::new); redisUtils.del(CacheKey.DICT_NAME + dict.getName()); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DictServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.impl; import cn.hutool.core.collection.CollectionUtil; import lombok.RequiredArgsConstructor; import me.zhengjie.utils.PageResult; import me.zhengjie.modules.system.domain.Dict; import me.zhengjie.modules.system.service.dto.DictDetailDto; import me.zhengjie.modules.system.service.dto.DictQueryCriteria; import me.zhengjie.utils.*; import me.zhengjie.modules.system.repository.DictRepository; import me.zhengjie.modules.system.service.DictService; import me.zhengjie.modules.system.service.dto.DictDto; import me.zhengjie.modules.system.service.mapstruct.DictMapper; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; /** * @author Zheng Jie * @date 2019-04-10 */ @Service @RequiredArgsConstructor public class DictServiceImpl implements DictService { private final DictRepository dictRepository; private final DictMapper dictMapper; private final RedisUtils redisUtils; @Override public PageResult queryAll(DictQueryCriteria dict, Pageable pageable){ Page page = dictRepository.findAll((root, query, cb) -> QueryHelp.getPredicate(root, dict, cb), pageable); return PageUtil.toPage(page.map(dictMapper::toDto)); } @Override public List queryAll(DictQueryCriteria dict) { List list = dictRepository.findAll((root, query, cb) -> QueryHelp.getPredicate(root, dict, cb)); return dictMapper.toDto(list); } @Override @Transactional(rollbackFor = Exception.class) public void create(Dict resources) { dictRepository.save(resources); } @Override @Transactional(rollbackFor = Exception.class) public void update(Dict resources) { // 清理缓存 delCaches(resources); Dict dict = dictRepository.findById(resources.getId()).orElseGet(Dict::new); ValidationUtil.isNull( dict.getId(),"Dict","id",resources.getId()); dict.setName(resources.getName()); dict.setDescription(resources.getDescription()); dictRepository.save(dict); } @Override @Transactional(rollbackFor = Exception.class) public void delete(Set ids) { // 清理缓存 List dicts = dictRepository.findByIdIn(ids); for (Dict dict : dicts) { delCaches(dict); } dictRepository.deleteByIdIn(ids); } @Override public void download(List dictDtos, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (DictDto dictDTO : dictDtos) { if(CollectionUtil.isNotEmpty(dictDTO.getDictDetails())){ for (DictDetailDto dictDetail : dictDTO.getDictDetails()) { Map map = new LinkedHashMap<>(); map.put("字典名称", dictDTO.getName()); map.put("字典描述", dictDTO.getDescription()); map.put("字典标签", dictDetail.getLabel()); map.put("字典值", dictDetail.getValue()); map.put("创建日期", dictDetail.getCreateTime()); list.add(map); } } else { Map map = new LinkedHashMap<>(); map.put("字典名称", dictDTO.getName()); map.put("字典描述", dictDTO.getDescription()); map.put("字典标签", null); map.put("字典值", null); map.put("创建日期", dictDTO.getCreateTime()); list.add(map); } } FileUtil.downloadExcel(list, response); } public void delCaches(Dict dict){ redisUtils.del(CacheKey.DICT_NAME + dict.getName()); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/JobServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.impl; import lombok.RequiredArgsConstructor; import me.zhengjie.utils.PageResult; import me.zhengjie.exception.BadRequestException; import me.zhengjie.exception.EntityExistException; import me.zhengjie.modules.system.domain.Job; import me.zhengjie.modules.system.repository.UserRepository; import me.zhengjie.modules.system.service.dto.JobQueryCriteria; import me.zhengjie.utils.*; import me.zhengjie.modules.system.repository.JobRepository; import me.zhengjie.modules.system.service.JobService; import me.zhengjie.modules.system.service.dto.JobDto; import me.zhengjie.modules.system.service.mapstruct.JobMapper; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; /** * @author Zheng Jie * @date 2019-03-29 */ @Service @RequiredArgsConstructor public class JobServiceImpl implements JobService { private final JobRepository jobRepository; private final JobMapper jobMapper; private final RedisUtils redisUtils; private final UserRepository userRepository; @Override public PageResult queryAll(JobQueryCriteria criteria, Pageable pageable) { Page page = jobRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); return PageUtil.toPage(page.map(jobMapper::toDto).getContent(),page.getTotalElements()); } @Override public List queryAll(JobQueryCriteria criteria) { List list = jobRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)); return jobMapper.toDto(list); } @Override public JobDto findById(Long id) { String key = CacheKey.JOB_ID + id; Job job = redisUtils.get(key, Job.class); if(job == null){ job = jobRepository.findById(id).orElseGet(Job::new); ValidationUtil.isNull(job.getId(),"Job","id",id); redisUtils.set(key, job, 1, TimeUnit.DAYS); } return jobMapper.toDto(job); } @Override @Transactional(rollbackFor = Exception.class) public void create(Job resources) { Job job = jobRepository.findByName(resources.getName()); if(job != null){ throw new EntityExistException(Job.class,"name",resources.getName()); } jobRepository.save(resources); } @Override @Transactional(rollbackFor = Exception.class) public void update(Job resources) { Job job = jobRepository.findById(resources.getId()).orElseGet(Job::new); Job old = jobRepository.findByName(resources.getName()); if(old != null && !old.getId().equals(resources.getId())){ throw new EntityExistException(Job.class,"name",resources.getName()); } ValidationUtil.isNull( job.getId(),"Job","id",resources.getId()); resources.setId(job.getId()); jobRepository.save(resources); // 删除缓存 delCaches(resources.getId()); } @Override @Transactional(rollbackFor = Exception.class) public void delete(Set ids) { jobRepository.deleteAllByIdIn(ids); // 删除缓存 redisUtils.delByKeys(CacheKey.JOB_ID, ids); } @Override public void download(List jobDtos, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (JobDto jobDTO : jobDtos) { Map map = new LinkedHashMap<>(); map.put("岗位名称", jobDTO.getName()); map.put("岗位状态", jobDTO.getEnabled() ? "启用" : "停用"); map.put("创建日期", jobDTO.getCreateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } @Override public void verification(Set ids) { if(userRepository.countByJobs(ids) > 0){ throw new BadRequestException("所选的岗位中存在用户关联,请解除关联再试!"); } } /** * 删除缓存 * @param id / */ public void delCaches(Long id){ redisUtils.del(CacheKey.JOB_ID + id); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/MenuServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import lombok.RequiredArgsConstructor; import me.zhengjie.modules.system.domain.Menu; import me.zhengjie.modules.system.domain.Role; import me.zhengjie.modules.system.domain.User; import me.zhengjie.modules.system.domain.vo.MenuMetaVo; import me.zhengjie.modules.system.domain.vo.MenuVo; import me.zhengjie.exception.BadRequestException; import me.zhengjie.exception.EntityExistException; import me.zhengjie.modules.system.repository.MenuRepository; import me.zhengjie.modules.system.repository.UserRepository; import me.zhengjie.modules.system.service.MenuService; import me.zhengjie.modules.system.service.RoleService; import me.zhengjie.modules.system.service.dto.MenuDto; import me.zhengjie.modules.system.service.dto.MenuQueryCriteria; import me.zhengjie.modules.system.service.dto.RoleSmallDto; import me.zhengjie.modules.system.service.mapstruct.MenuMapper; import me.zhengjie.utils.*; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.Field; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * @author Zheng Jie */ @Service @RequiredArgsConstructor public class MenuServiceImpl implements MenuService { private final MenuRepository menuRepository; private final UserRepository userRepository; private final MenuMapper menuMapper; private final RoleService roleService; private final RedisUtils redisUtils; private static final String HTTP_PRE = "http://"; private static final String HTTPS_PRE = "https://"; private static final String YES_STR = "是"; private static final String NO_STR = "否"; private static final String BAD_REQUEST = "外链必须以http://或者https://开头"; @Override public List queryAll(MenuQueryCriteria criteria, Boolean isQuery) throws Exception { Sort sort = Sort.by(Sort.Direction.ASC, "menuSort"); if(Boolean.TRUE.equals(isQuery)){ criteria.setPidIsNull(true); List fields = QueryHelp.getAllFields(criteria.getClass(), new ArrayList<>()); for (Field field : fields) { //设置对象的访问权限,保证对private的属性的访问 field.setAccessible(true); Object val = field.get(criteria); if("pidIsNull".equals(field.getName())){ continue; } if (ObjectUtil.isNotNull(val)) { criteria.setPidIsNull(null); break; } } } return menuMapper.toDto(menuRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),sort)); } @Override public MenuDto findById(long id) { String key = CacheKey.MENU_ID + id; Menu menu = redisUtils.get(key, Menu.class); if(menu == null){ menu = menuRepository.findById(id).orElseGet(Menu::new); ValidationUtil.isNull(menu.getId(),"Menu","id",id); redisUtils.set(key, menu, 1, TimeUnit.DAYS); } return menuMapper.toDto(menu); } /** * 用户角色改变时需清理缓存 * @param currentUserId / * @return / */ @Override public List findByUser(Long currentUserId) { String key = CacheKey.MENU_USER + currentUserId; List menus = redisUtils.getList(key, Menu.class); if (CollUtil.isEmpty(menus)){ List roles = roleService.findByUsersId(currentUserId); Set roleIds = roles.stream().map(RoleSmallDto::getId).collect(Collectors.toSet()); LinkedHashSet data = menuRepository.findByRoleIdsAndTypeNot(roleIds, 2); menus = new ArrayList<>(data); redisUtils.set(key, menus, 1, TimeUnit.DAYS); } return menus.stream().map(menuMapper::toDto).collect(Collectors.toList()); } @Override @Transactional(rollbackFor = Exception.class) public void create(Menu resources) { if(menuRepository.findByTitle(resources.getTitle()) != null){ throw new EntityExistException(Menu.class,"title",resources.getTitle()); } if(StringUtils.isNotBlank(resources.getComponentName())){ if(menuRepository.findByComponentName(resources.getComponentName()) != null){ throw new EntityExistException(Menu.class,"componentName",resources.getComponentName()); } } if (Long.valueOf(0L).equals(resources.getPid())) { resources.setPid(null); } if(resources.getIFrame()){ if (!(resources.getPath().toLowerCase().startsWith(HTTP_PRE)||resources.getPath().toLowerCase().startsWith(HTTPS_PRE))) { throw new BadRequestException(BAD_REQUEST); } } menuRepository.save(resources); // 计算子节点数目 resources.setSubCount(0); // 更新父节点菜单数目 updateSubCnt(resources.getPid()); } @Override @Transactional(rollbackFor = Exception.class) public void update(Menu resources) { if(resources.getId().equals(resources.getPid())) { throw new BadRequestException("上级不能为自己"); } Menu menu = menuRepository.findById(resources.getId()).orElseGet(Menu::new); ValidationUtil.isNull(menu.getId(),"Permission","id",resources.getId()); if(resources.getIFrame()){ if (!(resources.getPath().toLowerCase().startsWith(HTTP_PRE)||resources.getPath().toLowerCase().startsWith(HTTPS_PRE))) { throw new BadRequestException(BAD_REQUEST); } } Menu menu1 = menuRepository.findByTitle(resources.getTitle()); if(menu1 != null && !menu1.getId().equals(menu.getId())){ throw new EntityExistException(Menu.class,"title",resources.getTitle()); } if(resources.getPid().equals(0L)){ resources.setPid(null); } // 记录的父节点ID Long oldPid = menu.getPid(); Long newPid = resources.getPid(); if(StringUtils.isNotBlank(resources.getComponentName())){ menu1 = menuRepository.findByComponentName(resources.getComponentName()); if(menu1 != null && !menu1.getId().equals(menu.getId())){ throw new EntityExistException(Menu.class,"componentName",resources.getComponentName()); } } menu.setTitle(resources.getTitle()); menu.setComponent(resources.getComponent()); menu.setPath(resources.getPath()); menu.setIcon(resources.getIcon()); menu.setIFrame(resources.getIFrame()); menu.setPid(resources.getPid()); menu.setMenuSort(resources.getMenuSort()); menu.setCache(resources.getCache()); menu.setHidden(resources.getHidden()); menu.setComponentName(resources.getComponentName()); menu.setPermission(resources.getPermission()); menu.setType(resources.getType()); menuRepository.save(menu); // 计算父级菜单节点数目 updateSubCnt(oldPid); updateSubCnt(newPid); // 清理缓存 delCaches(resources.getId()); } @Override public Set getChildMenus(List menuList, Set menuSet) { for (Menu menu : menuList) { menuSet.add(menu); List menus = menuRepository.findByPidOrderByMenuSort(menu.getId()); if(CollUtil.isNotEmpty(menus)){ getChildMenus(menus, menuSet); } } return menuSet; } @Override @Transactional(rollbackFor = Exception.class) public void delete(Set menuSet) { for (Menu menu : menuSet) { // 清理缓存 delCaches(menu.getId()); roleService.untiedMenu(menu.getId()); menuRepository.deleteById(menu.getId()); updateSubCnt(menu.getPid()); } } @Override public List getMenus(Long pid) { List menus; if(pid != null && !pid.equals(0L)){ menus = menuRepository.findByPidOrderByMenuSort(pid); } else { menus = menuRepository.findByPidIsNullOrderByMenuSort(); } return menuMapper.toDto(menus); } @Override public List getSuperior(MenuDto menuDto, List menus) { if(menuDto.getPid() == null){ menus.addAll(menuRepository.findByPidIsNullOrderByMenuSort()); return menuMapper.toDto(menus); } menus.addAll(menuRepository.findByPidOrderByMenuSort(menuDto.getPid())); return getSuperior(findById(menuDto.getPid()), menus); } @Override public List buildTree(List menuDtos) { List trees = new ArrayList<>(); Set ids = new HashSet<>(); for (MenuDto menuDTO : menuDtos) { if (menuDTO.getPid() == null) { trees.add(menuDTO); } for (MenuDto it : menuDtos) { if (menuDTO.getId().equals(it.getPid())) { if (menuDTO.getChildren() == null) { menuDTO.setChildren(new ArrayList<>()); } menuDTO.getChildren().add(it); ids.add(it.getId()); } } } if(trees.isEmpty()){ trees = menuDtos.stream().filter(s -> !ids.contains(s.getId())).collect(Collectors.toList()); } return trees; } @Override public List buildMenus(List menuDtos) { List list = new LinkedList<>(); menuDtos.forEach(menuDTO -> { if (menuDTO!=null){ List menuDtoList = menuDTO.getChildren(); MenuVo menuVo = new MenuVo(); menuVo.setName(ObjectUtil.isNotEmpty(menuDTO.getComponentName()) ? menuDTO.getComponentName() : menuDTO.getTitle()); // 一级目录需要加斜杠,不然会报警告 menuVo.setPath(menuDTO.getPid() == null ? "/" + menuDTO.getPath() :menuDTO.getPath()); menuVo.setHidden(menuDTO.getHidden()); // 如果不是外链 if(!menuDTO.getIFrame()){ if(menuDTO.getPid() == null){ menuVo.setComponent(StringUtils.isEmpty(menuDTO.getComponent())?"Layout":menuDTO.getComponent()); // 如果不是一级菜单,并且菜单类型为目录,则代表是多级菜单 }else if(menuDTO.getType() == 0){ menuVo.setComponent(StringUtils.isEmpty(menuDTO.getComponent())?"ParentView":menuDTO.getComponent()); }else if(StringUtils.isNoneBlank(menuDTO.getComponent())){ menuVo.setComponent(menuDTO.getComponent()); } } menuVo.setMeta(new MenuMetaVo(menuDTO.getTitle(),menuDTO.getIcon(),!menuDTO.getCache())); if(CollectionUtil.isNotEmpty(menuDtoList)){ menuVo.setAlwaysShow(true); menuVo.setRedirect("noredirect"); menuVo.setChildren(buildMenus(menuDtoList)); // 处理是一级菜单并且没有子菜单的情况 } else if(menuDTO.getPid() == null){ MenuVo menuVo1 = getMenuVo(menuDTO, menuVo); menuVo.setName(null); menuVo.setMeta(null); menuVo.setComponent("Layout"); List list1 = new ArrayList<>(); list1.add(menuVo1); menuVo.setChildren(list1); } list.add(menuVo); } } ); return list; } @Override public Menu findOne(Long id) { Menu menu = menuRepository.findById(id).orElseGet(Menu::new); ValidationUtil.isNull(menu.getId(),"Menu","id",id); return menu; } @Override public void download(List menuDtos, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (MenuDto menuDTO : menuDtos) { Map map = new LinkedHashMap<>(); map.put("菜单标题", menuDTO.getTitle()); map.put("菜单类型", menuDTO.getType() == null ? "目录" : menuDTO.getType() == 1 ? "菜单" : "按钮"); map.put("权限标识", menuDTO.getPermission()); map.put("外链菜单", menuDTO.getIFrame() ? YES_STR : NO_STR); map.put("菜单可见", menuDTO.getHidden() ? NO_STR : YES_STR); map.put("是否缓存", menuDTO.getCache() ? YES_STR : NO_STR); map.put("创建日期", menuDTO.getCreateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } private void updateSubCnt(Long menuId){ if(menuId != null){ int count = menuRepository.countByPid(menuId); menuRepository.updateSubCntById(count, menuId); } } /** * 清理缓存 * @param id 菜单ID */ public void delCaches(Long id){ List users = userRepository.findByMenuId(id); redisUtils.del(CacheKey.MENU_ID + id); redisUtils.delByKeys(CacheKey.MENU_USER, users.stream().map(User::getId).collect(Collectors.toSet())); // 清除 Role 缓存 List roles = roleService.findInMenuId(new ArrayList(){{ add(id); }}); redisUtils.delByKeys(CacheKey.ROLE_ID, roles.stream().map(Role::getId).collect(Collectors.toSet())); } /** * 构建前端路由 * @param menuDTO / * @param menuVo / * @return / */ private static MenuVo getMenuVo(MenuDto menuDTO, MenuVo menuVo) { MenuVo menuVo1 = new MenuVo(); menuVo1.setMeta(menuVo.getMeta()); // 非外链 if(!menuDTO.getIFrame()){ menuVo1.setPath("index"); menuVo1.setName(menuVo.getName()); menuVo1.setComponent(menuVo.getComponent()); } else { menuVo1.setPath(menuDTO.getPath()); } return menuVo1; } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/MonitorServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.impl; import cn.hutool.core.date.BetweenFormatter.Level; import cn.hutool.core.date.DateUtil; import lombok.extern.slf4j.Slf4j; import me.zhengjie.modules.system.service.MonitorService; import me.zhengjie.utils.ElConstant; import me.zhengjie.utils.FileUtil; import me.zhengjie.utils.StringUtils; import org.springframework.stereotype.Service; import oshi.SystemInfo; import oshi.hardware.*; import oshi.software.os.FileSystem; import oshi.software.os.OSFileStore; import oshi.software.os.OperatingSystem; import oshi.util.FormatUtil; import oshi.util.Util; import java.lang.management.ManagementFactory; import java.text.DecimalFormat; import java.util.*; /** * @author Zheng Jie * @date 2020-05-02 */ @Slf4j @Service public class MonitorServiceImpl implements MonitorService { private final DecimalFormat df = new DecimalFormat("0.00"); @Override public Map getServers(){ Map resultMap = new LinkedHashMap<>(8); try { SystemInfo si = new SystemInfo(); OperatingSystem os = si.getOperatingSystem(); HardwareAbstractionLayer hal = si.getHardware(); // 系统信息 resultMap.put("sys", getSystemInfo(os)); // cpu 信息 resultMap.put("cpu", getCpuInfo(hal.getProcessor())); // 内存信息 resultMap.put("memory", getMemoryInfo(hal.getMemory())); // 交换区信息 resultMap.put("swap", getSwapInfo(hal.getMemory())); // 磁盘 resultMap.put("disk", getDiskInfo(os)); resultMap.put("time", DateUtil.format(new Date(), "HH:mm:ss")); } catch (Exception e) { log.error(e.getMessage(), e); } return resultMap; } /** * 获取磁盘信息 * @return / */ private Map getDiskInfo(OperatingSystem os) { Map diskInfo = new LinkedHashMap<>(); FileSystem fileSystem = os.getFileSystem(); List fsArray = fileSystem.getFileStores(); String osName = System.getProperty("os.name"); long available = 0, total = 0; for (OSFileStore fs : fsArray){ // windows 需要将所有磁盘分区累加,linux 和 mac 直接累加会出现磁盘重复的问题,待修复 if(osName.toLowerCase().startsWith(ElConstant.WIN)) { available += fs.getUsableSpace(); total += fs.getTotalSpace(); } else { available = fs.getUsableSpace(); total = fs.getTotalSpace(); break; } } long used = total - available; diskInfo.put("total", total > 0 ? FileUtil.getSize(total) : "?"); diskInfo.put("available", FileUtil.getSize(available)); diskInfo.put("used", FileUtil.getSize(used)); if(total != 0){ diskInfo.put("usageRate", df.format(used/(double)total * 100)); } else { diskInfo.put("usageRate", 0); } return diskInfo; } /** * 获取交换区信息 * @param memory / * @return / */ private Map getSwapInfo(GlobalMemory memory) { Map swapInfo = new LinkedHashMap<>(); VirtualMemory virtualMemory = memory.getVirtualMemory(); long total = virtualMemory.getSwapTotal(); long used = virtualMemory.getSwapUsed(); swapInfo.put("total", FormatUtil.formatBytes(total)); swapInfo.put("used", FormatUtil.formatBytes(used)); swapInfo.put("available", FormatUtil.formatBytes(total - used)); if(used == 0){ swapInfo.put("usageRate", 0); } else { swapInfo.put("usageRate", df.format(used/(double)total * 100)); } return swapInfo; } /** * 获取内存信息 * @param memory / * @return / */ private Map getMemoryInfo(GlobalMemory memory) { Map memoryInfo = new LinkedHashMap<>(); memoryInfo.put("total", FormatUtil.formatBytes(memory.getTotal())); memoryInfo.put("available", FormatUtil.formatBytes(memory.getAvailable())); memoryInfo.put("used", FormatUtil.formatBytes(memory.getTotal() - memory.getAvailable())); memoryInfo.put("usageRate", df.format((memory.getTotal() - memory.getAvailable())/(double)memory.getTotal() * 100)); return memoryInfo; } /** * 获取Cpu相关信息 * @param processor / * @return / */ private Map getCpuInfo(CentralProcessor processor) { Map cpuInfo = new LinkedHashMap<>(); cpuInfo.put("name", processor.getProcessorIdentifier().getName()); cpuInfo.put("package", processor.getPhysicalPackageCount() + "个物理CPU"); cpuInfo.put("core", processor.getPhysicalProcessorCount() + "个物理核心"); cpuInfo.put("coreNumber", processor.getPhysicalProcessorCount()); cpuInfo.put("logic", processor.getLogicalProcessorCount() + "个逻辑CPU"); // CPU信息 long[] prevTicks = processor.getSystemCpuLoadTicks(); // 默认等待300毫秒... long time = 300; Util.sleep(time); long[] ticks = processor.getSystemCpuLoadTicks(); while (Arrays.toString(prevTicks).equals(Arrays.toString(ticks)) && time < 1000){ time += 25; Util.sleep(25); ticks = processor.getSystemCpuLoadTicks(); } long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()]; long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()]; long sys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()]; long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()]; long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()]; long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()]; long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()]; long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()]; long totalCpu = user + nice + sys + idle + iowait + irq + softirq + steal; cpuInfo.put("used", df.format(100d * user / totalCpu + 100d * sys / totalCpu)); cpuInfo.put("idle", df.format(100d * idle / totalCpu)); return cpuInfo; } /** * 获取系统相关信息,系统、运行天数、系统IP * @param os / * @return / */ private Map getSystemInfo(OperatingSystem os){ Map systemInfo = new LinkedHashMap<>(); // jvm 运行时间 long time = ManagementFactory.getRuntimeMXBean().getStartTime(); Date date = new Date(time); // 计算项目运行时间 String formatBetween = DateUtil.formatBetween(date, new Date(), Level.HOUR); // 系统信息 systemInfo.put("os", os.toString()); systemInfo.put("day", formatBetween); systemInfo.put("ip", StringUtils.getLocalIp()); return systemInfo; } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/RoleServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import lombok.RequiredArgsConstructor; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.security.service.UserCacheManager; import me.zhengjie.modules.security.service.dto.AuthorityDto; import me.zhengjie.modules.system.domain.Menu; import me.zhengjie.modules.system.domain.Role; import me.zhengjie.exception.EntityExistException; import me.zhengjie.modules.system.domain.User; import me.zhengjie.modules.system.repository.RoleRepository; import me.zhengjie.modules.system.repository.UserRepository; import me.zhengjie.modules.system.service.RoleService; import me.zhengjie.modules.system.service.dto.RoleDto; import me.zhengjie.modules.system.service.dto.RoleQueryCriteria; import me.zhengjie.modules.system.service.dto.RoleSmallDto; import me.zhengjie.modules.system.service.dto.UserDto; import me.zhengjie.modules.system.service.mapstruct.RoleMapper; import me.zhengjie.modules.system.service.mapstruct.RoleSmallMapper; import me.zhengjie.utils.*; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * @author Zheng Jie * @date 2018-12-03 */ @Service @RequiredArgsConstructor public class RoleServiceImpl implements RoleService { private final RoleRepository roleRepository; private final RoleMapper roleMapper; private final RoleSmallMapper roleSmallMapper; private final RedisUtils redisUtils; private final UserRepository userRepository; private final UserCacheManager userCacheManager; @Override public List queryAll() { Sort sort = Sort.by(Sort.Direction.ASC, "level"); return roleMapper.toDto(roleRepository.findAll(sort)); } @Override public List queryAll(RoleQueryCriteria criteria) { return roleMapper.toDto(roleRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder))); } @Override public PageResult queryAll(RoleQueryCriteria criteria, Pageable pageable) { Page page = roleRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable); return PageUtil.toPage(page.map(roleMapper::toDto)); } @Override public RoleDto findById(long id) { String key = CacheKey.ROLE_ID + id; Role role = redisUtils.get(key, Role.class); if (role == null) { role = roleRepository.findById(id).orElseGet(Role::new); ValidationUtil.isNull(role.getId(), "Role", "id", id); redisUtils.set(key, role, 1, TimeUnit.DAYS); } return roleMapper.toDto(role); } @Override @Transactional(rollbackFor = Exception.class) public void create(Role resources) { if (roleRepository.findByName(resources.getName()) != null) { throw new EntityExistException(Role.class, "username", resources.getName()); } roleRepository.save(resources); } @Override @Transactional(rollbackFor = Exception.class) public void update(Role resources) { Role role = roleRepository.findById(resources.getId()).orElseGet(Role::new); ValidationUtil.isNull(role.getId(), "Role", "id", resources.getId()); Role role1 = roleRepository.findByName(resources.getName()); if (role1 != null && !role1.getId().equals(role.getId())) { throw new EntityExistException(Role.class, "username", resources.getName()); } role.setName(resources.getName()); role.setDescription(resources.getDescription()); role.setDataScope(resources.getDataScope()); role.setDepts(resources.getDepts()); role.setLevel(resources.getLevel()); roleRepository.save(role); // 更新相关缓存 delCaches(role.getId(), null); } @Override public void updateMenu(Role resources, RoleDto roleDTO) { Role role = roleMapper.toEntity(roleDTO); List users = userRepository.findByRoleId(role.getId()); // 更新菜单 role.setMenus(resources.getMenus()); delCaches(resources.getId(), users); roleRepository.save(role); } @Override @Transactional(rollbackFor = Exception.class) public void untiedMenu(Long menuId) { // 更新菜单 roleRepository.untiedMenu(menuId); } @Override @Transactional(rollbackFor = Exception.class) public void delete(Set ids) { for (Long id : ids) { // 更新相关缓存 delCaches(id, null); } roleRepository.deleteAllByIdIn(ids); } @Override public List findByUsersId(Long userId) { String key = CacheKey.ROLE_USER + userId; List roles = redisUtils.getList(key, RoleSmallDto.class); if (CollUtil.isEmpty(roles)) { roles = roleSmallMapper.toDto(new ArrayList<>(roleRepository.findByUserId(userId))); redisUtils.set(key, roles, 1, TimeUnit.DAYS); } return roles; } @Override public Integer findByRoles(Set roles) { if (roles.isEmpty()) { return Integer.MAX_VALUE; } Set roleDtos = new HashSet<>(); for (Role role : roles) { roleDtos.add(findById(role.getId())); } return Collections.min(roleDtos.stream().map(RoleDto::getLevel).collect(Collectors.toList())); } @Override public List buildPermissions(UserDto user) { String key = CacheKey.ROLE_AUTH + user.getId(); List authorityDtos = redisUtils.getList(key, AuthorityDto.class); if (CollUtil.isEmpty(authorityDtos)) { Set permissions = new HashSet<>(); // 如果是管理员直接返回 if (user.getIsAdmin()) { permissions.add("admin"); return permissions.stream().map(AuthorityDto::new) .collect(Collectors.toList()); } Set roles = roleRepository.findByUserId(user.getId()); permissions = roles.stream().flatMap(role -> role.getMenus().stream()) .map(Menu::getPermission) .filter(StringUtils::isNotBlank).collect(Collectors.toSet()); authorityDtos = permissions.stream().map(AuthorityDto::new) .collect(Collectors.toList()); redisUtils.set(key, authorityDtos, 1, TimeUnit.HOURS); } return authorityDtos; } @Override public void download(List roles, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (RoleDto role : roles) { Map map = new LinkedHashMap<>(); map.put("角色名称", role.getName()); map.put("角色级别", role.getLevel()); map.put("描述", role.getDescription()); map.put("创建日期", role.getCreateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } @Override public void verification(Set ids) { if (userRepository.countByRoles(ids) > 0) { throw new BadRequestException("所选角色存在用户关联,请解除关联再试!"); } } @Override public List findInMenuId(List menuIds) { return roleRepository.findInMenuId(menuIds); } /** * 清理缓存 * @param id / */ public void delCaches(Long id, List users) { users = CollectionUtil.isEmpty(users) ? userRepository.findByRoleId(id) : users; if (CollectionUtil.isNotEmpty(users)) { users.forEach(item -> userCacheManager.cleanUserCache(item.getUsername())); Set userIds = users.stream().map(User::getId).collect(Collectors.toSet()); redisUtils.delByKeys(CacheKey.DATA_USER, userIds); redisUtils.delByKeys(CacheKey.MENU_USER, userIds); redisUtils.delByKeys(CacheKey.ROLE_AUTH, userIds); redisUtils.delByKeys(CacheKey.ROLE_USER, userIds); } redisUtils.del(CacheKey.ROLE_ID + id); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/UserServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.impl; import lombok.RequiredArgsConstructor; import me.zhengjie.utils.PageResult; import me.zhengjie.config.properties.FileProperties; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.security.service.OnlineUserService; import me.zhengjie.modules.security.service.UserCacheManager; import me.zhengjie.modules.system.domain.User; import me.zhengjie.exception.EntityExistException; import me.zhengjie.exception.EntityNotFoundException; import me.zhengjie.modules.system.repository.UserRepository; import me.zhengjie.modules.system.service.UserService; import me.zhengjie.modules.system.service.dto.*; import me.zhengjie.modules.system.service.mapstruct.UserMapper; import me.zhengjie.utils.*; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotBlank; import java.io.File; import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * @author Zheng Jie * @date 2018-11-23 */ @Service @RequiredArgsConstructor public class UserServiceImpl implements UserService { private final UserRepository userRepository; private final UserMapper userMapper; private final FileProperties properties; private final RedisUtils redisUtils; private final UserCacheManager userCacheManager; private final OnlineUserService onlineUserService; @Override public PageResult queryAll(UserQueryCriteria criteria, Pageable pageable) { Page page = userRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable); return PageUtil.toPage(page.map(userMapper::toDto)); } @Override public List queryAll(UserQueryCriteria criteria) { List users = userRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)); return userMapper.toDto(users); } @Override @Transactional(rollbackFor = Exception.class) public UserDto findById(long id) { String key = CacheKey.USER_ID + id; User user = redisUtils.get(key, User.class); if (user == null) { user = userRepository.findById(id).orElseGet(User::new); ValidationUtil.isNull(user.getId(), "User", "id", id); redisUtils.set(key, user, 1, TimeUnit.DAYS); } return userMapper.toDto(user); } @Override @Transactional(rollbackFor = Exception.class) public void create(User resources) { if (userRepository.findByUsername(resources.getUsername()) != null) { throw new EntityExistException(User.class, "username", resources.getUsername()); } if (userRepository.findByEmail(resources.getEmail()) != null) { throw new EntityExistException(User.class, "email", resources.getEmail()); } if (userRepository.findByPhone(resources.getPhone()) != null) { throw new EntityExistException(User.class, "phone", resources.getPhone()); } userRepository.save(resources); } @Override @Transactional(rollbackFor = Exception.class) public void update(User resources) throws Exception { User user = userRepository.findById(resources.getId()).orElseGet(User::new); ValidationUtil.isNull(user.getId(), "User", "id", resources.getId()); User user1 = userRepository.findByUsername(resources.getUsername()); User user2 = userRepository.findByEmail(resources.getEmail()); User user3 = userRepository.findByPhone(resources.getPhone()); if (user1 != null && !user.getId().equals(user1.getId())) { throw new EntityExistException(User.class, "username", resources.getUsername()); } if (user2 != null && !user.getId().equals(user2.getId())) { throw new EntityExistException(User.class, "email", resources.getEmail()); } if (user3 != null && !user.getId().equals(user3.getId())) { throw new EntityExistException(User.class, "phone", resources.getPhone()); } // 如果用户的角色改变 if (!resources.getRoles().equals(user.getRoles())) { redisUtils.del(CacheKey.DATA_USER + resources.getId()); redisUtils.del(CacheKey.MENU_USER + resources.getId()); redisUtils.del(CacheKey.ROLE_AUTH + resources.getId()); redisUtils.del(CacheKey.ROLE_USER + resources.getId()); } // 修改部门会影响 数据权限 if (!Objects.equals(resources.getDept(),user.getDept())) { redisUtils.del(CacheKey.DATA_USER + resources.getId()); } // 如果用户被禁用,则清除用户登录信息 if(!resources.getEnabled()){ onlineUserService.kickOutForUsername(resources.getUsername()); } user.setUsername(resources.getUsername()); user.setEmail(resources.getEmail()); user.setEnabled(resources.getEnabled()); user.setRoles(resources.getRoles()); user.setDept(resources.getDept()); user.setJobs(resources.getJobs()); user.setPhone(resources.getPhone()); user.setNickName(resources.getNickName()); user.setGender(resources.getGender()); userRepository.save(user); // 清除缓存 delCaches(user.getId(), user.getUsername()); } @Override @Transactional(rollbackFor = Exception.class) public void updateCenter(User resources) { User user = userRepository.findById(resources.getId()).orElseGet(User::new); User user1 = userRepository.findByPhone(resources.getPhone()); if (user1 != null && !user.getId().equals(user1.getId())) { throw new EntityExistException(User.class, "phone", resources.getPhone()); } user.setNickName(resources.getNickName()); user.setPhone(resources.getPhone()); user.setGender(resources.getGender()); userRepository.save(user); // 清理缓存 delCaches(user.getId(), user.getUsername()); } @Override @Transactional(rollbackFor = Exception.class) public void delete(Set ids) { for (Long id : ids) { // 清理缓存 UserDto user = findById(id); delCaches(user.getId(), user.getUsername()); } userRepository.deleteAllByIdIn(ids); } @Override public UserDto findByName(String userName) { User user = userRepository.findByUsername(userName); if (user == null) { throw new EntityNotFoundException(User.class, "name", userName); } else { return userMapper.toDto(user); } } @Override public UserDto getLoginData(String userName) { User user = userRepository.findByUsername(userName); if (user == null) { return null; } else { return userMapper.toDto(user); } } @Override @Transactional(rollbackFor = Exception.class) public void updatePass(String username, String pass) { userRepository.updatePass(username, pass, new Date()); flushCache(username); } @Override @Transactional(rollbackFor = Exception.class) public void resetPwd(Set ids, String pwd) { List users = userRepository.findAllById(ids); // 清除缓存 users.forEach(user -> { // 清除缓存 flushCache(user.getUsername()); // 强制退出 onlineUserService.kickOutForUsername(user.getUsername()); }); // 重置密码 userRepository.resetPwd(ids, pwd); } @Override @Transactional(rollbackFor = Exception.class) public Map updateAvatar(MultipartFile multipartFile) { // 文件大小验证 FileUtil.checkSize(properties.getAvatarMaxSize(), multipartFile.getSize()); // 验证文件上传的格式 String image = "gif jpg png jpeg"; String fileType = FileUtil.getExtensionName(multipartFile.getOriginalFilename()); if(fileType != null && !image.contains(fileType)){ throw new BadRequestException("文件格式错误!, 仅支持 " + image +" 格式"); } User user = userRepository.findByUsername(SecurityUtils.getCurrentUsername()); String oldPath = user.getAvatarPath(); File file = FileUtil.upload(multipartFile, properties.getPath().getAvatar()); user.setAvatarPath(Objects.requireNonNull(file).getPath()); user.setAvatarName(file.getName()); userRepository.save(user); if (StringUtils.isNotBlank(oldPath)) { FileUtil.del(oldPath); } @NotBlank String username = user.getUsername(); flushCache(username); return new HashMap(1) {{ put("avatar", file.getName()); }}; } @Override @Transactional(rollbackFor = Exception.class) public void updateEmail(String username, String email) { userRepository.updateEmail(username, email); flushCache(username); } @Override public void download(List queryAll, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (UserDto userDTO : queryAll) { List roles = userDTO.getRoles().stream().map(RoleSmallDto::getName).collect(Collectors.toList()); Map map = new LinkedHashMap<>(); map.put("用户名", userDTO.getUsername()); map.put("角色", roles); map.put("部门", userDTO.getDept().getName()); map.put("岗位", userDTO.getJobs().stream().map(JobSmallDto::getName).collect(Collectors.toList())); map.put("邮箱", userDTO.getEmail()); map.put("状态", userDTO.getEnabled() ? "启用" : "禁用"); map.put("手机号码", userDTO.getPhone()); map.put("修改密码的时间", userDTO.getPwdResetTime()); map.put("创建日期", userDTO.getCreateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } /** * 清理缓存 * * @param id / */ public void delCaches(Long id, String username) { redisUtils.del(CacheKey.USER_ID + id); flushCache(username); } /** * 清理 登陆时 用户缓存信息 * * @param username / */ private void flushCache(String username) { userCacheManager.cleanUserCache(username); } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/VerifyServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.impl; import cn.hutool.core.lang.Dict; import cn.hutool.core.util.RandomUtil; import cn.hutool.extra.template.Template; import cn.hutool.extra.template.TemplateConfig; import cn.hutool.extra.template.TemplateEngine; import cn.hutool.extra.template.TemplateUtil; import lombok.RequiredArgsConstructor; import me.zhengjie.domain.vo.EmailVo; import me.zhengjie.exception.BadRequestException; import me.zhengjie.modules.system.service.VerifyService; import me.zhengjie.utils.RedisUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Collections; /** * @author Zheng Jie * @date 2018-12-26 */ @Service @RequiredArgsConstructor public class VerifyServiceImpl implements VerifyService { @Value("${code.expiration}") private Long expiration; private final RedisUtils redisUtils; @Override @Transactional(rollbackFor = Exception.class) public EmailVo sendEmail(String email, String key) { EmailVo emailVo; String content; String redisKey = key + email; // 如果不存在有效的验证码,就创建一个新的 TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH)); Template template = engine.getTemplate("email.ftl"); String oldCode = redisUtils.get(redisKey, String.class); if(oldCode == null){ String code = RandomUtil.randomNumbers (6); // 存入缓存 if(!redisUtils.set(redisKey, code, expiration)){ throw new BadRequestException("服务异常,请联系网站负责人"); } content = template.render(Dict.create().set("code",code)); // 存在就再次发送原来的验证码 } else { content = template.render(Dict.create().set("code",oldCode)); } emailVo = new EmailVo(Collections.singletonList(email),"ELADMIN后台管理系统",content); return emailVo; } @Override public void validated(String key, String code) { String value = redisUtils.get(key, String.class); if(!code.equals(value)){ throw new BadRequestException("无效验证码"); } else { redisUtils.del(key); } } } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DeptMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.system.domain.Dept; import me.zhengjie.modules.system.service.dto.DeptDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author Zheng Jie * @date 2019-03-25 */ @Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface DeptMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DeptSmallMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.system.domain.Dept; import me.zhengjie.modules.system.service.dto.DeptSmallDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author Zheng Jie * @date 2019-03-25 */ @Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface DeptSmallMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DictDetailMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.system.domain.DictDetail; import me.zhengjie.modules.system.service.dto.DictDetailDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author Zheng Jie * @date 2019-04-10 */ @Mapper(componentModel = "spring", uses = {DictSmallMapper.class}, unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface DictDetailMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DictMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.system.domain.Dict; import me.zhengjie.modules.system.service.dto.DictDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author Zheng Jie * @date 2019-04-10 */ @Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface DictMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DictSmallMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.system.domain.Dict; import me.zhengjie.modules.system.service.dto.DictSmallDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author Zheng Jie * @date 2019-04-10 */ @Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface DictSmallMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/JobMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.system.domain.Job; import me.zhengjie.modules.system.service.dto.JobDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author Zheng Jie * @date 2019-03-29 */ @Mapper(componentModel = "spring",uses = {DeptMapper.class},unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface JobMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/JobSmallMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.system.domain.Job; import me.zhengjie.modules.system.service.dto.JobSmallDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author Zheng Jie * @date 2019-03-29 */ @Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface JobSmallMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/MenuMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.system.domain.Menu; import me.zhengjie.modules.system.service.dto.MenuDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author Zheng Jie * @date 2018-12-17 */ @Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface MenuMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/RoleMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.system.domain.Role; import me.zhengjie.modules.system.service.dto.RoleDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author Zheng Jie * @date 2018-11-23 */ @Mapper(componentModel = "spring", uses = {MenuMapper.class, DeptMapper.class}, unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface RoleMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/RoleSmallMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.system.domain.Role; import me.zhengjie.modules.system.service.dto.RoleSmallDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author Zheng Jie * @date 2019-5-23 */ @Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface RoleSmallMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/UserMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.modules.system.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.modules.system.domain.User; import me.zhengjie.modules.system.service.dto.UserDto; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author Zheng Jie * @date 2018-11-23 */ @Mapper(componentModel = "spring",uses = {RoleMapper.class, DeptMapper.class, JobMapper.class},unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface UserMapper extends BaseMapper { } ================================================ FILE: eladmin-system/src/main/java/me/zhengjie/sysrunner/SystemRunner.java ================================================ /* * Copyright 2019-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.sysrunner; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; /** * @author Zheng Jie * @description 程序启动后处理数据 * @date 2025-01-13 **/ @Slf4j @Component @RequiredArgsConstructor public class SystemRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) { } } ================================================ FILE: eladmin-system/src/main/resources/banner.txt ================================================ _ _ _ | | | | (_) ___| |______ __ _ __| |_ __ ___ _ _ __ / _ | |______/ _` |/ _` | '_ ` _ \| | '_ \ | __| | | (_| | (_| | | | | | | | | | | \___|_| \__,_|\__,_|_| |_| |_|_|_| |_| :: Spring Boot :: (v2.6.4) ================================================ FILE: eladmin-system/src/main/resources/config/application-dev.yml ================================================ #配置数据源 spring: datasource: druid: db-type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:mysql://localhost:3306/eladmin?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false username: root password: 123456 # 初始连接数,建议设置为与最小空闲连接数相同 initial-size: 20 # 最小空闲连接数,保持足够的空闲连接以应对请求 min-idle: 20 # 最大连接数,根据并发需求适当增加 max-active: 50 # 获取连接超时时间(毫秒),调整以满足响应时间要求 max-wait: 3000 # 启用KeepAlive机制,保持长连接 keep-alive: true # 连接有效性检测间隔时间(毫秒),定期检查连接的健康状态 time-between-eviction-runs-millis: 60000 # 连接在池中最小生存时间(毫秒),确保连接在池中至少存在一段时间 min-evictable-idle-time-millis: 300000 # 连接在池中最大生存时间(毫秒),防止连接在池中停留过长 max-evictable-idle-time-millis: 900000 # 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除 test-while-idle: true # 指明是否在从池中取出连接前进行检验,如果检验失败, 则从池中去除连接并尝试取出另一个 test-on-borrow: true # 是否在归还到池中前进行检验 test-on-return: false # 停用 com_ping 探活机制 use-ping-method: false # 检测连接是否有效 validation-query: SELECT 1 # 配置监控统计 webStatFilter: enabled: true stat-view-servlet: enabled: true url-pattern: /druid/* reset-enable: false filter: stat: enabled: true # 记录慢SQL log-slow-sql: true slow-sql-millis: 2000 merge-sql: true wall: config: multi-statement-allow: true # 登录相关配置 login: # 是否限制单用户登录 single-login: false # Redis用户登录缓存配置 user-cache: # 存活时间/秒 idle-time: 21600 # 验证码 code: # 验证码类型配置 查看 LoginProperties 类 code-type: arithmetic # 登录图形验证码有效时间/分钟 expiration: 2 # 验证码高度 width: 111 # 验证码宽度 height: 36 # 内容长度 length: 2 # 字体名称,为空则使用默认字体 font-name: # 字体大小 font-size: 25 #jwt jwt: header: Authorization # 令牌前缀 token-start-with: Bearer # 必须使用最少88位的Base64对该令牌进行编码 base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI= # 令牌过期时间 此处单位/毫秒 ,默认4小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html token-validity-in-seconds: 14400000 # 在线用户key online-key: "online_token:" # 验证码 code-key: "captcha_code:" # token 续期检查时间范围(默认30分钟,单位毫秒),在token即将过期的一段时间内用户操作了,则给用户的token续期 detect: 1800000 # 续期时间范围,默认1小时,单位毫秒 renew: 3600000 #是否允许生成代码,生产环境设置为false generator: enabled: true #是否开启 swagger-ui swagger: enabled: true # 文件存储路径 file: mac: path: ~/file/ avatar: ~/avatar/ linux: path: /home/eladmin/file/ avatar: /home/eladmin/avatar/ windows: path: C:\eladmin\file\ avatar: C:\eladmin\avatar\ # 文件大小 /M maxSize: 100 avatarMaxSize: 5 # 亚马逊S3协议云存储配置 #支持七牛云,阿里云OSS,腾讯云COS,华为云OBS,移动云EOS等 amz: s3: # 地域 region: test # 地域对应的 endpoint endPoint: https://s3.test.com # 访问的域名 domain: https://s3.test.com # 账号的认证信息,或者子账号的认证信息 accessKey: 填写你的AccessKey secretKey: 填写你的SecretKey # 存储桶(Bucket) defaultBucket: 填写你的存储桶名称 # 文件存储路径 timeformat: yyyy-MM ================================================ FILE: eladmin-system/src/main/resources/config/application-prod.yml ================================================ #配置数据源 spring: datasource: druid: db-type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:eladmin}?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false username: ${DB_USER:root} password: ${DB_PWD:123456} # 初始连接数,建议设置为与最小空闲连接数相同 initial-size: 20 # 最小空闲连接数,保持足够的空闲连接以应对请求 min-idle: 20 # 最大连接数,根据并发需求适当增加 max-active: 50 # 获取连接超时时间(毫秒),调整以满足响应时间要求 max-wait: 3000 # 启用KeepAlive机制,保持长连接 keep-alive: true # 连接有效性检测间隔时间(毫秒),定期检查连接的健康状态 time-between-eviction-runs-millis: 60000 # 连接在池中最小生存时间(毫秒),确保连接在池中至少存在一段时间 min-evictable-idle-time-millis: 300000 # 连接在池中最大生存时间(毫秒),防止连接在池中停留过长 max-evictable-idle-time-millis: 900000 # 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除 test-while-idle: true # 指明是否在从池中取出连接前进行检验,如果检验失败, 则从池中去除连接并尝试取出另一个 test-on-borrow: true # 是否在归还到池中前进行检验 test-on-return: false # 停用 com_ping 探活机制 use-ping-method: false # 检测连接是否有效 validation-query: SELECT 1 # 配置监控统计 webStatFilter: enabled: true stat-view-servlet: allow: enabled: true # 控制台管理用户名和密码 url-pattern: /druid/* reset-enable: false login-username: admin login-password: 123456 filter: stat: enabled: true # 记录慢SQL log-slow-sql: true slow-sql-millis: 2000 merge-sql: true wall: config: multi-statement-allow: true # 登录相关配置 login: # 是否限制单用户登录 single-login: false # Redis用户登录缓存配置 user-cache: # 存活时间/秒 idle-time: 21600 # 验证码 code: # 验证码类型配置 查看 LoginProperties 类 code-type: arithmetic # 登录图形验证码有效时间/分钟 expiration: 2 # 验证码高度 width: 111 # 验证码宽度 height: 36 # 内容长度 length: 2 # 字体名称,为空则使用默认字体,如遇到线上乱码,设置其他字体即可 font-name: # 字体大小 font-size: 25 #jwt jwt: header: Authorization # 令牌前缀 token-start-with: Bearer # 必须使用最少88位的Base64对该令牌进行编码 base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI= # 令牌过期时间 此处单位/毫秒 ,默认2小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html token-validity-in-seconds: 7200000 # 在线用户key online-key: "online_token:" # 验证码 code-key: "captcha_code:" # token 续期检查时间范围(默认30分钟,单位默认毫秒),在token即将过期的一段时间内用户操作了,则给用户的token续期 detect: 1800000 # 续期时间范围,默认 1小时,这里单位毫秒 renew: 3600000 #是否允许生成代码,生产环境设置为false generator: enabled: false #如果生产环境要开启swagger,需要配置请求地址 #springfox: # documentation: # swagger: # v2: # host: # 接口域名或外网ip #是否开启 swagger-ui swagger: enabled: false # 文件存储路径 file: mac: path: ~/file/ avatar: ~/avatar/ linux: path: /home/eladmin/file/ avatar: /home/eladmin/avatar/ windows: path: C:\eladmin\file\ avatar: C:\eladmin\avatar\ # 文件大小 /M maxSize: 100 avatarMaxSize: 5 # 亚马逊S3协议云存储配置 #支持七牛云,阿里云OSS,腾讯云COS,华为云OBS,移动云EOS等 amz: s3: # 地域 region: test # 地域对应的 endpoint endPoint: https://s3.test.com # 访问的域名 domain: https://s3.test.com # 账号的认证信息,或者子账号的认证信息 accessKey: 填写你的AccessKey secretKey: 填写你的SecretKey # 存储桶(Bucket) defaultBucket: 填写你的存储桶名称 # 文件存储路径 timeformat: yyyy-MM ================================================ FILE: eladmin-system/src/main/resources/config/application-quartz.yml ================================================ # 配置 quartz 分布式支持, sql 文件在 sql 目录下,需要导入到数据库,并且需要修改 application.yml 文件的 active: dev 配置 spring: quartz: # 必需,指定使用 JDBC 存储 job-store-type: jdbc properties: org: quartz: scheduler: # 必需,指定调度器实例的名称 instanceName: eladmin # 必需,指定调度器实例的 ID instanceId: auto threadPool: # 可选,线程池的线程数量,可以根据需要调整 threadCount: 5 jobStore: # 可选,如果你不需要集群,可以去掉 isClustered: true # 可选,集群检查间隔时间,可以根据需要调整 clusterCheckinInterval: 10000 # 必需,指定 JDBC 驱动代理类 driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate # 可选,是否使用属性存储,可以根据需要调整 useProperties: false # 必需,指定表的前缀 tablePrefix: qrtz_ # 可选,指定误触发阈值,可以根据需要调整 misfireThreshold: 60000 ================================================ FILE: eladmin-system/src/main/resources/config/application.yml ================================================ server: port: 8000 http2: # 启用 HTTP/2 支持,提升传输效率 enabled: true compression: # 启用 GZIP 压缩,减少传输数据量 enabled: true # 需要压缩的 MIME 类型 mime-types: text/html, text/xml, text/plain, application/json # 最小压缩响应大小(字节) spring: freemarker: check-template-location: false profiles: # 激活的环境,如果需要 quartz 分布式支持,需要修改 active: dev,quartz active: dev data: redis: repositories: enabled: false # pid: # file: /自行指定位置/eladmin.pid #配置 Jpa jpa: hibernate: ddl-auto: none open-in-view: true properties: hibernate: dialect: org.hibernate.dialect.MySQL5InnoDBDialect redis: #数据库索引 database: ${REDIS_DB:0} host: ${REDIS_HOST:127.0.0.1} port: ${REDIS_PORT:6379} password: ${REDIS_PWD:} #连接超时时间 timeout: 5000 # 连接池配置 lettuce: pool: # 连接池最大连接数 max-active: 30 # 连接池最大阻塞等待时间(毫秒),负值表示没有限制 max-wait: -1 # 连接池中的最大空闲连接数 max-idle: 20 # 连接池中的最小空闲连接数 min-idle: 1 task: pool: # 核心线程池大小 core-pool-size: 10 # 最大线程数 max-pool-size: 30 # 活跃时间 keep-alive-seconds: 60 # 队列容量 queue-capacity: 50 #邮箱验证码有效时间/秒 code: expiration: 300 #密码加密传输,前端公钥加密,后端私钥解密 rsa: private_key: MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A== ================================================ FILE: eladmin-system/src/main/resources/logback.xml ================================================ elAdmin ${log.pattern} ${log.charset} ================================================ FILE: eladmin-system/src/main/resources/spy.properties ================================================ # 应用的拦截模块 modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory # 自定义日志打印 logMessageFormat=me.zhengjie.config.CustomP6SpyLogger # 日志输出到控制台 appender=com.p6spy.engine.spy.appender.Slf4JLogger # 日期格式 dateformat=yyyy-MM-dd HH:mm:ss # 实际驱动可多个 driverlist=com.mysql.cj.jdbc.Driver # 是否开启慢SQL记录 outagedetection=true # 慢SQL记录标准 2 秒 outagedetectioninterval=2 # 是否过滤 Log filter=true # 过滤 Log 时所排除的 sql 关键字,以逗号分隔 exclude=SELECT 1,INSERT INTO sys_log # 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset. excludecategories=info,debug,result,commit,resultset ================================================ FILE: eladmin-system/src/main/resources/template/email.ftl ================================================

尊敬的用户,您好:

您正在申请邮箱验证,您的验证码为:

${code}

Copyright ©${.now?string("yyyy")} ELADMIN 后台管理系统 All Rights Reserved.
================================================ FILE: eladmin-system/src/main/resources/template/taskAlarm.ftl ================================================

任务信息:

任务名称 Bean名称 执行方法 参数内容 Cron表达式 描述内容
${task.jobName} ${task.beanName} ${task.methodName} ${(task.params)!""} ${task.cronExpression} ${(task.description)!""}

异常信息:

                ${msg}
            
Copyright ©${.now?string("yyyy")} ELADMIN 后台管理系统 All Rights Reserved.
================================================ FILE: eladmin-system/src/test/java/me/zhengjie/EladminSystemApplicationTests.java ================================================ package me.zhengjie; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class EladminSystemApplicationTests { @Test public void contextLoads() { } public static void main(String[] args) { } } ================================================ FILE: eladmin-tools/pom.xml ================================================ eladmin me.zhengjie 2.7 4.0.0 eladmin-tools 工具模块 1.4.7 4.22.57.ALL me.zhengjie eladmin-logging 2.7 javax.mail mail ${mail.version} com.alipay.sdk alipay-sdk-java ${alipay.version} software.amazon.awssdk s3 2.30.13 compile ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/config/AmzS3Config.java ================================================ package me.zhengjie.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; import java.net.URI; /** * @author Zheng Jie * @date 2025-06-25 */ @Data @Configuration @ConfigurationProperties(prefix = "amz.s3") public class AmzS3Config { /** * Amazon S3 的区域配置,例如 "us-west-2"。 * 该区域决定了 S3 存储桶的地理位置。 */ private String region; /** * Amazon S3 的端点 URL * 该端点用于访问 S3 服务。 */ private String endPoint; /** * Amazon S3 的域名 * 该域名用于构建访问 S3 服务的完整 URL。 */ private String domain; /** * Amazon S3 的访问密钥 ID,用于身份验证。 * 该密钥与 secretKey 一起使用来授权对 S3 服务的访问。 */ private String accessKey; /** * Amazon S3 的秘密访问密钥,用于身份验证。 * 该密钥与 accessKey 一起使用来授权对 S3 服务的访问。 */ private String secretKey; /** * 默认的 S3 存储桶名称。 * 该存储桶用于存储上传的文件和数据。 */ private String defaultBucket; /** * 文件上传后存储的文件夹格式,默认为 "yyyy-MM"。 */ private String timeformat; /** * 创建并返回一个 AmazonS3 客户端实例。 * 使用当前配置类的 endPoint, region, accessKey 和 secretKey。 * 声明为 @Bean 后,Spring 会将其作为单例管理,并在需要时自动注入。 * * @return 配置好的 AmazonS3 客户端实例 */ @Bean public S3Client amazonS3Client() { return S3Client.builder().region(Region.of(region)) .endpointOverride(URI.create(endPoint)) .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey))) .build(); } } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/domain/AlipayConfig.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.persistence.*; import javax.validation.constraints.NotBlank; import java.io.Serializable; /** * 支付宝配置类 * @author Zheng Jie * @date 2018-12-31 */ @Data @Entity @Table(name = "tool_alipay_config") public class AlipayConfig implements Serializable { @Id @Column(name = "config_id") @ApiModelProperty(value = "ID", hidden = true) private Long id; @NotBlank @ApiModelProperty(value = "应用ID") private String appId; @NotBlank @ApiModelProperty(value = "商户私钥") private String privateKey; @NotBlank @ApiModelProperty(value = "支付宝公钥") private String publicKey; @ApiModelProperty(value = "签名方式") private String signType="RSA2"; @Column(name = "gateway_url") @ApiModelProperty(value = "支付宝开放安全地址", hidden = true) private String gatewayUrl = "https://openapi.alipaydev.com/gateway.do"; @ApiModelProperty(value = "编码", hidden = true) private String charset= "utf-8"; @NotBlank @ApiModelProperty(value = "异步通知地址") private String notifyUrl; @NotBlank @ApiModelProperty(value = "订单完成后返回的页面") private String returnUrl; @ApiModelProperty(value = "类型") private String format="JSON"; @NotBlank @ApiModelProperty(value = "商户号") private String sysServiceProviderId; } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/domain/EmailConfig.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.persistence.*; import javax.validation.constraints.NotBlank; import java.io.Serializable; /** * 邮件配置类,数据存覆盖式存入数据存 * @author Zheng Jie * @date 2018-12-26 */ @Entity @Data @Table(name = "tool_email_config") public class EmailConfig implements Serializable { @Id @Column(name = "config_id") @ApiModelProperty(value = "ID", hidden = true) private Long id; @NotBlank @ApiModelProperty(value = "邮件服务器SMTP地址") private String host; @NotBlank @ApiModelProperty(value = "邮件服务器 SMTP 端口") private String port; @NotBlank @ApiModelProperty(value = "发件者用户名") private String user; @NotBlank @ApiModelProperty(value = "密码") private String pass; @NotBlank @ApiModelProperty(value = "收件人") private String fromUser; } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/domain/LocalStorage.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.domain; import io.swagger.annotations.ApiModelProperty; import lombok.*; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; import me.zhengjie.base.BaseEntity; import javax.persistence.*; import java.io.Serializable; /** * @author Zheng Jie * @date 2019-09-05 */ @Getter @Setter @Entity @Table(name="tool_local_storage") @NoArgsConstructor public class LocalStorage extends BaseEntity implements Serializable { @Id @Column(name = "storage_id") @ApiModelProperty(value = "ID", hidden = true) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ApiModelProperty(value = "真实文件名") private String realName; @ApiModelProperty(value = "文件名") private String name; @ApiModelProperty(value = "后缀") private String suffix; @ApiModelProperty(value = "路径") private String path; @ApiModelProperty(value = "类型") private String type; @ApiModelProperty(value = "大小") private String size; public LocalStorage(String realName,String name, String suffix, String path, String type, String size) { this.realName = realName; this.name = name; this.suffix = suffix; this.path = path; this.type = type; this.size = size; } public void copy(LocalStorage source){ BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); } } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/domain/S3Storage.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.domain; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import me.zhengjie.base.BaseEntity; import javax.persistence.*; import javax.validation.constraints.NotBlank; import java.io.Serializable; /** * @description S3存储实体类 * @author Zheng Jie * @date 2025-06-25 **/ @Data @Entity @Table(name = "tool_s3_storage") @EqualsAndHashCode(callSuper = true) public class S3Storage extends BaseEntity implements Serializable { @Id @Column(name = "storage_id") @ApiModelProperty(value = "ID", hidden = true) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotBlank @ApiModelProperty(value = "文件名称") private String fileName; @NotBlank @ApiModelProperty(value = "真实存储的名称") private String fileRealName; @NotBlank @ApiModelProperty(value = "文件大小") private String fileSize; @NotBlank @ApiModelProperty(value = "文件MIME 类型") private String fileMimeType; @NotBlank @ApiModelProperty(value = "文件类型") private String fileType; @NotBlank @ApiModelProperty(value = "文件路径") private String filePath; public void copy(S3Storage source){ BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); } } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/domain/enums/AliPayStatusEnum.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.domain.enums; /** * 支付状态 * @author zhengjie * @date 2018/08/01 16:45:43 */ public enum AliPayStatusEnum { /** 交易成功 */ FINISHED("TRADE_FINISHED"), /** 支付成功 */ SUCCESS("TRADE_SUCCESS"), /** 交易创建 */ BUYER_PAY("WAIT_BUYER_PAY"), /** 交易关闭 */ CLOSED("TRADE_CLOSED"); private final String value; AliPayStatusEnum(String value) { this.value = value; } public String getValue() { return value; } } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/domain/vo/EmailVo.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.domain.vo; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; import java.util.List; /** * 发送邮件时,接收参数的类 * @author 郑杰 * @date 2018/09/28 12:02:14 */ @Data @AllArgsConstructor @NoArgsConstructor public class EmailVo { @NotEmpty @ApiModelProperty(value = "收件人") private List tos; @NotBlank @ApiModelProperty(value = "主题") private String subject; @NotBlank @ApiModelProperty(value = "内容") private String content; } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/domain/vo/TradeVo.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.domain.vo; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.NotBlank; import java.sql.Date; import java.sql.Timestamp; /** * 交易详情,按需应该存入数据库,这里存入数据库,仅供临时测试 * @author Zheng Jie * @date 2018-12-31 */ @Data public class TradeVo { @NotBlank @ApiModelProperty(value = "商品描述") private String body; @NotBlank @ApiModelProperty(value = "商品名称") private String subject; @ApiModelProperty(value = "商户订单号", hidden = true) private String outTradeNo; @ApiModelProperty(value = "第三方订单号", hidden = true) private String tradeNo; @NotBlank @ApiModelProperty(value = "价格") private String totalAmount; @ApiModelProperty(value = "订单状态,已支付,未支付,作废", hidden = true) private String state; @ApiModelProperty(value = "创建时间", hidden = true) private Timestamp createTime; @ApiModelProperty(value = "作废时间", hidden = true) private Date cancelTime; } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/repository/AliPayRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.repository; import me.zhengjie.domain.AlipayConfig; import org.springframework.data.jpa.repository.JpaRepository; /** * @author Zheng Jie * @date 2018-12-31 */ public interface AliPayRepository extends JpaRepository { } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/repository/EmailRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.repository; import me.zhengjie.domain.EmailConfig; import org.springframework.data.jpa.repository.JpaRepository; /** * @author Zheng Jie * @date 2018-12-26 */ public interface EmailRepository extends JpaRepository { } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/repository/LocalStorageRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.repository; import me.zhengjie.domain.LocalStorage; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * @author Zheng Jie * @date 2019-09-05 */ public interface LocalStorageRepository extends JpaRepository, JpaSpecificationExecutor { } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/repository/S3StorageRepository.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.repository; import me.zhengjie.domain.S3Storage; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; /** * @author Zheng Jie * @date 2025-06-25 */ public interface S3StorageRepository extends JpaRepository, JpaSpecificationExecutor { /** * 根据ID查询文件路径 * @param id 文件ID * @return 文件路径 */ @Query(value = "SELECT file_path FROM s3_storage WHERE id = ?1", nativeQuery = true) String selectFilePathById(Long id); } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/rest/AliPayController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.zhengjie.annotation.rest.AnonymousAccess; import me.zhengjie.annotation.Log; import me.zhengjie.annotation.rest.AnonymousGetMapping; import me.zhengjie.domain.vo.TradeVo; import me.zhengjie.domain.AlipayConfig; import me.zhengjie.domain.enums.AliPayStatusEnum; import me.zhengjie.utils.AlipayUtils; import me.zhengjie.service.AliPayService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import springfox.documentation.annotations.ApiIgnore; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.nio.charset.StandardCharsets; import java.util.Map; /** * @author Zheng Jie * @date 2018-12-31 */ @Slf4j @RestController @RequiredArgsConstructor @RequestMapping("/api/aliPay") @Api(tags = "工具:支付宝管理") public class AliPayController { private final AlipayUtils alipayUtils; private final AliPayService alipayService; @GetMapping public ResponseEntity queryAliConfig() { return new ResponseEntity<>(alipayService.find(), HttpStatus.OK); } @Log("配置支付宝") @ApiOperation("配置支付宝") @PutMapping public ResponseEntity updateAliPayConfig(@Validated @RequestBody AlipayConfig alipayConfig) { alipayService.config(alipayConfig); return new ResponseEntity<>(HttpStatus.OK); } @Log("支付宝PC网页支付") @ApiOperation("PC网页支付") @PostMapping(value = "/toPayAsPC") public ResponseEntity toPayAsPc(@Validated @RequestBody TradeVo trade) throws Exception { AlipayConfig aliPay = alipayService.find(); trade.setOutTradeNo(alipayUtils.getOrderCode()); String payUrl = alipayService.toPayAsPc(aliPay, trade); return ResponseEntity.ok(payUrl); } @Log("支付宝手机网页支付") @ApiOperation("手机网页支付") @PostMapping(value = "/toPayAsWeb") public ResponseEntity toPayAsWeb(@Validated @RequestBody TradeVo trade) throws Exception { AlipayConfig alipay = alipayService.find(); trade.setOutTradeNo(alipayUtils.getOrderCode()); String payUrl = alipayService.toPayAsWeb(alipay, trade); return ResponseEntity.ok(payUrl); } @ApiIgnore @AnonymousGetMapping("/return") @ApiOperation("支付之后跳转的链接") public ResponseEntity returnPage(HttpServletRequest request, HttpServletResponse response) { AlipayConfig alipay = alipayService.find(); response.setContentType("text/html;charset=" + alipay.getCharset()); //内容验签,防止黑客篡改参数 if (alipayUtils.rsaCheck(request, alipay)) { //商户订单号 String outTradeNo = new String(request.getParameter("out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); //支付宝交易号 String tradeNo = new String(request.getParameter("trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); System.out.println("商户订单号" + outTradeNo + " " + "第三方交易号" + tradeNo); // 根据业务需要返回数据,这里统一返回OK return new ResponseEntity<>("payment successful", HttpStatus.OK); } else { // 根据业务需要返回数据 return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } } @ApiIgnore @RequestMapping("/notify") @AnonymousAccess @ApiOperation("支付异步通知(要公网访问),接收异步通知,检查通知内容app_id、out_trade_no、total_amount是否与请求中的一致,根据trade_status进行后续业务处理") public ResponseEntity notify(HttpServletRequest request) { AlipayConfig alipay = alipayService.find(); Map parameterMap = request.getParameterMap(); //内容验签,防止黑客篡改参数 if (alipayUtils.rsaCheck(request, alipay)) { //交易状态 String tradeStatus = new String(request.getParameter("trade_status").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); // 商户订单号 String outTradeNo = new String(request.getParameter("out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); //支付宝交易号 String tradeNo = new String(request.getParameter("trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); //付款金额 String totalAmount = new String(request.getParameter("total_amount").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); //验证 if (tradeStatus.equals(AliPayStatusEnum.SUCCESS.getValue()) || tradeStatus.equals(AliPayStatusEnum.FINISHED.getValue())) { // 验证通过后应该根据业务需要处理订单 } return new ResponseEntity<>(HttpStatus.OK); } return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/rest/EmailController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import me.zhengjie.annotation.Log; import me.zhengjie.domain.vo.EmailVo; import me.zhengjie.domain.EmailConfig; import me.zhengjie.service.EmailService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; /** * 发送邮件 * @author 郑杰 * @date 2018/09/28 6:55:53 */ @RestController @RequiredArgsConstructor @RequestMapping("api/email") @Api(tags = "工具:邮件管理") public class EmailController { private final EmailService emailService; @GetMapping public ResponseEntity queryEmailConfig(){ return new ResponseEntity<>(emailService.find(),HttpStatus.OK); } @Log("配置邮件") @PutMapping @ApiOperation("配置邮件") public ResponseEntity updateEmailConfig(@Validated @RequestBody EmailConfig emailConfig) throws Exception { emailService.config(emailConfig,emailService.find()); return new ResponseEntity<>(HttpStatus.OK); } @Log("发送邮件") @PostMapping @ApiOperation("发送邮件") public ResponseEntity sendEmail(@Validated @RequestBody EmailVo emailVo){ emailService.send(emailVo,emailService.find()); return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/rest/LocalStorageController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.rest; import lombok.RequiredArgsConstructor; import me.zhengjie.annotation.Log; import me.zhengjie.domain.LocalStorage; import me.zhengjie.exception.BadRequestException; import me.zhengjie.service.LocalStorageService; import me.zhengjie.service.dto.LocalStorageDto; import me.zhengjie.service.dto.LocalStorageQueryCriteria; import me.zhengjie.utils.FileUtil; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import io.swagger.annotations.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author Zheng Jie * @date 2019-09-05 */ @RestController @RequiredArgsConstructor @Api(tags = "工具:本地存储管理") @RequestMapping("/api/localStorage") public class LocalStorageController { private final LocalStorageService localStorageService; @GetMapping @ApiOperation("查询文件") @PreAuthorize("@el.check('storage:list')") public ResponseEntity> queryFile(LocalStorageQueryCriteria criteria, Pageable pageable){ return new ResponseEntity<>(localStorageService.queryAll(criteria,pageable),HttpStatus.OK); } @ApiOperation("导出数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('storage:list')") public void exportFile(HttpServletResponse response, LocalStorageQueryCriteria criteria) throws IOException { localStorageService.download(localStorageService.queryAll(criteria), response); } @PostMapping @ApiOperation("上传文件") @PreAuthorize("@el.check('storage:add')") public ResponseEntity createFile(@RequestParam String name, @RequestParam("file") MultipartFile file){ localStorageService.create(name, file); return new ResponseEntity<>(HttpStatus.CREATED); } @ApiOperation("上传图片") @PostMapping("/pictures") public ResponseEntity uploadPicture(@RequestParam MultipartFile file){ // 判断文件是否为图片 String suffix = FileUtil.getExtensionName(file.getOriginalFilename()); if(!FileUtil.IMAGE.equals(FileUtil.getFileType(suffix))){ throw new BadRequestException("只能上传图片"); } LocalStorage localStorage = localStorageService.create(null, file); return new ResponseEntity<>(localStorage, HttpStatus.OK); } @PutMapping @Log("修改文件") @ApiOperation("修改文件") @PreAuthorize("@el.check('storage:edit')") public ResponseEntity updateFile(@Validated @RequestBody LocalStorage resources){ localStorageService.update(resources); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Log("删除文件") @DeleteMapping @ApiOperation("多选删除") public ResponseEntity deleteFile(@RequestBody Long[] ids) { localStorageService.deleteAll(ids); return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/rest/S3StorageController.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.rest; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.zhengjie.annotation.Log; import me.zhengjie.config.AmzS3Config; import me.zhengjie.domain.S3Storage; import me.zhengjie.service.S3StorageService; import me.zhengjie.service.dto.S3StorageQueryCriteria; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; /** * amz S3 协议云存储管理 * @author 郑杰 * @date 2025-06-25 */ @Slf4j @RestController @RequiredArgsConstructor @RequestMapping("/api/s3Storage") @Api(tags = "工具:S3协议云存储管理") public class S3StorageController { private final AmzS3Config amzS3Config; private final S3StorageService s3StorageService; @ApiOperation("导出数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('storage:list')") public void exportS3Storage(HttpServletResponse response, S3StorageQueryCriteria criteria) throws IOException { s3StorageService.download(s3StorageService.queryAll(criteria), response); } @GetMapping @ApiOperation("查询文件") @PreAuthorize("@el.check('storage:list')") public ResponseEntity> queryS3Storage(S3StorageQueryCriteria criteria, Pageable pageable){ return new ResponseEntity<>(s3StorageService.queryAll(criteria, pageable),HttpStatus.OK); } @PostMapping @ApiOperation("上传文件") public ResponseEntity uploadS3Storage(@RequestParam MultipartFile file){ S3Storage storage = s3StorageService.upload(file); Map map = new HashMap<>(3); map.put("id",storage.getId()); map.put("errno",0); map.put("data",new String[]{amzS3Config.getDomain() + "/" + storage.getFilePath()}); return new ResponseEntity<>(map,HttpStatus.OK); } @Log("下载文件") @ApiOperation("下载文件") @GetMapping(value = "/download/{id}") public ResponseEntity downloadS3Storage(@PathVariable Long id){ Map map = new HashMap<>(1); S3Storage storage = s3StorageService.getById(id); if (storage == null) { map.put("message", "文件不存在或已被删除"); return new ResponseEntity<>(map, HttpStatus.NOT_FOUND); } // 仅适合公开文件访问,私有文件可以使用服务中的 privateDownload 方法 String url = amzS3Config.getDomain() + "/" + storage.getFilePath(); map.put("url", url); return new ResponseEntity<>(map,HttpStatus.OK); } @Log("删除多个文件") @DeleteMapping @ApiOperation("删除多个文件") @PreAuthorize("@el.check('storage:del')") public ResponseEntity deleteAllS3Storage(@RequestBody List ids) { s3StorageService.deleteAll(ids); return new ResponseEntity<>(HttpStatus.OK); } } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/service/AliPayService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service; import me.zhengjie.domain.vo.TradeVo; import me.zhengjie.domain.AlipayConfig; /** * @author Zheng Jie * @date 2018-12-31 */ public interface AliPayService { /** * 查询配置 * @return AlipayConfig */ AlipayConfig find(); /** * 更新配置 * @param alipayConfig 支付宝配置 * @return AlipayConfig */ AlipayConfig config(AlipayConfig alipayConfig); /** * 处理来自PC的交易请求 * @param alipay 支付宝配置 * @param trade 交易详情 * @return String * @throws Exception 异常 */ String toPayAsPc(AlipayConfig alipay, TradeVo trade) throws Exception; /** * 处理来自手机网页的交易请求 * @param alipay 支付宝配置 * @param trade 交易详情 * @return String * @throws Exception 异常 */ String toPayAsWeb(AlipayConfig alipay, TradeVo trade) throws Exception; } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/service/EmailService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service; import me.zhengjie.domain.vo.EmailVo; import me.zhengjie.domain.EmailConfig; /** * @author Zheng Jie * @date 2018-12-26 */ public interface EmailService { /** * 更新邮件配置 * @param emailConfig 邮箱配置 * @param old / * @return / * @throws Exception / */ EmailConfig config(EmailConfig emailConfig, EmailConfig old) throws Exception; /** * 查询配置 * @return EmailConfig 邮件配置 */ EmailConfig find(); /** * 发送邮件 * @param emailVo 邮件发送的内容 * @param emailConfig 邮件配置 */ void send(EmailVo emailVo, EmailConfig emailConfig); } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/service/LocalStorageService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service; import me.zhengjie.domain.LocalStorage; import me.zhengjie.service.dto.LocalStorageDto; import me.zhengjie.service.dto.LocalStorageQueryCriteria; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; /** * @author Zheng Jie * @date 2019-09-05 */ public interface LocalStorageService { /** * 分页查询 * @param criteria 条件 * @param pageable 分页参数 * @return / */ PageResult queryAll(LocalStorageQueryCriteria criteria, Pageable pageable); /** * 查询全部数据 * @param criteria 条件 * @return / */ List queryAll(LocalStorageQueryCriteria criteria); /** * 根据ID查询 * @param id / * @return / */ LocalStorageDto findById(Long id); /** * 上传 * @param name 文件名称 * @param file 文件 * @return / */ LocalStorage create(String name, MultipartFile file); /** * 编辑 * @param resources 文件信息 */ void update(LocalStorage resources); /** * 多选删除 * @param ids / */ void deleteAll(Long[] ids); /** * 导出数据 * @param localStorageDtos 待导出的数据 * @param response / * @throws IOException / */ void download(List localStorageDtos, HttpServletResponse response) throws IOException; } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/service/S3StorageService.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service; import me.zhengjie.domain.S3Storage; import me.zhengjie.service.dto.S3StorageQueryCriteria; import me.zhengjie.utils.PageResult; import org.springframework.data.domain.Pageable; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Map; /** * @description 服务接口 * @author Zheng Jie * @date 2025-06-25 **/ public interface S3StorageService { /** * 查询数据分页 * @param criteria 条件 * @param pageable 分页参数 * @return PageResult */ PageResult queryAll(S3StorageQueryCriteria criteria, Pageable pageable); /** * 查询所有数据不分页 * @param criteria 条件参数 * @return List */ List queryAll(S3StorageQueryCriteria criteria); /** * 多选删除 * @param ids / */ void deleteAll(List ids); /** * 导出数据 * @param all 待导出的数据 * @param response / * @throws IOException / */ void download(List all, HttpServletResponse response) throws IOException; /** * 私有化下载,仅供参考,还有许多方式 * @param id 文件ID */ Map privateDownload(Long id); /** * 上传文件 * @param file 上传的文件 * @return S3Storage 对象,包含文件存储信息 */ S3Storage upload(MultipartFile file); /** * 根据ID获取文件信息 * @param id 文件ID * @return S3Storage 对象,包含文件存储信息 */ S3Storage getById(Long id); } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/service/dto/LocalStorageDto.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import me.zhengjie.base.BaseDTO; import java.io.Serializable; /** * @author Zheng Jie * @date 2019-09-05 */ @Getter @Setter public class LocalStorageDto extends BaseDTO implements Serializable { @ApiModelProperty(value = "ID") private Long id; @ApiModelProperty(value = "真实文件名") private String realName; @ApiModelProperty(value = "文件名") private String name; @ApiModelProperty(value = "后缀") private String suffix; @ApiModelProperty(value = "文件类型") private String type; @ApiModelProperty(value = "文件大小") private String size; } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/service/dto/LocalStorageQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.sql.Timestamp; import java.util.List; import me.zhengjie.annotation.Query; /** * @author Zheng Jie * @date 2019-09-05 */ @Data public class LocalStorageQueryCriteria{ @ApiModelProperty(value = "模糊查询") @Query(blurry = "name,suffix,type,createBy,size") private String blurry; @ApiModelProperty(value = "创建时间") @Query(type = Query.Type.BETWEEN) private List createTime; } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/service/dto/S3StorageQueryCriteria.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import me.zhengjie.annotation.Query; import java.sql.Timestamp; import java.util.List; /** * @author Zheng Jie * @date 2025-06-25 **/ @Data public class S3StorageQueryCriteria { @Query(type = Query.Type.INNER_LIKE) @ApiModelProperty(value = "文件名称") private String fileName; @Query(type = Query.Type.BETWEEN) @ApiModelProperty(value = "创建时间") private List createTime; } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/service/impl/AliPayServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.impl; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.request.AlipayTradePagePayRequest; import com.alipay.api.request.AlipayTradeWapPayRequest; import lombok.RequiredArgsConstructor; import me.zhengjie.domain.vo.TradeVo; import me.zhengjie.domain.AlipayConfig; import me.zhengjie.exception.BadRequestException; import me.zhengjie.repository.AliPayRepository; import me.zhengjie.service.AliPayService; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Optional; /** * @author Zheng Jie * @date 2018-12-31 */ @Service @RequiredArgsConstructor @CacheConfig(cacheNames = "aliPay") public class AliPayServiceImpl implements AliPayService { private final AliPayRepository alipayRepository; @Override @Cacheable(key = "'config'") public AlipayConfig find() { Optional alipayConfig = alipayRepository.findById(1L); return alipayConfig.orElseGet(AlipayConfig::new); } @Override @CachePut(key = "'config'") @Transactional(rollbackFor = Exception.class) public AlipayConfig config(AlipayConfig alipayConfig) { alipayConfig.setId(1L); return alipayRepository.save(alipayConfig); } @Override public String toPayAsPc(AlipayConfig alipay, TradeVo trade) throws Exception { if(alipay.getId() == null){ throw new BadRequestException("请先添加相应配置,再操作"); } AlipayClient alipayClient = new DefaultAlipayClient(alipay.getGatewayUrl(), alipay.getAppId(), alipay.getPrivateKey(), alipay.getFormat(), alipay.getCharset(), alipay.getPublicKey(), alipay.getSignType()); // 创建API对应的request(电脑网页版) AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); // 订单完成后返回的页面和异步通知地址 request.setReturnUrl(alipay.getReturnUrl()); request.setNotifyUrl(alipay.getNotifyUrl()); // 填充订单参数 request.setBizContent("{" + " \"out_trade_no\":\""+trade.getOutTradeNo()+"\"," + " \"product_code\":\"FAST_INSTANT_TRADE_PAY\"," + " \"total_amount\":"+trade.getTotalAmount()+"," + " \"subject\":\""+trade.getSubject()+"\"," + " \"body\":\""+trade.getBody()+"\"," + " \"extend_params\":{" + " \"sys_service_provider_id\":\""+alipay.getSysServiceProviderId()+"\"" + " }"+ " }");//填充业务参数 // 调用SDK生成表单, 通过GET方式,口可以获取url return alipayClient.pageExecute(request, "GET").getBody(); } @Override public String toPayAsWeb(AlipayConfig alipay, TradeVo trade) throws Exception { if(alipay.getId() == null){ throw new BadRequestException("请先添加相应配置,再操作"); } AlipayClient alipayClient = new DefaultAlipayClient(alipay.getGatewayUrl(), alipay.getAppId(), alipay.getPrivateKey(), alipay.getFormat(), alipay.getCharset(), alipay.getPublicKey(), alipay.getSignType()); double money = Double.parseDouble(trade.getTotalAmount()); double maxMoney = 5000; if(money <= 0 || money >= maxMoney){ throw new BadRequestException("测试金额过大"); } // 创建API对应的request(手机网页版) AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest(); request.setReturnUrl(alipay.getReturnUrl()); request.setNotifyUrl(alipay.getNotifyUrl()); request.setBizContent("{" + " \"out_trade_no\":\""+trade.getOutTradeNo()+"\"," + " \"product_code\":\"FAST_INSTANT_TRADE_PAY\"," + " \"total_amount\":"+trade.getTotalAmount()+"," + " \"subject\":\""+trade.getSubject()+"\"," + " \"body\":\""+trade.getBody()+"\"," + " \"extend_params\":{" + " \"sys_service_provider_id\":\""+alipay.getSysServiceProviderId()+"\"" + " }"+ " }"); return alipayClient.pageExecute(request, "GET").getBody(); } } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/service/impl/EmailServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.impl; import cn.hutool.extra.mail.Mail; import cn.hutool.extra.mail.MailAccount; import lombok.RequiredArgsConstructor; import me.zhengjie.domain.EmailConfig; import me.zhengjie.domain.vo.EmailVo; import me.zhengjie.exception.BadRequestException; import me.zhengjie.repository.EmailRepository; import me.zhengjie.service.EmailService; import me.zhengjie.utils.EncryptUtils; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Optional; /** * @author Zheng Jie * @date 2018-12-26 */ @Service @RequiredArgsConstructor @CacheConfig(cacheNames = "email") public class EmailServiceImpl implements EmailService { private final EmailRepository emailRepository; @Override @CachePut(key = "'config'") @Transactional(rollbackFor = Exception.class) public EmailConfig config(EmailConfig emailConfig, EmailConfig old) throws Exception { emailConfig.setId(1L); if(!emailConfig.getPass().equals(old.getPass())){ // 对称加密 emailConfig.setPass(EncryptUtils.desEncrypt(emailConfig.getPass())); } return emailRepository.save(emailConfig); } @Override @Cacheable(key = "'config'") public EmailConfig find() { Optional emailConfig = emailRepository.findById(1L); return emailConfig.orElseGet(EmailConfig::new); } @Override @Transactional(rollbackFor = Exception.class) public void send(EmailVo emailVo, EmailConfig emailConfig){ if(emailConfig.getId() == null){ throw new BadRequestException("请先配置,再操作"); } // 封装 MailAccount account = new MailAccount(); // 设置用户 String user = emailConfig.getFromUser().split("@")[0]; account.setUser(user); account.setHost(emailConfig.getHost()); account.setPort(Integer.parseInt(emailConfig.getPort())); account.setAuth(true); try { // 对称解密 account.setPass(EncryptUtils.desDecrypt(emailConfig.getPass())); } catch (Exception e) { throw new BadRequestException(e.getMessage()); } account.setFrom(emailConfig.getUser()+"<"+emailConfig.getFromUser()+">"); // ssl方式发送 account.setSslEnable(true); // 使用STARTTLS安全连接 account.setStarttlsEnable(true); // 解决jdk8之后默认禁用部分tls协议,导致邮件发送失败的问题 account.setSslProtocols("TLSv1 TLSv1.1 TLSv1.2"); String content = emailVo.getContent(); // 发送 try { int size = emailVo.getTos().size(); Mail.create(account) .setTos(emailVo.getTos().toArray(new String[size])) .setTitle(emailVo.getSubject()) .setContent(content) .setHtml(true) //关闭session .setUseGlobalSession(false) .send(); }catch (Exception e){ throw new BadRequestException(e.getMessage()); } } } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/service/impl/LocalStorageServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.impl; import cn.hutool.core.util.ObjectUtil; import lombok.RequiredArgsConstructor; import me.zhengjie.config.properties.FileProperties; import me.zhengjie.domain.LocalStorage; import me.zhengjie.service.dto.LocalStorageDto; import me.zhengjie.service.dto.LocalStorageQueryCriteria; import me.zhengjie.service.mapstruct.LocalStorageMapper; import me.zhengjie.exception.BadRequestException; import me.zhengjie.utils.*; import me.zhengjie.repository.LocalStorageRepository; import me.zhengjie.service.LocalStorageService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; /** * @author Zheng Jie * @date 2019-09-05 */ @Service @RequiredArgsConstructor public class LocalStorageServiceImpl implements LocalStorageService { private final LocalStorageRepository localStorageRepository; private final LocalStorageMapper localStorageMapper; private final FileProperties properties; @Override public PageResult queryAll(LocalStorageQueryCriteria criteria, Pageable pageable){ Page page = localStorageRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); return PageUtil.toPage(page.map(localStorageMapper::toDto)); } @Override public List queryAll(LocalStorageQueryCriteria criteria){ return localStorageMapper.toDto(localStorageRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); } @Override public LocalStorageDto findById(Long id){ LocalStorage localStorage = localStorageRepository.findById(id).orElseGet(LocalStorage::new); ValidationUtil.isNull(localStorage.getId(),"LocalStorage","id",id); return localStorageMapper.toDto(localStorage); } @Override @Transactional(rollbackFor = Exception.class) public LocalStorage create(String name, MultipartFile multipartFile) { FileUtil.checkSize(properties.getMaxSize(), multipartFile.getSize()); String suffix = FileUtil.getExtensionName(multipartFile.getOriginalFilename()); String type = FileUtil.getFileType(suffix); File file = FileUtil.upload(multipartFile, properties.getPath().getPath() + type + File.separator); if(ObjectUtil.isNull(file)){ throw new BadRequestException("上传失败"); } try { name = StringUtils.isBlank(name) ? FileUtil.getFileNameNoEx(multipartFile.getOriginalFilename()) : name; LocalStorage localStorage = new LocalStorage( file.getName(), name, suffix, file.getPath(), type, FileUtil.getSize(multipartFile.getSize()) ); return localStorageRepository.save(localStorage); }catch (Exception e){ FileUtil.del(file); throw e; } } @Override @Transactional(rollbackFor = Exception.class) public void update(LocalStorage resources) { LocalStorage localStorage = localStorageRepository.findById(resources.getId()).orElseGet(LocalStorage::new); ValidationUtil.isNull( localStorage.getId(),"LocalStorage","id",resources.getId()); localStorage.copy(resources); localStorageRepository.save(localStorage); } @Override @Transactional(rollbackFor = Exception.class) public void deleteAll(Long[] ids) { for (Long id : ids) { LocalStorage storage = localStorageRepository.findById(id).orElseGet(LocalStorage::new); FileUtil.del(storage.getPath()); localStorageRepository.delete(storage); } } @Override public void download(List queryAll, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (LocalStorageDto localStorageDTO : queryAll) { Map map = new LinkedHashMap<>(); map.put("文件名", localStorageDTO.getRealName()); map.put("备注名", localStorageDTO.getName()); map.put("文件类型", localStorageDTO.getType()); map.put("文件大小", localStorageDTO.getSize()); map.put("创建者", localStorageDTO.getCreateBy()); map.put("创建日期", localStorageDTO.getCreateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/service/impl/S3StorageServiceImpl.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.impl; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.IdUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.zhengjie.config.AmzS3Config; import me.zhengjie.domain.S3Storage; import me.zhengjie.exception.BadRequestException; import me.zhengjie.repository.S3StorageRepository; import me.zhengjie.service.S3StorageService; import me.zhengjie.service.dto.S3StorageQueryCriteria; import me.zhengjie.utils.*; import org.apache.commons.io.IOUtils; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import software.amazon.awssdk.core.ResponseInputStream; import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.core.waiters.WaiterResponse; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.*; import software.amazon.awssdk.services.s3.waiters.S3Waiter; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; /** * @description 服务实现 * @author Zheng Jie * @date 2025-06-25 **/ @Slf4j @Service @RequiredArgsConstructor public class S3StorageServiceImpl implements S3StorageService { private final S3Client s3Client; private final AmzS3Config amzS3Config; private final S3StorageRepository s3StorageRepository; @Override public S3Storage getById(Long id) { return s3StorageRepository.findById(id).orElse(null); } @Override public PageResult queryAll(S3StorageQueryCriteria criteria, Pageable pageable){ Page page = s3StorageRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); return PageUtil.toPage(page); } @Override public List queryAll(S3StorageQueryCriteria criteria){ return s3StorageRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)); } @Override @Transactional(rollbackFor = Exception.class) public void deleteAll(List ids) { // 检查桶是否存在 String bucketName = amzS3Config.getDefaultBucket(); if (!bucketExists(bucketName)) { throw new BadRequestException("存储桶不存在,请检查配置或权限。"); } // 遍历 ID 列表,删除对应的文件和数据库记录 for (Long id : ids) { String filePath = s3StorageRepository.selectFilePathById(id); if (filePath == null) { System.err.println("未找到 ID 为 " + id + " 的文件记录,无法删除。"); continue; } try { // 创建 DeleteObjectRequest,指定存储桶和文件键 DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder() .bucket(bucketName) .key(filePath) .build(); // 调用 deleteObject 方法 s3Client.deleteObject(deleteObjectRequest); // 删除数据库数据 s3StorageRepository.deleteById(id); } catch (S3Exception e) { // 处理 AWS 特定的异常 log.error("从 S3 删除文件时出错: {}", e.awsErrorDetails().errorMessage(), e); } } } @Override public S3Storage upload(MultipartFile file) { String bucketName = amzS3Config.getDefaultBucket(); // 检查存储桶是否存在 if (!bucketExists(bucketName)) { log.warn("存储桶 {} 不存在,尝试创建...", bucketName); if (createBucket(bucketName)){ log.info("存储桶 {} 创建成功。", bucketName); } else { throw new BadRequestException("存储桶创建失败,请检查配置或权限。"); } } // 获取文件名 String originalName = file.getOriginalFilename(); if (StringUtils.isBlank(originalName)) { throw new IllegalArgumentException("文件名不能为空"); } // 生成存储路径和文件名 String folder = DateUtil.format(new Date(), amzS3Config.getTimeformat()); String fileName = IdUtil.simpleUUID() + "." + FileUtil.getExtensionName(originalName); String filePath = folder + "/" + fileName; // 构建上传请求 PutObjectRequest putObjectRequest = PutObjectRequest.builder() .bucket(amzS3Config.getDefaultBucket()) .key(filePath) .build(); // 创建 S3Storage 实例 S3Storage s3Storage = new S3Storage(); try { // 上传文件到 S3 s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(file.getInputStream(), file.getSize())); // 设置 S3Storage 属性 s3Storage.setFileMimeType(FileUtil.getMimeType(originalName)); s3Storage.setFileName(originalName); s3Storage.setFileRealName(fileName); s3Storage.setFileSize(FileUtil.getSize(file.getSize())); s3Storage.setFileType(FileUtil.getExtensionName(originalName)); s3Storage.setFilePath(filePath); // 保存入库 s3StorageRepository.save(s3Storage); } catch (IOException e) { throw new RuntimeException(e); } // 设置地址 return s3Storage; } @Override public void download(List all, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (S3Storage s3Storage : all) { Map map = new LinkedHashMap<>(); map.put("文件名称", s3Storage.getFileName()); map.put("真实存储的名称", s3Storage.getFileRealName()); map.put("文件大小", s3Storage.getFileSize()); map.put("文件MIME 类型", s3Storage.getFileMimeType()); map.put("文件类型", s3Storage.getFileType()); map.put("文件路径", s3Storage.getFilePath()); map.put("创建者", s3Storage.getCreateBy()); map.put("更新者", s3Storage.getUpdateBy()); map.put("创建日期", s3Storage.getCreateTime()); map.put("更新时间", s3Storage.getUpdateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } public Map privateDownload(Long id) { S3Storage storage = s3StorageRepository.findById(id).orElse(null); if (storage == null) { throw new BadRequestException("文件不存在或已被删除"); } // 创建 GetObjectRequest,指定存储桶和文件键 GetObjectRequest getObjectRequest = GetObjectRequest.builder() .bucket(amzS3Config.getDefaultBucket()) .key(storage.getFilePath()) .build(); String base64Data; // 使用 try-with-resources 确保流能被自动关闭 // s3Client.getObject() 返回一个 ResponseInputStream,它是一个包含S3对象数据的输入流 try (ResponseInputStream s3InputStream = s3Client.getObject(getObjectRequest)) { // 使用 IOUtils.toByteArray 将输入流直接转换为字节数组 byte[] fileBytes = IOUtils.toByteArray(s3InputStream); // 使用 Java 内置的 Base64 编码器将字节数组转换为 Base64 字符串 base64Data = Base64.getEncoder().encodeToString(fileBytes); } catch (S3Exception e) { // 处理 AWS 特定的异常 throw new BadRequestException("从 S3 下载文件时出错: " + e.awsErrorDetails().errorMessage()); } catch (IOException e) { // 处理通用的 IO 异常 (IOUtils.toByteArray 可能会抛出) throw new BadRequestException("读取 S3 输入流时出错: " + e.getMessage()); } // 构造返回数据 Map responseData = new HashMap<>(); // 文件名 responseData.put("fileName", storage.getFileName()); // 文件类型 responseData.put("fileMimeType", storage.getFileMimeType()); // 文件内容 responseData.put("base64Data", base64Data); return responseData; } /** * 检查云存储桶是否存在 * @param bucketName 存储桶名称 */ @SuppressWarnings({"all"}) private boolean bucketExists(String bucketName) { try { HeadBucketRequest headBucketRequest = HeadBucketRequest.builder() .bucket(bucketName) .build(); s3Client.headBucket(headBucketRequest); return true; } catch (S3Exception e) { // 如果状态码是 404 (Not Found), 说明存储桶不存在 if (e.statusCode() == 404) { log.error("存储桶 '{}' 不存在。", bucketName); return false; } // 其他异常 (如 403 Forbidden) 说明存在问题,但不能断定它不存在 throw new BadRequestException("检查存储桶时出错: " + e.awsErrorDetails().errorMessage()); } } /** * 创建云存储桶 * @param bucketName 存储桶名称 */ private boolean createBucket(String bucketName) { try { // 使用 S3Waiter 等待存储桶创建完成 S3Waiter s3Waiter = s3Client.waiter(); CreateBucketRequest bucketRequest = CreateBucketRequest.builder() .bucket(bucketName) .acl(BucketCannedACL.PRIVATE) .build(); s3Client.createBucket(bucketRequest); // 等待直到存储桶创建完成 HeadBucketRequest bucketRequestWait = HeadBucketRequest.builder() .bucket(bucketName) .build(); // 使用 WaiterResponse 等待存储桶存在 WaiterResponse waiterResponse = s3Waiter.waitUntilBucketExists(bucketRequestWait); waiterResponse.matched().response().ifPresent(response -> log.info("存储桶 '{}' 创建成功,状态: {}", bucketName, response.sdkHttpResponse().statusCode()) ); } catch (BucketAlreadyOwnedByYouException e) { log.warn("存储桶 '{}' 已经被您拥有,无需重复创建。", bucketName); } catch (S3Exception e) { throw new BadRequestException("创建存储桶时出错: " + e.awsErrorDetails().errorMessage()); } return true; } } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/service/mapstruct/LocalStorageMapper.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.service.mapstruct; import me.zhengjie.base.BaseMapper; import me.zhengjie.service.dto.LocalStorageDto; import me.zhengjie.domain.LocalStorage; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; /** * @author Zheng Jie * @date 2019-09-05 */ @Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface LocalStorageMapper extends BaseMapper { } ================================================ FILE: eladmin-tools/src/main/java/me/zhengjie/utils/AlipayUtils.java ================================================ /* * Copyright 2019-2025 Zheng Jie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.zhengjie.utils; import com.alipay.api.AlipayApiException; import com.alipay.api.internal.util.AlipaySignature; import me.zhengjie.domain.AlipayConfig; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * 支付宝工具类 * @author zhengjie * @date 2018/09/30 14:04:35 */ @Component public class AlipayUtils { /** * 生成订单号 * @return String */ public String getOrderCode() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); int a = (int)(Math.random() * 9000.0D) + 1000; System.out.println(a); Date date = new Date(); String str = sdf.format(date); String[] split = str.split("-"); String s = split[0] + split[1] + split[2]; String[] split1 = s.split(" "); String s1 = split1[0] + split1[1]; String[] split2 = s1.split(":"); return split2[0] + split2[1] + split2[2] + a; } /** * 校验签名 * @param request HttpServletRequest * @param alipay 阿里云配置 * @return boolean */ public boolean rsaCheck(HttpServletRequest request, AlipayConfig alipay){ // 获取支付宝POST过来反馈信息 Map params = new HashMap<>(1); Map requestParams = request.getParameterMap(); for (Object o : requestParams.keySet()) { String name = (String) o; String[] values = requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; } params.put(name, valueStr); } try { return AlipaySignature.rsaCheckV1(params, alipay.getPublicKey(), alipay.getCharset(), alipay.getSignType()); } catch (AlipayApiException e) { return false; } } } ================================================ FILE: pom.xml ================================================ 4.0.0 me.zhengjie eladmin pom 2.7 eladmin-common eladmin-logging eladmin-system eladmin-tools eladmin-generator ELADMIN 后台管理 https://eladmin.vip org.springframework.boot spring-boot-starter-parent 2.7.18 1.2.9 UTF-8 UTF-8 1.8 2.0.54 1.2.19 2.11.1 1.4.2.Final org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-websocket org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-json org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-cache org.springframework.boot spring-boot-starter-data-redis org.redisson redisson-spring-boot-starter 3.17.1 org.apache.commons commons-pool2 ${commons-pool2.version} org.apache.commons commons-lang3 p6spy p6spy 3.9.1 com.github.xiaoymin knife4j-spring-boot-starter 3.0.3 io.swagger swagger-annotations io.swagger swagger-annotations 1.5.22 com.mysql mysql-connector-j 9.2.0 runtime com.alibaba druid-spring-boot-starter ${druid.version} net.dreamlu mica-ip2region 2.7.18.9 org.projectlombok lombok true org.apache.poi poi 5.4.0 org.apache.poi poi-ooxml 5.4.0 xerces xercesImpl 2.12.2 com.alibaba.fastjson2 fastjson2 ${fastjson2.version} com.alibaba.fastjson2 fastjson2-extension-spring5 ${fastjson2.version} com.github.whvcse easy-captcha 1.6.2 org.apache.commons commons-text 1.13.0 org.mapstruct mapstruct ${mapstruct.version} org.mapstruct mapstruct-processor ${mapstruct.version} provided javax.inject javax.inject 1 org.apache.maven.plugins maven-surefire-plugin true public aliyun nexus http://maven.aliyun.com/nexus/content/groups/public/ true public aliyun nexus http://maven.aliyun.com/nexus/content/groups/public/ true false ================================================ FILE: sql/eladmin.sql ================================================ /* Navicat Premium Dump SQL Source Server : localhost Source Server Type : MariaDB Source Server Version : 110206 (11.2.6-MariaDB) Source Host : localhost:3306 Source Schema : eladmin Target Server Type : MariaDB Target Server Version : 110206 (11.2.6-MariaDB) File Encoding : 65001 Date: 25/06/2025 15:56:51 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for code_column -- ---------------------------- DROP TABLE IF EXISTS `code_column`; CREATE TABLE `code_column` ( `column_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `table_name` varchar(180) DEFAULT NULL COMMENT '表名', `column_name` varchar(255) DEFAULT NULL COMMENT '数据库字段名称', `column_type` varchar(255) DEFAULT NULL COMMENT '数据库字段类型', `dict_name` varchar(255) DEFAULT NULL COMMENT '字典名称', `extra` varchar(255) DEFAULT NULL COMMENT '字段额外的参数', `form_show` bit(1) DEFAULT NULL COMMENT '是否表单显示', `form_type` varchar(255) DEFAULT NULL COMMENT '表单类型', `key_type` varchar(255) DEFAULT NULL COMMENT '数据库字段键类型', `list_show` bit(1) DEFAULT NULL COMMENT '是否在列表显示', `not_null` bit(1) DEFAULT NULL COMMENT '是否为空', `query_type` varchar(255) DEFAULT NULL COMMENT '查询类型', `remark` varchar(255) DEFAULT NULL COMMENT '描述', `date_annotation` varchar(255) DEFAULT NULL COMMENT '日期注解', PRIMARY KEY (`column_id`) USING BTREE, KEY `idx_table_name` (`table_name`) ) ENGINE=InnoDB AUTO_INCREMENT=266 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='代码生成字段信息存储'; -- ---------------------------- -- Records of code_column -- ---------------------------- BEGIN; COMMIT; -- ---------------------------- -- Table structure for code_config -- ---------------------------- DROP TABLE IF EXISTS `code_config`; CREATE TABLE `code_config` ( `config_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `table_name` varchar(255) DEFAULT NULL COMMENT '表名', `author` varchar(255) DEFAULT NULL COMMENT '作者', `cover` bit(1) DEFAULT NULL COMMENT '是否覆盖', `module_name` varchar(255) DEFAULT NULL COMMENT '模块名称', `pack` varchar(255) DEFAULT NULL COMMENT '至于哪个包下', `path` varchar(255) DEFAULT NULL COMMENT '前端代码生成的路径', `api_path` varchar(255) DEFAULT NULL COMMENT '前端Api文件路径', `prefix` varchar(255) DEFAULT NULL COMMENT '表前缀', `api_alias` varchar(255) DEFAULT NULL COMMENT '接口名称', PRIMARY KEY (`config_id`) USING BTREE, KEY `idx_table_name` (`table_name`(100)) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='代码生成器配置'; -- ---------------------------- -- Records of code_config -- ---------------------------- BEGIN; COMMIT; -- ---------------------------- -- Table structure for mnt_app -- ---------------------------- DROP TABLE IF EXISTS `mnt_app`; CREATE TABLE `mnt_app` ( `app_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `name` varchar(255) DEFAULT NULL COMMENT '应用名称', `upload_path` varchar(255) DEFAULT NULL COMMENT '上传目录', `deploy_path` varchar(255) DEFAULT NULL COMMENT '部署路径', `backup_path` varchar(255) DEFAULT NULL COMMENT '备份路径', `port` int(255) DEFAULT NULL COMMENT '应用端口', `start_script` varchar(4000) DEFAULT NULL COMMENT '启动脚本', `deploy_script` varchar(4000) DEFAULT NULL COMMENT '部署脚本', `create_by` varchar(255) DEFAULT NULL COMMENT '创建者', `update_by` varchar(255) DEFAULT NULL COMMENT '更新者', `create_time` datetime DEFAULT NULL COMMENT '创建日期', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`app_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='应用管理'; -- ---------------------------- -- Records of mnt_app -- ---------------------------- BEGIN; COMMIT; -- ---------------------------- -- Table structure for mnt_database -- ---------------------------- DROP TABLE IF EXISTS `mnt_database`; CREATE TABLE `mnt_database` ( `db_id` varchar(50) NOT NULL COMMENT 'ID', `name` varchar(255) NOT NULL COMMENT '名称', `jdbc_url` varchar(255) NOT NULL COMMENT 'jdbc连接', `user_name` varchar(255) NOT NULL COMMENT '账号', `pwd` varchar(255) NOT NULL COMMENT '密码', `create_by` varchar(255) DEFAULT NULL COMMENT '创建者', `update_by` varchar(255) DEFAULT NULL COMMENT '更新者', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`db_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='数据库管理'; -- ---------------------------- -- Records of mnt_database -- ---------------------------- BEGIN; COMMIT; -- ---------------------------- -- Table structure for mnt_deploy -- ---------------------------- DROP TABLE IF EXISTS `mnt_deploy`; CREATE TABLE `mnt_deploy` ( `deploy_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `app_id` bigint(20) DEFAULT NULL COMMENT '应用编号', `create_by` varchar(255) DEFAULT NULL COMMENT '创建者', `update_by` varchar(255) DEFAULT NULL COMMENT '更新者', `create_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`deploy_id`) USING BTREE, KEY `idx_app_id` (`app_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='部署管理'; -- ---------------------------- -- Records of mnt_deploy -- ---------------------------- BEGIN; COMMIT; -- ---------------------------- -- Table structure for mnt_deploy_history -- ---------------------------- DROP TABLE IF EXISTS `mnt_deploy_history`; CREATE TABLE `mnt_deploy_history` ( `history_id` varchar(50) NOT NULL COMMENT 'ID', `app_name` varchar(255) NOT NULL COMMENT '应用名称', `deploy_date` datetime NOT NULL COMMENT '部署日期', `deploy_user` varchar(50) NOT NULL COMMENT '部署用户', `ip` varchar(20) NOT NULL COMMENT '服务器IP', `deploy_id` bigint(20) DEFAULT NULL COMMENT '部署编号', PRIMARY KEY (`history_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='部署历史管理'; -- ---------------------------- -- Records of mnt_deploy_history -- ---------------------------- BEGIN; COMMIT; -- ---------------------------- -- Table structure for mnt_deploy_server -- ---------------------------- DROP TABLE IF EXISTS `mnt_deploy_server`; CREATE TABLE `mnt_deploy_server` ( `deploy_id` bigint(20) NOT NULL COMMENT '部署ID', `server_id` bigint(20) NOT NULL COMMENT '服务ID', PRIMARY KEY (`deploy_id`,`server_id`) USING BTREE, KEY `idx_deploy_id` (`deploy_id`), KEY `idx_server_id` (`server_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='应用与服务器关联'; -- ---------------------------- -- Records of mnt_deploy_server -- ---------------------------- BEGIN; COMMIT; -- ---------------------------- -- Table structure for mnt_server -- ---------------------------- DROP TABLE IF EXISTS `mnt_server`; CREATE TABLE `mnt_server` ( `server_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `account` varchar(50) DEFAULT NULL COMMENT '账号', `ip` varchar(20) DEFAULT NULL COMMENT 'IP地址', `name` varchar(100) DEFAULT NULL COMMENT '名称', `password` varchar(100) DEFAULT NULL COMMENT '密码', `port` int(11) DEFAULT NULL COMMENT '端口', `create_by` varchar(255) DEFAULT NULL COMMENT '创建者', `update_by` varchar(255) DEFAULT NULL COMMENT '更新者', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`server_id`) USING BTREE, KEY `idx_ip` (`ip`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='服务器管理'; -- ---------------------------- -- Records of mnt_server -- ---------------------------- BEGIN; COMMIT; -- ---------------------------- -- Table structure for sys_dept -- ---------------------------- DROP TABLE IF EXISTS `sys_dept`; CREATE TABLE `sys_dept` ( `dept_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `pid` bigint(20) DEFAULT NULL COMMENT '上级部门', `sub_count` int(5) DEFAULT 0 COMMENT '子部门数目', `name` varchar(255) NOT NULL COMMENT '名称', `dept_sort` int(5) DEFAULT 999 COMMENT '排序', `enabled` bit(1) NOT NULL COMMENT '状态', `create_by` varchar(255) DEFAULT NULL COMMENT '创建者', `update_by` varchar(255) DEFAULT NULL COMMENT '更新者', `create_time` datetime DEFAULT NULL COMMENT '创建日期', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`dept_id`) USING BTREE, KEY `idx_pid` (`pid`), KEY `idx_enabled` (`enabled`) ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='部门'; -- ---------------------------- -- Records of sys_dept -- ---------------------------- BEGIN; INSERT INTO `sys_dept` (`dept_id`, `pid`, `sub_count`, `name`, `dept_sort`, `enabled`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (2, 7, 1, '研发部', 3, b'1', 'admin', 'admin', '2019-03-25 09:15:32', '2020-08-02 14:48:47'); INSERT INTO `sys_dept` (`dept_id`, `pid`, `sub_count`, `name`, `dept_sort`, `enabled`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (5, 7, 0, '运维部', 4, b'1', 'admin', 'admin', '2019-03-25 09:20:44', '2020-05-17 14:27:27'); INSERT INTO `sys_dept` (`dept_id`, `pid`, `sub_count`, `name`, `dept_sort`, `enabled`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (6, 8, 0, '测试部', 6, b'1', 'admin', 'admin', '2019-03-25 09:52:18', '2020-06-08 11:59:21'); INSERT INTO `sys_dept` (`dept_id`, `pid`, `sub_count`, `name`, `dept_sort`, `enabled`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (7, NULL, 2, '华南分部', 0, b'1', 'admin', 'admin', '2019-03-25 11:04:50', '2020-06-08 12:08:56'); INSERT INTO `sys_dept` (`dept_id`, `pid`, `sub_count`, `name`, `dept_sort`, `enabled`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (8, NULL, 2, '华北分部', 1, b'1', 'admin', 'admin', '2019-03-25 11:04:53', '2020-05-14 12:54:00'); INSERT INTO `sys_dept` (`dept_id`, `pid`, `sub_count`, `name`, `dept_sort`, `enabled`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (15, 8, 0, 'UI部门', 7, b'1', 'admin', 'admin', '2020-05-13 22:56:53', '2020-05-14 12:54:13'); INSERT INTO `sys_dept` (`dept_id`, `pid`, `sub_count`, `name`, `dept_sort`, `enabled`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (17, 2, 0, '研发一组', 999, b'1', 'admin', 'admin', '2020-08-02 14:49:07', '2020-08-02 14:49:07'); COMMIT; -- ---------------------------- -- Table structure for sys_dict -- ---------------------------- DROP TABLE IF EXISTS `sys_dict`; CREATE TABLE `sys_dict` ( `dict_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `name` varchar(255) NOT NULL COMMENT '字典名称', `description` varchar(255) DEFAULT NULL COMMENT '描述', `create_by` varchar(255) DEFAULT NULL COMMENT '创建者', `update_by` varchar(255) DEFAULT NULL COMMENT '更新者', `create_time` datetime DEFAULT NULL COMMENT '创建日期', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`dict_id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='数据字典'; -- ---------------------------- -- Records of sys_dict -- ---------------------------- BEGIN; INSERT INTO `sys_dict` (`dict_id`, `name`, `description`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (1, 'user_status', '用户状态', NULL, NULL, '2019-10-27 20:31:36', NULL); INSERT INTO `sys_dict` (`dict_id`, `name`, `description`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (4, 'dept_status', '部门状态', NULL, NULL, '2019-10-27 20:31:36', NULL); INSERT INTO `sys_dict` (`dict_id`, `name`, `description`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (5, 'job_status', '岗位状态', NULL, 'admin', '2019-10-27 20:31:36', '2025-01-14 15:48:29'); COMMIT; -- ---------------------------- -- Table structure for sys_dict_detail -- ---------------------------- DROP TABLE IF EXISTS `sys_dict_detail`; CREATE TABLE `sys_dict_detail` ( `detail_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `dict_id` bigint(11) DEFAULT NULL COMMENT '字典id', `label` varchar(255) NOT NULL COMMENT '字典标签', `value` varchar(255) NOT NULL COMMENT '字典值', `dict_sort` int(5) DEFAULT NULL COMMENT '排序', `create_by` varchar(255) DEFAULT NULL COMMENT '创建者', `update_by` varchar(255) DEFAULT NULL COMMENT '更新者', `create_time` datetime DEFAULT NULL COMMENT '创建日期', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`detail_id`) USING BTREE, KEY `idx_dict_id` (`dict_id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='数据字典详情'; -- ---------------------------- -- Records of sys_dict_detail -- ---------------------------- BEGIN; INSERT INTO `sys_dict_detail` (`detail_id`, `dict_id`, `label`, `value`, `dict_sort`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (1, 1, '激活', 'true', 1, NULL, NULL, '2019-10-27 20:31:36', NULL); INSERT INTO `sys_dict_detail` (`detail_id`, `dict_id`, `label`, `value`, `dict_sort`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (2, 1, '禁用', 'false', 2, NULL, NULL, NULL, NULL); INSERT INTO `sys_dict_detail` (`detail_id`, `dict_id`, `label`, `value`, `dict_sort`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (3, 4, '启用', 'true', 1, NULL, NULL, NULL, NULL); INSERT INTO `sys_dict_detail` (`detail_id`, `dict_id`, `label`, `value`, `dict_sort`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (4, 4, '停用', 'false', 2, NULL, NULL, '2019-10-27 20:31:36', NULL); INSERT INTO `sys_dict_detail` (`detail_id`, `dict_id`, `label`, `value`, `dict_sort`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (5, 5, '启用', 'true', 1, NULL, NULL, NULL, NULL); INSERT INTO `sys_dict_detail` (`detail_id`, `dict_id`, `label`, `value`, `dict_sort`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (6, 5, '停用', 'false', 2, NULL, NULL, '2019-10-27 20:31:36', NULL); COMMIT; -- ---------------------------- -- Table structure for sys_job -- ---------------------------- DROP TABLE IF EXISTS `sys_job`; CREATE TABLE `sys_job` ( `job_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `name` varchar(180) NOT NULL COMMENT '岗位名称', `enabled` bit(1) NOT NULL COMMENT '岗位状态', `job_sort` int(5) DEFAULT NULL COMMENT '排序', `create_by` varchar(255) DEFAULT NULL COMMENT '创建者', `update_by` varchar(255) DEFAULT NULL COMMENT '更新者', `create_time` datetime DEFAULT NULL COMMENT '创建日期', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`job_id`) USING BTREE, UNIQUE KEY `uniq_name` (`name`), KEY `idx_enabled` (`enabled`) ) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='岗位'; -- ---------------------------- -- Records of sys_job -- ---------------------------- BEGIN; INSERT INTO `sys_job` (`job_id`, `name`, `enabled`, `job_sort`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (8, '人事专员', b'1', 3, NULL, NULL, '2019-03-29 14:52:28', NULL); INSERT INTO `sys_job` (`job_id`, `name`, `enabled`, `job_sort`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (10, '产品经理', b'1', 4, NULL, NULL, '2019-03-29 14:55:51', NULL); INSERT INTO `sys_job` (`job_id`, `name`, `enabled`, `job_sort`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (11, '全栈开发', b'1', 2, NULL, 'admin', '2019-03-31 13:39:30', '2020-05-05 11:33:43'); INSERT INTO `sys_job` (`job_id`, `name`, `enabled`, `job_sort`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (12, '软件测试', b'1', 5, NULL, 'admin', '2019-03-31 13:39:43', '2020-05-10 19:56:26'); COMMIT; -- ---------------------------- -- Table structure for sys_log -- ---------------------------- DROP TABLE IF EXISTS `sys_log`; CREATE TABLE `sys_log` ( `log_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `description` varchar(255) DEFAULT NULL COMMENT '描述', `log_type` varchar(10) NOT NULL COMMENT '日志类型:INFI/ERROR', `method` varchar(255) DEFAULT NULL COMMENT '方法名', `params` text DEFAULT NULL COMMENT '参数', `request_ip` varchar(255) DEFAULT NULL COMMENT '请求IP', `time` bigint(20) DEFAULT NULL COMMENT '执行时间', `username` varchar(255) DEFAULT NULL COMMENT '用户名', `address` varchar(255) DEFAULT NULL COMMENT '地址', `browser` varchar(255) DEFAULT NULL COMMENT '浏览器', `exception_detail` text DEFAULT NULL COMMENT '异常', `create_time` datetime NOT NULL COMMENT '创建时间', PRIMARY KEY (`log_id`) USING BTREE, KEY `idx_create_time_index` (`create_time`), KEY `idx_log_type` (`log_type`) ) ENGINE=InnoDB AUTO_INCREMENT=3636 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='系统日志'; -- ---------------------------- -- Records of sys_log -- ---------------------------- BEGIN; COMMIT; -- ---------------------------- -- Table structure for sys_menu -- ---------------------------- DROP TABLE IF EXISTS `sys_menu`; CREATE TABLE `sys_menu` ( `menu_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `pid` bigint(20) DEFAULT NULL COMMENT '上级菜单ID', `sub_count` int(5) DEFAULT 0 COMMENT '子菜单数目', `type` int(11) DEFAULT NULL COMMENT '菜单类型', `title` varchar(100) DEFAULT NULL COMMENT '菜单标题', `name` varchar(100) DEFAULT NULL COMMENT '组件名称', `component` varchar(255) DEFAULT NULL COMMENT '组件', `menu_sort` int(5) DEFAULT NULL COMMENT '排序', `icon` varchar(255) DEFAULT NULL COMMENT '图标', `path` varchar(255) DEFAULT NULL COMMENT '链接地址', `i_frame` bit(1) DEFAULT NULL COMMENT '是否外链', `cache` bit(1) DEFAULT b'0' COMMENT '缓存', `hidden` bit(1) DEFAULT b'0' COMMENT '隐藏', `permission` varchar(255) DEFAULT NULL COMMENT '权限', `create_by` varchar(255) DEFAULT NULL COMMENT '创建者', `update_by` varchar(255) DEFAULT NULL COMMENT '更新者', `create_time` datetime DEFAULT NULL COMMENT '创建日期', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`menu_id`) USING BTREE, UNIQUE KEY `uniq_name` (`name`), UNIQUE KEY `uniq_title` (`title`), KEY `idx_pid` (`pid`) ) ENGINE=InnoDB AUTO_INCREMENT=117 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='系统菜单'; -- ---------------------------- -- Records of sys_menu -- ---------------------------- BEGIN; INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (1, NULL, 7, 0, '系统管理', NULL, NULL, 1, 'system', 'system', b'0', b'0', b'0', NULL, NULL, 'admin', '2018-12-18 15:11:29', '2025-01-14 15:48:18'); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (2, 1, 3, 1, '用户管理', 'User', 'system/user/index', 2, 'peoples', 'user', b'0', b'0', b'0', 'user:list', NULL, NULL, '2018-12-18 15:14:44', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (3, 1, 3, 1, '角色管理', 'Role', 'system/role/index', 3, 'role', 'role', b'0', b'0', b'0', 'roles:list', NULL, NULL, '2018-12-18 15:16:07', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (5, 1, 3, 1, '菜单管理', 'Menu', 'system/menu/index', 5, 'menu', 'menu', b'0', b'0', b'0', 'menu:list', NULL, NULL, '2018-12-18 15:17:28', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (6, NULL, 5, 0, '系统监控', NULL, NULL, 10, 'monitor', 'monitor', b'0', b'0', b'0', NULL, NULL, NULL, '2018-12-18 15:17:48', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (7, 6, 0, 1, '操作日志', 'Log', 'monitor/log/index', 11, 'log', 'logs', b'0', b'1', b'0', NULL, NULL, 'admin', '2018-12-18 15:18:26', '2020-06-06 13:11:57'); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (9, 6, 0, 1, 'SQL监控', 'Sql', 'monitor/sql/index', 18, 'sqlMonitor', 'druid', b'0', b'0', b'0', NULL, NULL, NULL, '2018-12-18 15:19:34', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (10, NULL, 5, 0, '组件管理', NULL, NULL, 50, 'zujian', 'components', b'0', b'0', b'0', NULL, NULL, NULL, '2018-12-19 13:38:16', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (11, 10, 0, 1, '图标库', 'Icons', 'components/icons/index', 51, 'icon', 'icon', b'0', b'0', b'0', NULL, NULL, NULL, '2018-12-19 13:38:49', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (14, 36, 0, 1, '邮件工具', 'Email', 'tools/email/index', 35, 'email', 'email', b'0', b'0', b'0', NULL, NULL, NULL, '2018-12-27 10:13:09', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (15, 10, 0, 1, '富文本', 'Editor', 'components/Editor', 52, 'fwb', 'tinymce', b'0', b'0', b'0', NULL, NULL, NULL, '2018-12-27 11:58:25', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (18, 36, 3, 1, '存储管理', 'Storage', 'tools/storage/index', 34, 'qiniu', 'storage', b'0', b'0', b'0', 'storage:list', NULL, NULL, '2018-12-31 11:12:15', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (19, 36, 0, 1, '支付宝工具', 'AliPay', 'tools/aliPay/index', 37, 'alipay', 'aliPay', b'0', b'0', b'0', NULL, NULL, NULL, '2018-12-31 14:52:38', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (21, NULL, 2, 0, '多级菜单', NULL, '', 900, 'menu', 'nested', b'0', b'0', b'0', NULL, NULL, 'admin', '2019-01-04 16:22:03', '2020-06-21 17:27:35'); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (22, 21, 2, 0, '二级菜单1', NULL, '', 999, 'menu', 'menu1', b'0', b'0', b'0', NULL, NULL, 'admin', '2019-01-04 16:23:29', '2020-06-21 17:27:20'); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (23, 21, 0, 1, '二级菜单2', NULL, 'nested/menu2/index', 999, 'menu', 'menu2', b'0', b'0', b'0', NULL, NULL, NULL, '2019-01-04 16:23:57', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (24, 22, 0, 1, '三级菜单1', 'Test', 'nested/menu1/menu1-1', 999, 'menu', 'menu1-1', b'0', b'0', b'0', NULL, NULL, NULL, '2019-01-04 16:24:48', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (27, 22, 0, 1, '三级菜单2', NULL, 'nested/menu1/menu1-2', 999, 'menu', 'menu1-2', b'0', b'0', b'0', NULL, NULL, NULL, '2019-01-07 17:27:32', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (28, 1, 3, 1, '任务调度', 'Timing', 'system/timing/index', 999, 'timing', 'timing', b'0', b'0', b'0', 'timing:list', NULL, NULL, '2019-01-07 20:34:40', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (30, 36, 0, 1, '代码生成', 'GeneratorIndex', 'generator/index', 32, 'dev', 'generator', b'0', b'1', b'0', NULL, NULL, NULL, '2019-01-11 15:45:55', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (32, 6, 0, 1, '异常日志', 'ErrorLog', 'monitor/log/errorLog', 12, 'error', 'errorLog', b'0', b'0', b'0', NULL, NULL, NULL, '2019-01-13 13:49:03', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (33, 10, 0, 1, 'Markdown', 'Markdown', 'components/MarkDown', 53, 'markdown', 'markdown', b'0', b'0', b'0', NULL, NULL, NULL, '2019-03-08 13:46:44', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (34, 10, 0, 1, 'Yaml编辑器', 'YamlEdit', 'components/YamlEdit', 54, 'dev', 'yaml', b'0', b'0', b'0', NULL, NULL, NULL, '2019-03-08 15:49:40', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (35, 1, 3, 1, '部门管理', 'Dept', 'system/dept/index', 6, 'dept', 'dept', b'0', b'0', b'0', 'dept:list', NULL, NULL, '2019-03-25 09:46:00', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (36, NULL, 6, 0, '系统工具', NULL, '', 30, 'sys-tools', 'sys-tools', b'0', b'0', b'0', NULL, NULL, NULL, '2019-03-29 10:57:35', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (37, 1, 3, 1, '岗位管理', 'Job', 'system/job/index', 7, 'Steve-Jobs', 'job', b'0', b'0', b'0', 'job:list', NULL, NULL, '2019-03-29 13:51:18', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (39, 1, 3, 1, '字典管理', 'Dict', 'system/dict/index', 8, 'dictionary', 'dict', b'0', b'0', b'0', 'dict:list', NULL, NULL, '2019-04-10 11:49:04', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (41, 6, 0, 1, '在线用户', 'OnlineUser', 'monitor/online/index', 10, 'Steve-Jobs', 'online', b'0', b'0', b'0', NULL, NULL, NULL, '2019-10-26 22:08:43', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (44, 2, 0, 2, '用户新增', NULL, '', 2, '', '', b'0', b'0', b'0', 'user:add', NULL, NULL, '2019-10-29 10:59:46', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (45, 2, 0, 2, '用户编辑', NULL, '', 3, '', '', b'0', b'0', b'0', 'user:edit', NULL, NULL, '2019-10-29 11:00:08', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (46, 2, 0, 2, '用户删除', NULL, '', 4, '', '', b'0', b'0', b'0', 'user:del', NULL, NULL, '2019-10-29 11:00:23', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (48, 3, 0, 2, '角色创建', NULL, '', 2, '', '', b'0', b'0', b'0', 'roles:add', NULL, NULL, '2019-10-29 12:45:34', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (49, 3, 0, 2, '角色修改', NULL, '', 3, '', '', b'0', b'0', b'0', 'roles:edit', NULL, NULL, '2019-10-29 12:46:16', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (50, 3, 0, 2, '角色删除', NULL, '', 4, '', '', b'0', b'0', b'0', 'roles:del', NULL, NULL, '2019-10-29 12:46:51', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (52, 5, 0, 2, '菜单新增', NULL, '', 2, '', '', b'0', b'0', b'0', 'menu:add', NULL, NULL, '2019-10-29 12:55:07', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (53, 5, 0, 2, '菜单编辑', NULL, '', 3, '', '', b'0', b'0', b'0', 'menu:edit', NULL, NULL, '2019-10-29 12:55:40', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (54, 5, 0, 2, '菜单删除', NULL, '', 4, '', '', b'0', b'0', b'0', 'menu:del', NULL, NULL, '2019-10-29 12:56:00', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (56, 35, 0, 2, '部门新增', NULL, '', 2, '', '', b'0', b'0', b'0', 'dept:add', NULL, NULL, '2019-10-29 12:57:09', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (57, 35, 0, 2, '部门编辑', NULL, '', 3, '', '', b'0', b'0', b'0', 'dept:edit', NULL, NULL, '2019-10-29 12:57:27', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (58, 35, 0, 2, '部门删除', NULL, '', 4, '', '', b'0', b'0', b'0', 'dept:del', NULL, NULL, '2019-10-29 12:57:41', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (60, 37, 0, 2, '岗位新增', NULL, '', 2, '', '', b'0', b'0', b'0', 'job:add', NULL, NULL, '2019-10-29 12:58:27', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (61, 37, 0, 2, '岗位编辑', NULL, '', 3, '', '', b'0', b'0', b'0', 'job:edit', NULL, NULL, '2019-10-29 12:58:45', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (62, 37, 0, 2, '岗位删除', NULL, '', 4, '', '', b'0', b'0', b'0', 'job:del', NULL, NULL, '2019-10-29 12:59:04', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (64, 39, 0, 2, '字典新增', NULL, '', 2, '', '', b'0', b'0', b'0', 'dict:add', NULL, NULL, '2019-10-29 13:00:17', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (65, 39, 0, 2, '字典编辑', NULL, '', 3, '', '', b'0', b'0', b'0', 'dict:edit', NULL, NULL, '2019-10-29 13:00:42', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (66, 39, 0, 2, '字典删除', NULL, '', 4, '', '', b'0', b'0', b'0', 'dict:del', NULL, NULL, '2019-10-29 13:00:59', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (73, 28, 0, 2, '任务新增', NULL, '', 2, '', '', b'0', b'0', b'0', 'timing:add', NULL, NULL, '2019-10-29 13:07:28', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (74, 28, 0, 2, '任务编辑', NULL, '', 3, '', '', b'0', b'0', b'0', 'timing:edit', NULL, NULL, '2019-10-29 13:07:41', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (75, 28, 0, 2, '任务删除', NULL, '', 4, '', '', b'0', b'0', b'0', 'timing:del', NULL, NULL, '2019-10-29 13:07:54', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (77, 18, 0, 2, '上传文件', NULL, '', 2, '', '', b'0', b'0', b'0', 'storage:add', NULL, NULL, '2019-10-29 13:09:09', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (78, 18, 0, 2, '文件编辑', NULL, '', 3, '', '', b'0', b'0', b'0', 'storage:edit', NULL, NULL, '2019-10-29 13:09:22', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (79, 18, 0, 2, '文件删除', NULL, '', 4, '', '', b'0', b'0', b'0', 'storage:del', NULL, NULL, '2019-10-29 13:09:34', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (80, 6, 0, 1, '服务监控', 'ServerMonitor', 'monitor/server/index', 14, 'codeConsole', 'server', b'0', b'0', b'0', 'monitor:list', NULL, 'admin', '2019-11-07 13:06:39', '2020-05-04 18:20:50'); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (82, 36, 0, 1, '生成配置', 'GeneratorConfig', 'generator/config', 33, 'dev', 'generator/config/:tableName', b'0', b'1', b'1', '', NULL, NULL, '2019-11-17 20:08:56', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (83, 10, 0, 1, '图表库', 'Echarts', 'components/Echarts', 50, 'chart', 'echarts', b'0', b'1', b'0', '', NULL, NULL, '2019-11-21 09:04:32', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (90, NULL, 5, 1, '运维管理', 'Mnt', '', 20, 'mnt', 'mnt', b'0', b'0', b'0', NULL, NULL, NULL, '2019-11-09 10:31:08', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (92, 90, 3, 1, '服务器', 'ServerDeploy', 'maint/server/index', 22, 'server', 'maint/serverDeploy', b'0', b'0', b'0', 'serverDeploy:list', NULL, NULL, '2019-11-10 10:29:25', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (93, 90, 3, 1, '应用管理', 'App', 'maint/app/index', 23, 'app', 'maint/app', b'0', b'0', b'0', 'app:list', NULL, NULL, '2019-11-10 11:05:16', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (94, 90, 3, 1, '部署管理', 'Deploy', 'maint/deploy/index', 24, 'deploy', 'maint/deploy', b'0', b'0', b'0', 'deploy:list', NULL, NULL, '2019-11-10 15:56:55', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (97, 90, 1, 1, '部署备份', 'DeployHistory', 'maint/deployHistory/index', 25, 'backup', 'maint/deployHistory', b'0', b'0', b'0', 'deployHistory:list', NULL, NULL, '2019-11-10 16:49:44', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (98, 90, 3, 1, '数据库管理', 'Database', 'maint/database/index', 26, 'database', 'maint/database', b'0', b'0', b'0', 'database:list', NULL, NULL, '2019-11-10 20:40:04', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (102, 97, 0, 2, '删除', NULL, '', 999, '', '', b'0', b'0', b'0', 'deployHistory:del', NULL, NULL, '2019-11-17 09:32:48', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (103, 92, 0, 2, '服务器新增', NULL, '', 999, '', '', b'0', b'0', b'0', 'serverDeploy:add', NULL, NULL, '2019-11-17 11:08:33', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (104, 92, 0, 2, '服务器编辑', NULL, '', 999, '', '', b'0', b'0', b'0', 'serverDeploy:edit', NULL, NULL, '2019-11-17 11:08:57', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (105, 92, 0, 2, '服务器删除', NULL, '', 999, '', '', b'0', b'0', b'0', 'serverDeploy:del', NULL, NULL, '2019-11-17 11:09:15', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (106, 93, 0, 2, '应用新增', NULL, '', 999, '', '', b'0', b'0', b'0', 'app:add', NULL, NULL, '2019-11-17 11:10:03', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (107, 93, 0, 2, '应用编辑', NULL, '', 999, '', '', b'0', b'0', b'0', 'app:edit', NULL, NULL, '2019-11-17 11:10:28', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (108, 93, 0, 2, '应用删除', NULL, '', 999, '', '', b'0', b'0', b'0', 'app:del', NULL, NULL, '2019-11-17 11:10:55', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (109, 94, 0, 2, '部署新增', NULL, '', 999, '', '', b'0', b'0', b'0', 'deploy:add', NULL, NULL, '2019-11-17 11:11:22', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (110, 94, 0, 2, '部署编辑', NULL, '', 999, '', '', b'0', b'0', b'0', 'deploy:edit', NULL, NULL, '2019-11-17 11:11:41', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (111, 94, 0, 2, '部署删除', NULL, '', 999, '', '', b'0', b'0', b'0', 'deploy:del', NULL, NULL, '2019-11-17 11:12:01', NULL); INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (116, 36, 0, 1, '生成预览', 'Preview', 'generator/preview', 999, 'java', 'generator/preview/:tableName', b'0', b'1', b'1', NULL, NULL, NULL, '2019-11-26 14:54:36', NULL); COMMIT; -- ---------------------------- -- Table structure for sys_quartz_job -- ---------------------------- DROP TABLE IF EXISTS `sys_quartz_job`; CREATE TABLE `sys_quartz_job` ( `job_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `bean_name` varchar(255) DEFAULT NULL COMMENT 'Spring Bean名称', `cron_expression` varchar(255) DEFAULT NULL COMMENT 'cron 表达式', `is_pause` bit(1) DEFAULT NULL COMMENT '状态:1暂停、0启用', `job_name` varchar(255) DEFAULT NULL COMMENT '任务名称', `method_name` varchar(255) DEFAULT NULL COMMENT '方法名称', `params` varchar(255) DEFAULT NULL COMMENT '参数', `description` varchar(255) DEFAULT NULL COMMENT '备注', `person_in_charge` varchar(100) DEFAULT NULL COMMENT '负责人', `email` varchar(100) DEFAULT NULL COMMENT '报警邮箱', `sub_task` varchar(100) DEFAULT NULL COMMENT '子任务ID', `pause_after_failure` bit(1) DEFAULT NULL COMMENT '任务失败后是否暂停', `create_by` varchar(255) DEFAULT NULL COMMENT '创建者', `update_by` varchar(255) DEFAULT NULL COMMENT '更新者', `create_time` datetime DEFAULT NULL COMMENT '创建日期', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`job_id`) USING BTREE, KEY `idx_is_pause` (`is_pause`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='定时任务'; -- ---------------------------- -- Records of sys_quartz_job -- ---------------------------- BEGIN; INSERT INTO `sys_quartz_job` (`job_id`, `bean_name`, `cron_expression`, `is_pause`, `job_name`, `method_name`, `params`, `description`, `person_in_charge`, `email`, `sub_task`, `pause_after_failure`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (2, 'testTask', '0/5 * * * * ?', b'1', '测试1', 'run1', 'test', '带参测试,多参使用json', '测试', NULL, NULL, NULL, NULL, 'admin', '2019-08-22 14:08:29', '2020-05-24 13:58:33'); INSERT INTO `sys_quartz_job` (`job_id`, `bean_name`, `cron_expression`, `is_pause`, `job_name`, `method_name`, `params`, `description`, `person_in_charge`, `email`, `sub_task`, `pause_after_failure`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (3, 'testTask', '0/5 * * * * ?', b'1', '测试', 'run', '', '不带参测试', 'Zheng Jie', '', '6', b'1', NULL, 'admin', '2019-09-26 16:44:39', '2020-05-24 14:48:12'); INSERT INTO `sys_quartz_job` (`job_id`, `bean_name`, `cron_expression`, `is_pause`, `job_name`, `method_name`, `params`, `description`, `person_in_charge`, `email`, `sub_task`, `pause_after_failure`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (5, 'Test', '0/5 * * * * ?', b'1', '任务告警测试', 'run', NULL, '测试', 'test', '', NULL, b'1', 'admin', 'admin', '2020-05-05 20:32:41', '2020-05-05 20:36:13'); INSERT INTO `sys_quartz_job` (`job_id`, `bean_name`, `cron_expression`, `is_pause`, `job_name`, `method_name`, `params`, `description`, `person_in_charge`, `email`, `sub_task`, `pause_after_failure`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (6, 'testTask', '0/5 * * * * ?', b'1', '测试3', 'run2', NULL, '测试3', 'Zheng Jie', '', NULL, b'1', 'admin', 'admin', '2020-05-05 20:35:41', '2020-05-05 20:36:07'); COMMIT; -- ---------------------------- -- Table structure for sys_quartz_log -- ---------------------------- DROP TABLE IF EXISTS `sys_quartz_log`; CREATE TABLE `sys_quartz_log` ( `log_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `bean_name` varchar(255) DEFAULT NULL COMMENT 'Bean名称', `cron_expression` varchar(255) DEFAULT NULL COMMENT 'cron 表达式', `is_success` bit(1) DEFAULT NULL COMMENT '是否执行成功', `job_name` varchar(255) DEFAULT NULL COMMENT '任务名称', `method_name` varchar(255) DEFAULT NULL COMMENT '方法名称', `params` varchar(255) DEFAULT NULL COMMENT '参数', `time` bigint(20) DEFAULT NULL COMMENT '执行耗时', `exception_detail` text DEFAULT NULL COMMENT '异常详情', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`log_id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=262 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='定时任务日志'; -- ---------------------------- -- Records of sys_quartz_log -- ---------------------------- BEGIN; COMMIT; -- ---------------------------- -- Table structure for sys_role -- ---------------------------- DROP TABLE IF EXISTS `sys_role`; CREATE TABLE `sys_role` ( `role_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `name` varchar(100) NOT NULL COMMENT '名称', `level` int(50) DEFAULT NULL COMMENT '角色级别', `description` varchar(255) DEFAULT NULL COMMENT '描述', `data_scope` varchar(255) DEFAULT NULL COMMENT '数据权限', `create_by` varchar(255) DEFAULT NULL COMMENT '创建者', `update_by` varchar(255) DEFAULT NULL COMMENT '更新者', `create_time` datetime DEFAULT NULL COMMENT '创建日期', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`role_id`) USING BTREE, UNIQUE KEY `uniq_name` (`name`), KEY `idx_level` (`level`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='角色表'; -- ---------------------------- -- Records of sys_role -- ---------------------------- BEGIN; INSERT INTO `sys_role` (`role_id`, `name`, `level`, `description`, `data_scope`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (1, '管理员', 1, '-', '全部', NULL, 'admin', '2018-11-23 11:04:37', '2025-01-21 14:53:13'); INSERT INTO `sys_role` (`role_id`, `name`, `level`, `description`, `data_scope`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (2, '普通用户', 2, '-', '本级', NULL, 'admin', '2018-11-23 13:09:06', '2020-09-05 10:45:12'); COMMIT; -- ---------------------------- -- Table structure for sys_roles_depts -- ---------------------------- DROP TABLE IF EXISTS `sys_roles_depts`; CREATE TABLE `sys_roles_depts` ( `role_id` bigint(20) NOT NULL COMMENT '角色ID', `dept_id` bigint(20) NOT NULL COMMENT '部门ID', PRIMARY KEY (`role_id`,`dept_id`) USING BTREE, KEY `idx_role_id` (`role_id`), KEY `idx_dept_id` (`dept_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='角色部门关联'; -- ---------------------------- -- Records of sys_roles_depts -- ---------------------------- BEGIN; COMMIT; -- ---------------------------- -- Table structure for sys_roles_menus -- ---------------------------- DROP TABLE IF EXISTS `sys_roles_menus`; CREATE TABLE `sys_roles_menus` ( `menu_id` bigint(20) NOT NULL COMMENT '菜单ID', `role_id` bigint(20) NOT NULL COMMENT '角色ID', PRIMARY KEY (`menu_id`,`role_id`) USING BTREE, KEY `idx_menu_id` (`menu_id`), KEY `idx_role_id` (`role_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='角色菜单关联'; -- ---------------------------- -- Records of sys_roles_menus -- ---------------------------- BEGIN; INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (1, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (1, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (2, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (2, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (3, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (5, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (6, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (6, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (7, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (7, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (9, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (9, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (10, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (10, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (11, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (11, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (14, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (14, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (15, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (15, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (18, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (19, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (19, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (21, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (21, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (22, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (22, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (23, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (23, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (24, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (24, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (27, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (27, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (28, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (30, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (30, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (32, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (32, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (33, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (33, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (34, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (34, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (35, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (36, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (36, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (37, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (39, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (41, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (44, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (45, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (46, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (48, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (49, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (50, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (52, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (53, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (54, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (56, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (57, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (58, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (60, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (61, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (62, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (64, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (65, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (66, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (73, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (74, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (75, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (77, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (78, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (79, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (80, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (80, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (82, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (82, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (83, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (83, 2); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (90, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (92, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (93, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (94, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (97, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (98, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (102, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (103, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (104, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (105, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (106, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (107, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (108, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (109, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (110, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (111, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (116, 1); INSERT INTO `sys_roles_menus` (`menu_id`, `role_id`) VALUES (116, 2); COMMIT; -- ---------------------------- -- Table structure for sys_user -- ---------------------------- DROP TABLE IF EXISTS `sys_user`; CREATE TABLE `sys_user` ( `user_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `dept_id` bigint(20) DEFAULT NULL COMMENT '部门名称', `username` varchar(180) DEFAULT NULL COMMENT '用户名', `nick_name` varchar(255) DEFAULT NULL COMMENT '昵称', `gender` varchar(2) DEFAULT NULL COMMENT '性别', `phone` varchar(255) DEFAULT NULL COMMENT '手机号码', `email` varchar(180) DEFAULT NULL COMMENT '邮箱', `avatar_name` varchar(255) DEFAULT NULL COMMENT '头像地址', `avatar_path` varchar(255) DEFAULT NULL COMMENT '头像真实路径', `password` varchar(255) DEFAULT NULL COMMENT '密码', `is_admin` bit(1) DEFAULT b'0' COMMENT '是否为admin账号', `enabled` bit(1) DEFAULT NULL COMMENT '状态:1启用、0禁用', `create_by` varchar(255) DEFAULT NULL COMMENT '创建者', `update_by` varchar(255) DEFAULT NULL COMMENT '更新者', `pwd_reset_time` datetime DEFAULT NULL COMMENT '修改密码的时间', `create_time` datetime DEFAULT NULL COMMENT '创建日期', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`user_id`) USING BTREE, UNIQUE KEY `uniq_email` (`email`) USING BTREE, UNIQUE KEY `uniq_username` (`username`) USING BTREE, KEY `idx_dept_id` (`dept_id`) USING BTREE, KEY `idx_enabled` (`enabled`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='系统用户'; -- ---------------------------- -- Records of sys_user -- ---------------------------- BEGIN; INSERT INTO `sys_user` (`user_id`, `dept_id`, `username`, `nick_name`, `gender`, `phone`, `email`, `avatar_name`, `avatar_path`, `password`, `is_admin`, `enabled`, `create_by`, `update_by`, `pwd_reset_time`, `create_time`, `update_time`) VALUES (1, 2, 'admin', '管理员', '男', '18888888888', '201507802@qq.com', 'avatar-20250122102642222.png', '/Users/jie/Documents/work/private/eladmin/~/avatar/avatar-20250122102642222.png', '$2a$10$Egp1/gvFlt7zhlXVfEFw4OfWQCGPw0ClmMcc6FjTnvXNRVf9zdMRa', b'1', b'1', NULL, 'admin', '2020-05-03 16:38:31', '2018-08-23 09:11:56', '2025-01-22 10:26:42'); INSERT INTO `sys_user` (`user_id`, `dept_id`, `username`, `nick_name`, `gender`, `phone`, `email`, `avatar_name`, `avatar_path`, `password`, `is_admin`, `enabled`, `create_by`, `update_by`, `pwd_reset_time`, `create_time`, `update_time`) VALUES (2, 7, 'test', '测试', '男', '19999999999', '231@qq.com', NULL, NULL, '$2a$10$4XcyudOYTSz6fue6KFNMHeUQnCX5jbBQypLEnGk1PmekXt5c95JcK', b'0', b'1', 'admin', 'admin', NULL, '2020-05-05 11:15:49', '2025-01-21 14:53:04'); COMMIT; -- ---------------------------- -- Table structure for sys_users_jobs -- ---------------------------- DROP TABLE IF EXISTS `sys_users_jobs`; CREATE TABLE `sys_users_jobs` ( `user_id` bigint(20) NOT NULL COMMENT '用户ID', `job_id` bigint(20) NOT NULL COMMENT '岗位ID', PRIMARY KEY (`user_id`,`job_id`), KEY `idx_user_id` (`user_id`), KEY `idx_job_id` (`job_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci COMMENT='用户与岗位关联表'; -- ---------------------------- -- Records of sys_users_jobs -- ---------------------------- BEGIN; INSERT INTO `sys_users_jobs` (`user_id`, `job_id`) VALUES (1, 11); INSERT INTO `sys_users_jobs` (`user_id`, `job_id`) VALUES (2, 12); COMMIT; -- ---------------------------- -- Table structure for sys_users_roles -- ---------------------------- DROP TABLE IF EXISTS `sys_users_roles`; CREATE TABLE `sys_users_roles` ( `user_id` bigint(20) NOT NULL COMMENT '用户ID', `role_id` bigint(20) NOT NULL COMMENT '角色ID', PRIMARY KEY (`user_id`,`role_id`) USING BTREE, KEY `idx_user_id` (`user_id`), KEY `idx_role_id` (`role_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='用户角色关联'; -- ---------------------------- -- Records of sys_users_roles -- ---------------------------- BEGIN; INSERT INTO `sys_users_roles` (`user_id`, `role_id`) VALUES (1, 1); INSERT INTO `sys_users_roles` (`user_id`, `role_id`) VALUES (2, 2); COMMIT; -- ---------------------------- -- Table structure for tool_alipay_config -- ---------------------------- DROP TABLE IF EXISTS `tool_alipay_config`; CREATE TABLE `tool_alipay_config` ( `config_id` bigint(20) NOT NULL COMMENT 'ID', `app_id` varchar(255) DEFAULT NULL COMMENT '应用ID', `charset` varchar(255) DEFAULT NULL COMMENT '编码', `format` varchar(255) DEFAULT NULL COMMENT '类型 固定格式json', `gateway_url` varchar(255) DEFAULT NULL COMMENT '网关地址', `notify_url` varchar(255) DEFAULT NULL COMMENT '异步回调', `private_key` text DEFAULT NULL COMMENT '私钥', `public_key` text DEFAULT NULL COMMENT '公钥', `return_url` varchar(255) DEFAULT NULL COMMENT '回调地址', `sign_type` varchar(255) DEFAULT NULL COMMENT '签名方式', `sys_service_provider_id` varchar(255) DEFAULT NULL COMMENT '商户号', PRIMARY KEY (`config_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='支付宝配置类'; -- ---------------------------- -- Records of tool_alipay_config -- ---------------------------- BEGIN; COMMIT; -- ---------------------------- -- Table structure for tool_email_config -- ---------------------------- DROP TABLE IF EXISTS `tool_email_config`; CREATE TABLE `tool_email_config` ( `config_id` bigint(20) NOT NULL COMMENT 'ID', `from_user` varchar(255) DEFAULT NULL COMMENT '收件人', `host` varchar(255) DEFAULT NULL COMMENT '邮件服务器SMTP地址', `pass` varchar(255) DEFAULT NULL COMMENT '密码', `port` varchar(255) DEFAULT NULL COMMENT '端口', `user` varchar(255) DEFAULT NULL COMMENT '发件者用户名', PRIMARY KEY (`config_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='邮箱配置'; -- ---------------------------- -- Records of tool_email_config -- ---------------------------- BEGIN; COMMIT; -- ---------------------------- -- Table structure for tool_local_storage -- ---------------------------- DROP TABLE IF EXISTS `tool_local_storage`; CREATE TABLE `tool_local_storage` ( `storage_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `real_name` varchar(255) DEFAULT NULL COMMENT '文件真实的名称', `name` varchar(255) DEFAULT NULL COMMENT '文件名', `suffix` varchar(255) DEFAULT NULL COMMENT '后缀', `path` varchar(255) DEFAULT NULL COMMENT '路径', `type` varchar(255) DEFAULT NULL COMMENT '类型', `size` varchar(100) DEFAULT NULL COMMENT '大小', `create_by` varchar(255) DEFAULT NULL COMMENT '创建者', `update_by` varchar(255) DEFAULT NULL COMMENT '更新者', `create_time` datetime DEFAULT NULL COMMENT '创建日期', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`storage_id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='本地存储'; -- ---------------------------- -- Records of tool_local_storage -- ---------------------------- BEGIN; COMMIT; -- ---------------------------- -- Table structure for tool_s3_storage -- ---------------------------- DROP TABLE IF EXISTS `tool_s3_storage`; CREATE TABLE `tool_s3_storage` ( `storage_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `file_name` varchar(255) NOT NULL COMMENT '文件名称', `file_real_name` varchar(255) NOT NULL COMMENT '真实存储的名称', `file_size` varchar(100) NOT NULL COMMENT '文件大小', `file_mime_type` varchar(50) NOT NULL COMMENT '文件MIME 类型', `file_type` varchar(50) NOT NULL COMMENT '文件类型', `file_path` tinytext NOT NULL COMMENT '文件路径', `create_by` varchar(255) NOT NULL COMMENT '创建者', `update_by` varchar(255) NOT NULL COMMENT '更新者', `create_time` datetime NOT NULL COMMENT '创建日期', `update_time` datetime NOT NULL COMMENT '更新时间', PRIMARY KEY (`storage_id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='s3 协议对象存储'; -- ---------------------------- -- Records of tool_s3_storage -- ---------------------------- BEGIN; INSERT INTO `tool_s3_storage` (`storage_id`, `file_name`, `file_real_name`, `file_size`, `file_mime_type`, `file_type`, `file_path`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (4, 'tx.jpg', '2ca1de24d8fa422eae4ede30e97c46d8.jpg', '29.67KB', 'image/jpeg', 'jpg', '2025-06/2ca1de24d8fa422eae4ede30e97c46d8.jpg', 'admin', 'admin', '2025-06-25 15:48:22', '2025-06-25 15:48:22'); COMMIT; SET FOREIGN_KEY_CHECKS = 1; ================================================ FILE: sql/quartz.sql ================================================ drop table if exists qrtz_fired_triggers; drop table if exists qrtz_paused_trigger_grps; drop table if exists qrtz_scheduler_state; drop table if exists qrtz_locks; drop table if exists qrtz_simple_triggers; drop table if exists qrtz_simprop_triggers; drop table if exists qrtz_cron_triggers; drop table if exists qrtz_blob_triggers; drop table if exists qrtz_triggers; drop table if exists qrtz_job_details; drop table if exists qrtz_calendars; create table qrtz_job_details( sched_name varchar(120) not null, job_name varchar(200) not null, job_group varchar(200) not null, description varchar(250) null, job_class_name varchar(250) not null, is_durable varchar(1) not null, is_nonconcurrent varchar(1) not null, is_update_data varchar(1) not null, requests_recovery varchar(1) not null, job_data blob null, primary key (sched_name, job_name, job_group)) engine=innodb; create table qrtz_triggers ( sched_name varchar(120) not null, trigger_name varchar(200) not null, trigger_group varchar(200) not null, job_name varchar(200) not null, job_group varchar(200) not null, description varchar(250) null, next_fire_time bigint(13) null, prev_fire_time bigint(13) null, priority integer null, trigger_state varchar(16) not null, trigger_type varchar(8) not null, start_time bigint(13) not null, end_time bigint(13) null, calendar_name varchar(200) null, misfire_instr smallint(2) null, job_data blob null, primary key (sched_name, trigger_name, trigger_group), foreign key (sched_name, job_name, job_group) references qrtz_job_details(sched_name, job_name, job_group)) engine=innodb; create table qrtz_simple_triggers ( sched_name varchar(120) not null, trigger_name varchar(200) not null, trigger_group varchar(200) not null, repeat_count bigint(7) not null, repeat_interval bigint(12) not null, times_triggered bigint(10) not null, primary key (sched_name, trigger_name, trigger_group), foreign key (sched_name, trigger_name, trigger_group) references qrtz_triggers(sched_name, trigger_name, trigger_group)) engine=innodb; create table qrtz_cron_triggers ( sched_name varchar(120) not null, trigger_name varchar(200) not null, trigger_group varchar(200) not null, cron_expression varchar(120) not null, time_zone_id varchar(80), primary key (sched_name, trigger_name, trigger_group), foreign key (sched_name, trigger_name, trigger_group) references qrtz_triggers(sched_name, trigger_name, trigger_group)) engine=innodb; create table qrtz_simprop_triggers ( sched_name varchar(120) not null, trigger_name varchar(200) not null, trigger_group varchar(200) not null, str_prop_1 varchar(512) null, str_prop_2 varchar(512) null, str_prop_3 varchar(512) null, int_prop_1 int null, int_prop_2 int null, long_prop_1 bigint null, long_prop_2 bigint null, dec_prop_1 numeric(13, 4) null, dec_prop_2 numeric(13, 4) null, bool_prop_1 varchar(1) null, bool_prop_2 varchar(1) null, primary key (sched_name, trigger_name, trigger_group), foreign key (sched_name, trigger_name, trigger_group) references qrtz_triggers(sched_name, trigger_name, trigger_group)) engine=innodb; create table qrtz_blob_triggers ( sched_name varchar(120) not null, trigger_name varchar(200) not null, trigger_group varchar(200) not null, blob_data blob null, primary key (sched_name, trigger_name, trigger_group), index (sched_name, trigger_name, trigger_group), foreign key (sched_name, trigger_name, trigger_group) references qrtz_triggers(sched_name, trigger_name, trigger_group)) engine=innodb; create table qrtz_calendars ( sched_name varchar(120) not null, calendar_name varchar(200) not null, calendar blob not null, primary key (sched_name, calendar_name)) engine=innodb; create table qrtz_paused_trigger_grps ( sched_name varchar(120) not null, trigger_group varchar(200) not null, primary key (sched_name, trigger_group)) engine=innodb; create table qrtz_fired_triggers ( sched_name varchar(120) not null, entry_id varchar(95) not null, trigger_name varchar(200) not null, trigger_group varchar(200) not null, instance_name varchar(200) not null, fired_time bigint(13) not null, sched_time bigint(13) not null, priority integer not null, state varchar(16) not null, job_name varchar(200) null, job_group varchar(200) null, is_nonconcurrent varchar(1) null, requests_recovery varchar(1) null, primary key (sched_name, entry_id)) engine=innodb; create table qrtz_scheduler_state ( sched_name varchar(120) not null, instance_name varchar(200) not null, last_checkin_time bigint(13) not null, checkin_interval bigint(13) not null, primary key (sched_name, instance_name)) engine=innodb; create table qrtz_locks ( sched_name varchar(120) not null, lock_name varchar(40) not null, primary key (sched_name, lock_name)) engine=innodb; create index idx_qrtz_j_req_recovery on qrtz_job_details(sched_name, requests_recovery); create index idx_qrtz_j_grp on qrtz_job_details(sched_name, job_group); create index idx_qrtz_t_j on qrtz_triggers(sched_name, job_name, job_group); create index idx_qrtz_t_jg on qrtz_triggers(sched_name, job_group); create index idx_qrtz_t_c on qrtz_triggers(sched_name, calendar_name); create index idx_qrtz_t_g on qrtz_triggers(sched_name, trigger_group); create index idx_qrtz_t_state on qrtz_triggers(sched_name, trigger_state); create index idx_qrtz_t_n_state on qrtz_triggers(sched_name, trigger_name, trigger_group, trigger_state); create index idx_qrtz_t_n_g_state on qrtz_triggers(sched_name, trigger_group, trigger_state); create index idx_qrtz_t_next_fire_time on qrtz_triggers(sched_name, next_fire_time); create index idx_qrtz_t_nft_st on qrtz_triggers(sched_name, trigger_state, next_fire_time); create index idx_qrtz_t_nft_misfire on qrtz_triggers(sched_name, misfire_instr, next_fire_time); create index idx_qrtz_t_nft_st_misfire on qrtz_triggers(sched_name, misfire_instr, next_fire_time, trigger_state); create index idx_qrtz_t_nft_st_misfire_grp on qrtz_triggers(sched_name, misfire_instr, next_fire_time, trigger_group, trigger_state); create index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(sched_name, instance_name); create index idx_qrtz_ft_inst_job_req_rcvry on qrtz_fired_triggers(sched_name, instance_name, requests_recovery); create index idx_qrtz_ft_j_g on qrtz_fired_triggers(sched_name, job_name, job_group); create index idx_qrtz_ft_jg on qrtz_fired_triggers(sched_name, job_group); create index idx_qrtz_ft_t_g on qrtz_fired_triggers(sched_name, trigger_name, trigger_group); create index idx_qrtz_ft_tg on qrtz_fired_triggers(sched_name, trigger_group); commit;