疯狂的狮子li
2021-07-28 eb2be90e74adfa06ee251f80c08243d0301ce467
Merge remote-tracking branch 'origin/dev'

# Conflicts:
# README.md
# docker/docker-compose.yml
# pom.xml
# ruoyi-admin/pom.xml
# ruoyi-admin/src/main/resources/application-dev.yml
# ruoyi-admin/src/main/resources/application-prod.yml
# ruoyi-common/pom.xml
# ruoyi-demo/pom.xml
# ruoyi-extend/pom.xml
# ruoyi-extend/ruoyi-monitor-admin/pom.xml
# ruoyi-framework/pom.xml
# ruoyi-generator/pom.xml
# ruoyi-generator/src/main/resources/vm/java/editBo.java.vm
# ruoyi-generator/src/main/resources/vm/java/queryBo.java.vm
# ruoyi-quartz/pom.xml
# ruoyi-system/pom.xml
# ruoyi-ui/package.json
# ruoyi-ui/src/views/index.vue
已修改86个文件
已添加32个文件
已重命名2个文件
已删除9个文件
4568 ■■■■ 文件已修改
README.md 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docker/deploy.sh 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docker/docker-compose.yml 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/pom.xml 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-dev.yml 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-prod.yml 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/IServicePlus.java 357 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/ServicePlusImpl.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/validate/AddGroup.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/validate/EditGroup.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/BeanCopyUtils.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoAddBo.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoEditBo.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoQueryBo.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestTreeAddBo.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestTreeEditBo.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestTreeQueryBo.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestTreeBo.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestTreeVo.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestTreeService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/resources/mapper/demo/TestDemoMapper.xml 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-monitor-admin/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/addBo.java.vm 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/bo.java.vm 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/controller.java.vm 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/editBo.java.vm 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/queryBo.java.vm 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/service.java.vm 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/vo.java.vm 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/pom.xml 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/constant/CloudConstant.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/entity/UploadResult.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/CloudServiceEnumd.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/PolicyType.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/exception/OssException.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/factory/OssFactory.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/properties/CloudStorageProperties.java 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/service/ICloudStorageService.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/service/abstractd/AbstractCloudStorageService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/AliyunCloudStorageServiceImpl.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/MinioCloudStorageServiceImpl.java 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/QcloudCloudStorageServiceImpl.java 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/QiniuCloudStorageServiceImpl.java 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/system/controller/SysOssController.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/system/domain/SysOss.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/system/domain/vo/SysOssVo.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/system/mapper/SysOssMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/system/service/ISysOssService.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/resources/mapper/system/SysOssMapper.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/package.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/demo/demo.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/system/oss.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/components/Editor/index.vue 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/components/FileUpload/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/components/ImageUpload/index.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/components/RuoYi/Doc/index.vue 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/components/RuoYi/Git/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/components/TopNav/index.vue 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/router/index.js 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/utils/ossdownload.js 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/utils/request.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/demo/demo/index.vue 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/index.vue 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/monitor/job/index.vue 50 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/monitor/job/log.vue 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/dict/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/menu/index.vue 58 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/oss/index.vue 322 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/role/index.vue 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/user/index.vue 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/user/profile/resetPwd.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/tool/gen/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/oss.sql 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md
@@ -4,14 +4,14 @@
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/blob/master/LICENSE)
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
<br>
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-2.5.2-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-2.6.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.4-blue.svg)]()
[![JDK-8+](https://img.shields.io/badge/JDK-8+-green.svg)]()
[![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]()
RuoYi-Vue-Plus æ˜¯åŸºäºŽ RuoYi-Vue é’ˆå¯¹ `分布式集群` åœºæ™¯å‡çº§ å®šæœŸä¸Ž RuoYi-Vue åŒæ­¥
集成 Lock4j dynamic-datasource ç­‰åˆ†å¸ƒå¼åœºæ™¯è§£å†³æ–¹æ¡ˆ
集成 Lock4j dynamic-datasource OSS存储 ç­‰åˆ†å¸ƒå¼åœºæ™¯è§£å†³æ–¹æ¡ˆ
集成 Mybatis-Plus Lombok Hutool ç­‰ä¾¿æ·å¼€å‘工具 é€‚配重写相关业务 ä¾¿äºŽå¼€å‘ 
@@ -33,6 +33,7 @@
* Redis客户端 é‡‡ç”¨ Redisson æ€§èƒ½æ›´å¼º
* åˆ†å¸ƒå¼é” Lock4j æ³¨è§£é”ã€å·¥å…·é” å¤šç§å¤šæ ·
* éƒ¨ç½²æ–¹å¼ Docker å®¹å™¨ç¼–排 ä¸€é”®éƒ¨ç½²ä¸šåŠ¡é›†ç¾¤
* æ–‡ä»¶å­˜å‚¨ OSS å¯¹è±¡å­˜å‚¨æ¨¡å— æ”¯æŒ(Minio、七牛、阿里、腾讯)
## å‚考文档
@@ -89,13 +90,14 @@
* é›†æˆ dynamic-datasource å¤šæ•°æ®æº(默认支持MySQL,其他种类需自行适配)
* é›†æˆ Lock4j å®žçŽ°åˆ†å¸ƒå¼ æ³¨è§£é”ã€å·¥å…·é” å¤šç§å¤šæ ·
* å¢žåŠ  Docker å®¹å™¨ç¼–排 æ‰“包插件与部署脚本
* ç§»é™¤ æœ¬åœ°æ–‡ä»¶ä¸Šä¼  æ”¹ä¸º OSS对象存储 æ”¯æŒ(Minio、七牛、阿里、腾讯)
### ä»£ç æ”¹åЍ
* æ‰€æœ‰åŽŸç”ŸåŠŸèƒ½ä½¿ç”¨ Mybatis-Plus ä¸Ž Lombok é‡å†™
* å¢žåŠ  IServicePlus ä¸Ž BaseMapperPlus å¯è‡ªå®šä¹‰é€šç”¨æ–¹æ³•
* ä»£ç ç”Ÿæˆæ¨¡æ¿ æ”¹ä¸ºé€‚配 Mybatis-Plus çš„代码
* ä»£ç ç”Ÿæˆæ¨¡æ¿ æ‹†åˆ†å‡º Vo,QueryBo,AddBo,EditBo ç­‰é¢†åŸŸå¯¹è±¡
* ä»£ç ç”Ÿæˆæ¨¡æ¿ æ ¹æ® Alibaba ä»£ç è§„约 æ‹†åˆ†å‡º VO、BO ç­‰é¢†åŸŸå¯¹è±¡
* ä»£ç ç”Ÿæˆæ¨¡æ¿ å¢žåŠ  æ–‡æ¡£æ³¨è§£ ä¸Ž æ ¡éªŒæ³¨è§£ ç®€åŒ–通用操作
* é¡¹ç›®ä¿®æ”¹ä¸º maven多环境配置
* é¡¹ç›®é…ç½®ä¿®æ”¹ä¸º application.yml ç»Ÿä¸€ç®¡ç†
docker/deploy.sh
@@ -6,10 +6,17 @@
    exit 1
}
#开启所需端口
#开启所需端口(生产环境不推荐开启)
port(){
    # mysql ç«¯å£
    firewall-cmd --add-port=3306/tcp --permanent
    # redis ç«¯å£
    firewall-cmd --add-port=6379/tcp --permanent
    # minio api ç«¯å£
    firewall-cmd --add-port=9000/tcp --permanent
    # minio æŽ§åˆ¶å°ç«¯å£
    firewall-cmd --add-port=9001/tcp --permanent
    # é‡å¯é˜²ç«å¢™
    service firewalld restart
}
@@ -24,7 +31,7 @@
#启动基础模块
base(){
    docker-compose up -d mysql nginx-web redis
    docker-compose up -d mysql nginx-web redis minio
}
#启动基础模块
docker/docker-compose.yml
@@ -18,6 +18,8 @@
      - /docker/mysql/data/:/var/lib/mysql/
      # é…ç½®æŒ‚è½½
      - /docker/mysql/conf/:/etc/mysql/conf.d/
      # ä¸»æœºæœ¬æœºæ—¶é—´æ–‡ä»¶æ˜ å°„ ä¸Žæœ¬æœºæ—¶é—´åŒæ­¥
      - /etc/localtime:/etc/localtime:ro
    command:
      # å°†mysql8.0默认密码策略 ä¿®æ”¹ä¸º åŽŸå…ˆ ç­–ç•¥ (mysql8.0对其默认策略做了更改 ä¼šå¯¼è‡´å¯†ç æ— æ³•匹配)
      --default-authentication-plugin=mysql_native_password
@@ -68,6 +70,8 @@
      - /docker/redis/conf/redis.conf:/redis.conf:rw
      # æ•°æ®æ–‡ä»¶
      - /docker/redis/data:/data:rw
      # ä¸»æœºæœ¬æœºæ—¶é—´æ–‡ä»¶æ˜ å°„ ä¸Žæœ¬æœºæ—¶é—´åŒæ­¥
      - /etc/localtime:/etc/localtime:ro
    command: "redis-server --appendonly yes"
    privileged: true
    restart: always
@@ -75,8 +79,35 @@
      ruoyi_net:
        ipv4_address: 172.30.0.48
  minio:
    image: minio/minio:RELEASE.2021-07-08T01-15-01Z
    hostname: "minio"
    ports:
      # api ç«¯å£
      - 9000:9000
      # æŽ§åˆ¶å°ç«¯å£
      - 9001:9001
    environment:
      # ç®¡ç†åŽå°ç”¨æˆ·å
      MINIO_ACCESS_KEY: ruoyi
      # ç®¡ç†åŽå°å¯†ç ï¼Œæœ€å°8个字符
      MINIO_SECRET_KEY: ruoyi123
    volumes:
      # æ˜ å°„当前目录下的data目录至容器内/data目录
      - /docker/minio/data:/data
      # æ˜ å°„配置目录
      - /docker/minio/config:/root/.minio/
      # ä¸»æœºæœ¬æœºæ—¶é—´æ–‡ä»¶æ˜ å°„ ä¸Žæœ¬æœºæ—¶é—´åŒæ­¥
      - /etc/localtime:/etc/localtime:ro
    command: server --console-address ':9001' /data  # æŒ‡å®šå®¹å™¨ä¸­çš„目录 /data
    privileged: true
    restart: always
    networks:
      ruoyi_net:
        ipv4_address: 172.30.0.54
  ruoyi-server1:
    image: "ruoyi/ruoyi-server:2.5.2"
    image: "ruoyi/ruoyi-server:2.6.0"
    environment:
      - TZ=Asia/Shanghai
    volumes:
@@ -90,7 +121,7 @@
        ipv4_address: 172.30.0.60
  ruoyi-server2:
    image: "ruoyi/ruoyi-server:2.5.2"
    image: "ruoyi/ruoyi-server:2.6.0"
    environment:
      - TZ=Asia/Shanghai
    volumes:
@@ -104,7 +135,7 @@
        ipv4_address: 172.30.0.61
  ruoyi-monitor-admin:
    image: "ruoyi/ruoyi-monitor-admin:2.5.2"
    image: "ruoyi/ruoyi-monitor-admin:2.6.0"
    environment:
      - TZ=Asia/Shanghai
    privileged: true
pom.xml
@@ -6,15 +6,15 @@
    <groupId>com.ruoyi</groupId>
    <artifactId>ruoyi-vue-plus</artifactId>
    <version>2.5.2</version>
    <version>2.6.0</version>
    <name>RuoYi-Vue-Plus</name>
    <url>https://gitee.com/JavaLionLi/RuoYi-Vue-Plus</url>
    <description>RuoYi-Vue-Plus后台管理系统</description>
    <properties>
        <ruoyi-vue-plus.version>2.5.2</ruoyi-vue-plus.version>
        <spring-boot.version>2.4.8</spring-boot.version>
        <ruoyi-vue-plus.version>2.6.0</ruoyi-vue-plus.version>
        <spring-boot.version>2.5.3</spring-boot.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
@@ -25,19 +25,25 @@
        <velocity.version>1.7</velocity.version>
        <jwt.version>0.9.1</jwt.version>
        <mybatis-plus.version>3.4.3</mybatis-plus.version>
        <hutool.version>5.7.4</hutool.version>
        <hutool.version>5.7.6</hutool.version>
        <feign.version>3.0.3</feign.version>
        <feign-okhttp.version>11.0</feign-okhttp.version>
        <spring-boot-admin.version>2.4.3</spring-boot-admin.version>
        <redisson.version>3.16.0</redisson.version>
        <redisson.version>3.16.1</redisson.version>
        <lock4j.version>2.2.1</lock4j.version>
        <datasource.version>3.4.0</datasource.version>
        <datasource.version>3.4.1</datasource.version>
        <!-- OSS é…ç½® -->
        <qiniu.version>7.8.0</qiniu.version>
        <aliyun.oss.version>3.13.0</aliyun.oss.version>
        <qcloud.cos.version>5.6.47</qcloud.cos.version>
        <minio.version>8.3.0</minio.version>
        <!-- docker é…ç½® -->
        <docker.registry.url>localhost</docker.registry.url>
        <docker.registry.host>http://${docker.registry.url}:2375</docker.registry.host>
        <docker.namespace>ruoyi</docker.namespace>
        <docker.plugin.version>1.2.0</docker.plugin.version>
        <docker.plugin.version>1.2.2</docker.plugin.version>
    </properties>
    <!-- ä¾èµ–声明 -->
@@ -183,6 +189,13 @@
            <!-- demo模块 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-oss</artifactId>
                <version>${ruoyi-vue-plus.version}</version>
            </dependency>
            <!-- demo模块 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-demo</artifactId>
                <version>${ruoyi-vue-plus.version}</version>
            </dependency>
@@ -199,6 +212,7 @@
        <module>ruoyi-common</module>
        <module>ruoyi-demo</module>
        <module>ruoyi-extend</module>
        <module>ruoyi-oss</module>
    </modules>
    <packaging>pom</packaging>
ruoyi-admin/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.5.2</version>
        <version>2.6.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
@@ -42,6 +42,11 @@
            <artifactId>ruoyi-quartz</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-oss</artifactId>
        </dependency>
        <!-- ä»£ç ç”Ÿæˆ-->
        <dependency>
            <groupId>com.ruoyi</groupId>
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java
@@ -3,24 +3,16 @@
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.framework.config.ServerConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
/**
 * é€šç”¨è¯·æ±‚处理
@@ -31,9 +23,6 @@
public class CommonController
{
    private static final Logger log = LoggerFactory.getLogger(CommonController.class);
    @Autowired
    private ServerConfig serverConfig;
    /**
     * é€šç”¨ä¸‹è½½è¯·æ±‚
@@ -64,30 +53,6 @@
        catch (Exception e)
        {
            log.error("下载文件失败", e);
        }
    }
    /**
     * é€šç”¨ä¸Šä¼ è¯·æ±‚
     */
    @PostMapping("/common/upload")
    public AjaxResult uploadFile(MultipartFile file) throws Exception
    {
        try
        {
            // ä¸Šä¼ æ–‡ä»¶è·¯å¾„
            String filePath = RuoYiConfig.getUploadPath();
            // ä¸Šä¼ å¹¶è¿”回新文件名称
            String fileName = FileUploadUtils.upload(filePath, file);
            String url = serverConfig.getUrl() + fileName;
            Map<String,Object> ajax = new HashMap<>();
            ajax.put("fileName", fileName);
            ajax.put("url", url);
            return AjaxResult.success(ajax);
        }
        catch (Exception e)
        {
            return AjaxResult.error(e.getMessage());
        }
    }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
@@ -69,9 +69,12 @@
        {
            return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
        }
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        SysUser sysUser = loginUser.getUser();
        user.setUserId(sysUser.getUserId());
        user.setPassword(null);
        if (userService.updateUserProfile(user) > 0)
        {
            LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
            // æ›´æ–°ç¼“存用户信息
            loginUser.getUser().setNickName(user.getNickName());
            loginUser.getUser().setPhonenumber(user.getPhonenumber());
ruoyi-admin/src/main/resources/application-dev.yml
@@ -42,6 +42,10 @@
        testOnReturn: false
        # æ³¨æ„è¿™ä¸ªå€¼å’Œdruid原生不一致,默认启动了stat
        filters: stat
--- # druid é…ç½®
spring:
  datasource:
    druid:
      webStatFilter:
        enabled: true
@@ -63,7 +67,9 @@
        wall:
          config:
            multi-statement-allow: true
  # redis é…ç½®
--- # redis é…ç½®
spring:
  redis:
    # åœ°å€
    host: localhost
@@ -78,7 +84,6 @@
    # æ˜¯å¦å¼€å¯ssl
    ssl: false
--- # redisson å®¢æˆ·ç«¯é…ç½®
redisson:
  # çº¿ç¨‹æ± æ•°é‡
  threads: 16
@@ -137,3 +142,42 @@
  endpoint:
    logfile:
      external-file: ./logs/sys-console.log
--- # OSS äº‘存储(界面 <参数设置> å¯åˆ‡æ¢)
cloud-storage:
  # minio配置
  minio:
    endpoint: http://localhost:9000
    accessKey: ruoyi
    secretKey: ruoyi123
    bucketName: ruoyi
  # ä¸ƒç‰›äº‘配置
  qiniu:
    domain: http://XXX.XXXX.com
    prefix:
    accessKey: XXXXXXXXXXXXXXX
    secretKey: XXXXXXXXXXXXXXX
    bucketName: ruoyi
    isHttps: false
    # z0 åŽä¸œ  z1 åŽåŒ—  z2 åŽå—  na0 åŒ—美  as0 ä¸œå—亚
    # ä¸å¡«ä¸ºè‡ªåŠ¨èŽ·å–(性能低 æ˜“出问题)
    region: z0
  # é˜¿é‡Œäº‘配置
  aliyun:
    endpoint: http://oss-cn-beijing.aliyuncs.com
    prefix:
    accessKeyId: XXXXXXXXXXXXXXX
    accessKeySecret: XXXXXXXXXXXXXXX
    bucketName: ruoyi
  # è…¾è®¯äº‘配置
  qcloud:
    endpoint: http://cos.ap-beijing.myqcloud.com
    prefix:
    secretId: XXXXXXXXXXXXXXX
    secretKey: XXXXXXXXXXXXXXX
    # è…¾è®¯äº‘bucket名规则 æ ¼å¼ä¸º BucketName-APPID æ­¤å¤„填写的存储桶名称必须为此格式
    bucketName: ruoyi-1250000000
    isHttps: false
    # åœ°åŸŸåå‚考官方文档
    # https://cloud.tencent.com/document/product/436/6224
    region: ap-beijing
ruoyi-admin/src/main/resources/application-prod.yml
@@ -42,6 +42,10 @@
        testOnReturn: false
        # æ³¨æ„è¿™ä¸ªå€¼å’Œdruid原生不一致,默认启动了stat
        filters: stat
--- # druid é…ç½®
spring:
  datasource:
    druid:
      webStatFilter:
        enabled: true
@@ -63,7 +67,9 @@
        wall:
          config:
            multi-statement-allow: true
  # redis é…ç½®
--- # redis é…ç½®
spring:
  redis:
    # åœ°å€
    host: 172.30.0.48
@@ -78,7 +84,6 @@
    # æ˜¯å¦å¼€å¯ssl
    ssl: false
--- # redisson å®¢æˆ·ç«¯é…ç½®
redisson:
  # çº¿ç¨‹æ± æ•°é‡
  threads: 16
@@ -137,3 +142,42 @@
  endpoint:
    logfile:
      external-file: ./logs/sys-console.log
--- # OSS äº‘存储(界面 <参数设置> å¯åˆ‡æ¢)
cloud-storage:
  # minio配置
  minio:
    endpoint: http://172.30.0.54:9000
    accessKey: ruoyi
    secretKey: ruoyi123
    bucketName: ruoyi
  # ä¸ƒç‰›äº‘配置
  qiniu:
    domain: http://XXX.XXXX.com
    prefix:
    accessKey: XXXXXXXXXXXXXXX
    secretKey: XXXXXXXXXXXXXXX
    bucketName: ruoyi
    isHttps: false
    # z0 åŽä¸œ  z1 åŽåŒ—  z2 åŽå—  na0 åŒ—美  as0 ä¸œå—亚
    # ä¸å¡«ä¸ºè‡ªåŠ¨èŽ·å–(性能低 æ˜“出问题)
    region: z0
  # é˜¿é‡Œäº‘配置
  aliyun:
    endpoint: http://oss-cn-beijing.aliyuncs.com
    prefix:
    accessKeyId: XXXXXXXXXXXXXXX
    accessKeySecret: XXXXXXXXXXXXXXX
    bucketName: ruoyi
  # è…¾è®¯äº‘配置
  qcloud:
    endpoint: http://cos.ap-beijing.myqcloud.com
    prefix:
    secretId: XXXXXXXXXXXXXXX
    secretKey: XXXXXXXXXXXXXXX
    # è…¾è®¯äº‘bucket名规则 æ ¼å¼ä¸º BucketName-APPID æ­¤å¤„填写的存储桶名称必须为此格式
    bucketName: ruoyi-1250000000
    isHttps: false
    # åœ°åŸŸåå‚考官方文档
    # https://cloud.tencent.com/document/product/436/6224
    region: ap-beijing
ruoyi-common/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.5.2</version>
        <version>2.6.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/IServicePlus.java
@@ -1,6 +1,6 @@
package com.ruoyi.common.core.mybatisplus.core;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
@@ -11,7 +11,6 @@
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
 * è‡ªå®šä¹‰ Service æŽ¥å£, å®žçް æ•°æ®åº“实体与 vo å¯¹è±¡è½¬æ¢è¿”回
@@ -19,225 +18,153 @@
 * @author Lion Li
 * @since 2021-05-13
 */
public interface IServicePlus<T> extends IService<T> {
public interface IServicePlus<T, K> extends IService<T> {
    /**
     * æ ¹æ® ID æŸ¥è¯¢
     *
     * @param kClass vo类型
     * @param id     ä¸»é”®ID
     */
    default <K> K getVoById(Serializable id, Class<K> kClass) {
        T t = getBaseMapper().selectById(id);
        return BeanUtil.toBean(t, kClass);
    }
    /**
     * @param id          ä¸»é”®id
     * @param copyOptions copy条件
     * @return K对象
     */
    K getVoById(Serializable id, CopyOptions copyOptions);
    /**
     * æ ¹æ® ID æŸ¥è¯¢
     *
     * @param id        ä¸»é”®ID
     * @param convertor è½¬æ¢å‡½æ•°
     * @param <K>       vo类型
     */
    default <K> K getVoById(Serializable id, Function<T, K> convertor) {
        T t = getBaseMapper().selectById(id);
        return convertor.apply(t);
    }
    /**
     * æŸ¥è¯¢ï¼ˆæ ¹æ®ID æ‰¹é‡æŸ¥è¯¢ï¼‰
     *
     * @param kClass vo类型
     * @param idList ä¸»é”®ID列表
     */
    default <K> List<K> listVoByIds(Collection<? extends Serializable> idList, Class<K> kClass) {
        List<T> list = getBaseMapper().selectBatchIds(idList);
        if (list == null) {
            return null;
        }
        return list.stream()
                .map(any -> BeanUtil.toBean(any, kClass))
                .collect(Collectors.toList());
    }
    /**
     * æŸ¥è¯¢ï¼ˆæ ¹æ®ID æ‰¹é‡æŸ¥è¯¢ï¼‰
     *
     * @param convertor è½¬æ¢å‡½æ•°
     * @param idList    ä¸»é”®ID列表
     */
    default <K> List<K> listVoByIds(Collection<? extends Serializable> idList,
                                    Function<Collection<T>, List<K>> convertor) {
        List<T> list = getBaseMapper().selectBatchIds(idList);
        if (list == null) {
            return null;
        }
        return convertor.apply(list);
    }
    /**
     * æŸ¥è¯¢ï¼ˆæ ¹æ® columnMap æ¡ä»¶ï¼‰
     *
     * @param kClass    vo类型
     * @param columnMap è¡¨å­—段 map å¯¹è±¡
     */
    default <K> List<K> listVoByMap(Map<String, Object> columnMap, Class<K> kClass) {
        List<T> list = getBaseMapper().selectByMap(columnMap);
        if (list == null) {
            return null;
        }
        return list.stream()
                .map(any -> BeanUtil.toBean(any, kClass))
                .collect(Collectors.toList());
    }
    /**
     * æŸ¥è¯¢ï¼ˆæ ¹æ® columnMap æ¡ä»¶ï¼‰
     *
     * @param convertor è½¬æ¢å‡½æ•°
     * @param columnMap è¡¨å­—段 map å¯¹è±¡
     */
    default <K> List<K> listVoByMap(Map<String, Object> columnMap,
                                    Function<Collection<T>, List<K>> convertor) {
        List<T> list = getBaseMapper().selectByMap(columnMap);
        if (list == null) {
            return null;
        }
        return convertor.apply(list);
    }
    /**
     * æ ¹æ® Wrapper,查询一条记录 <br/>
     * <p>结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")</p>
     *
     * @param kClass       vo类型
     * @param queryWrapper å®žä½“对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default <K> K getVoOne(Wrapper<T> queryWrapper, Class<K> kClass) {
        return BeanUtil.toBean(getOne(queryWrapper, true), kClass);
    }
    /**
     * æ ¹æ® Wrapper,查询一条记录 <br/>
     * <p>结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")</p>
     *
     * @param convertor    è½¬æ¢å‡½æ•°
     * @param queryWrapper å®žä½“对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default <K> K getVoOne(Wrapper<T> queryWrapper, Function<T, K> convertor) {
        return convertor.apply(getOne(queryWrapper, true));
    }
    /**
     * æŸ¥è¯¢åˆ—表
     *
     * @param kClass       vo类型
     * @param queryWrapper å®žä½“对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default <K> List<K> listVo(Wrapper<T> queryWrapper, Class<K> kClass) {
        List<T> list = getBaseMapper().selectList(queryWrapper);
        if (list == null) {
            return null;
        }
        return list.stream()
                .map(any -> BeanUtil.toBean(any, kClass))
                .collect(Collectors.toList());
    }
    /**
     * æŸ¥è¯¢åˆ—表
     *
     * @param convertor    è½¬æ¢å‡½æ•°
     * @param queryWrapper å®žä½“对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default <K> List<K> listVo(Wrapper<T> queryWrapper, Function<Collection<T>, List<K>> convertor) {
        List<T> list = getBaseMapper().selectList(queryWrapper);
        if (list == null) {
            return null;
        }
        return convertor.apply(list);
    }
    /**
     * æŸ¥è¯¢æ‰€æœ‰
     *
     * @param kClass vo类型
     * @see Wrappers#emptyWrapper()
     */
    default <K> List<K> listVo(Class<K> kClass) {
        return listVo(Wrappers.emptyWrapper(), kClass);
    }
    /**
     * æŸ¥è¯¢æ‰€æœ‰
     *
     * @param convertor è½¬æ¢å‡½æ•°
     * @see Wrappers#emptyWrapper()
     */
    default <K> List<K> listVo(Function<Collection<T>, List<K>> convertor) {
        return listVo(Wrappers.emptyWrapper(), convertor);
    }
    /**
     * ç¿»é¡µæŸ¥è¯¢
     *
     * @param page         ç¿»é¡µå¯¹è±¡
     * @param queryWrapper å®žä½“对象封装操作类
     */
    default <K> PagePlus<T, K> pageVo(PagePlus<T, K> page, Wrapper<T> queryWrapper, Class<K> kClass) {
        PagePlus<T, K> result = getBaseMapper().selectPage(page, queryWrapper);
        List<K> volist = result.getRecords().stream()
                .map(any -> BeanUtil.toBean(any, kClass))
                .collect(Collectors.toList());
        result.setRecordsVo(volist);
        return result;
    }
    /**
     * ç¿»é¡µæŸ¥è¯¢
     *
     * @param page         ç¿»é¡µå¯¹è±¡
     * @param queryWrapper å®žä½“对象封装操作类
     * @param convertor    è½¬æ¢å‡½æ•°
     */
    default <K> PagePlus<T, K> pageVo(PagePlus<T, K> page, Wrapper<T> queryWrapper,
                                      Function<Collection<T>, List<K>> convertor) {
        PagePlus<T, K> result = getBaseMapper().selectPage(page, queryWrapper);
        return result.setRecordsVo(convertor.apply(result.getRecords()));
    }
    /**
     * æ— æ¡ä»¶ç¿»é¡µæŸ¥è¯¢
     *
     * @param page   ç¿»é¡µå¯¹è±¡
     */
    default <K> PagePlus<T, K> pageVo(PagePlus<T, K> page, Class<K> kClass) {
        return pageVo(page, Wrappers.emptyWrapper(), kClass);
    }
    /**
     * æ— æ¡ä»¶ç¿»é¡µæŸ¥è¯¢
     *
     * @param page      ç¿»é¡µå¯¹è±¡
     * @param convertor è½¬æ¢å‡½æ•°
     */
    default <K> PagePlus<T, K> pageVo(PagePlus<T, K> page, Function<Collection<T>, List<K>> convertor) {
        return pageVo(page, Wrappers.emptyWrapper(), convertor);
    }
    @Override
    default boolean saveBatch(Collection<T> entityList) {
        return saveBatch(entityList, DEFAULT_BATCH_SIZE);
    default K getVoById(Serializable id) {
        return getVoById(id, new CopyOptions());
    }
    @Override
    default boolean saveOrUpdateBatch(Collection<T> entityList) {
        return saveOrUpdateBatch(entityList, DEFAULT_BATCH_SIZE);
    /**
     * @param convertor è‡ªå®šä¹‰è½¬æ¢å™¨
     */
    default K getVoById(Serializable id, Function<T, K> convertor) {
        return convertor.apply(getById(id));
    }
    @Override
    default boolean updateBatchById(Collection<T> entityList) {
        return updateBatchById(entityList, DEFAULT_BATCH_SIZE);
    /**
     * @param idList      id列表
     * @param copyOptions copy条件
     * @return K对象
     */
    List<K> listVoByIds(Collection<? extends Serializable> idList, CopyOptions copyOptions);
    default List<K> listVoByIds(Collection<? extends Serializable> idList) {
        return listVoByIds(idList, new CopyOptions());
    }
    /**
     * @param convertor è‡ªå®šä¹‰è½¬æ¢å™¨
     */
    default List<K> listVoByIds(Collection<? extends Serializable> idList,
                                Function<Collection<T>, List<K>> convertor) {
        List<T> list = getBaseMapper().selectBatchIds(idList);
        if (list == null) {
            return null;
        }
        return convertor.apply(list);
    }
    /**
     * @param columnMap   è¡¨å­—段 map å¯¹è±¡
     * @param copyOptions copy条件
     * @return K对象
     */
    List<K> listVoByMap(Map<String, Object> columnMap, CopyOptions copyOptions);
    default List<K> listVoByMap(Map<String, Object> columnMap) {
        return listVoByMap(columnMap, new CopyOptions());
    }
    /**
     * @param convertor è‡ªå®šä¹‰è½¬æ¢å™¨
     */
    default List<K> listVoByMap(Map<String, Object> columnMap,
                                Function<Collection<T>, List<K>> convertor) {
        List<T> list = getBaseMapper().selectByMap(columnMap);
        if (list == null) {
            return null;
        }
        return convertor.apply(list);
    }
    /**
     * @param queryWrapper æŸ¥è¯¢æ¡ä»¶
     * @param copyOptions  copy条件
     * @return K对象
     */
    K getVoOne(Wrapper<T> queryWrapper, CopyOptions copyOptions);
    default K getVoOne(Wrapper<T> queryWrapper) {
        return getVoOne(queryWrapper, new CopyOptions());
    }
    /**
     * @param convertor è‡ªå®šä¹‰è½¬æ¢å™¨
     */
    default K getVoOne(Wrapper<T> queryWrapper, Function<T, K> convertor) {
        return convertor.apply(getOne(queryWrapper, true));
    }
    /**
     * @param queryWrapper æŸ¥è¯¢æ¡ä»¶
     * @param copyOptions  copy条件
     * @return K对象
     */
    List<K> listVo(Wrapper<T> queryWrapper, CopyOptions copyOptions);
    default List<K> listVo(Wrapper<T> queryWrapper) {
        return listVo(queryWrapper, new CopyOptions());
    }
    /**
     * @param convertor è‡ªå®šä¹‰è½¬æ¢å™¨
     */
    default List<K> listVo(Wrapper<T> queryWrapper, Function<Collection<T>, List<K>> convertor) {
        List<T> list = getBaseMapper().selectList(queryWrapper);
        if (list == null) {
            return null;
        }
        return convertor.apply(list);
    }
    default List<K> listVo() {
        return listVo(Wrappers.emptyWrapper());
    }
    /**
     * @param convertor è‡ªå®šä¹‰è½¬æ¢å™¨
     */
    default List<K> listVo(Function<Collection<T>, List<K>> convertor) {
        return listVo(Wrappers.emptyWrapper(), convertor);
    }
    /**
     * @param page         åˆ†é¡µå¯¹è±¡
     * @param queryWrapper æŸ¥è¯¢æ¡ä»¶
     * @param copyOptions  copy条件
     * @return K对象
     */
    PagePlus<T, K> pageVo(PagePlus<T, K> page, Wrapper<T> queryWrapper, CopyOptions copyOptions);
    default PagePlus<T, K> pageVo(PagePlus<T, K> page, Wrapper<T> queryWrapper) {
        return pageVo(page, queryWrapper, new CopyOptions());
    }
    /**
     * @param convertor è‡ªå®šä¹‰è½¬æ¢å™¨
     */
    default PagePlus<T, K> pageVo(PagePlus<T, K> page, Wrapper<T> queryWrapper,
                                  Function<Collection<T>, List<K>> convertor) {
        PagePlus<T, K> result = getBaseMapper().selectPage(page, queryWrapper);
        return result.setRecordsVo(convertor.apply(result.getRecords()));
    }
    default PagePlus<T, K> pageVo(PagePlus<T, K> page) {
        return pageVo(page, Wrappers.emptyWrapper());
    }
    /**
     * @param convertor è‡ªå®šä¹‰è½¬æ¢å™¨
     */
    default PagePlus<T, K> pageVo(PagePlus<T, K> page, Function<Collection<T>, List<K>> convertor) {
        return pageVo(page, Wrappers.emptyWrapper(), convertor);
    }
    boolean saveAll(Collection<T> entityList);
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/ServicePlusImpl.java
@@ -1,12 +1,19 @@
package com.ruoyi.common.core.mybatisplus.core;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.ClassUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.page.PagePlus;
import com.ruoyi.common.utils.BeanCopyUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ResolvableType;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
 * IServicePlus å®žçŽ°ç±»
@@ -15,7 +22,7 @@
 */
@Slf4j
@SuppressWarnings("unchecked")
public class ServicePlusImpl<M extends BaseMapperPlus<T>, T> extends ServiceImpl<M, T> implements IServicePlus<T> {
public class ServicePlusImpl<M extends BaseMapperPlus<T>, T, K> extends ServiceImpl<M, T> implements IServicePlus<T, K> {
    @Autowired
    protected M baseMapper;
@@ -35,6 +42,12 @@
    protected Class<T> mapperClass = currentMapperClass();
    protected Class<K> voClass = currentVoClass();
    public Class<K> getVoClass() {
        return voClass;
    }
    @Override
    protected Class<T> currentMapperClass() {
        return (Class<T>) this.getResolvableType().as(ServicePlusImpl.class).getGeneric(0).getType();
@@ -43,6 +56,10 @@
    @Override
    protected Class<T> currentModelClass() {
        return (Class<T>) this.getResolvableType().as(ServicePlusImpl.class).getGeneric(1).getType();
    }
    protected Class<K> currentVoClass() {
        return (Class<K>) this.getResolvableType().as(ServicePlusImpl.class).getGeneric(2).getType();
    }
    @Override
@@ -81,8 +98,106 @@
     * é€‚用于无脑插入
     */
    @Override
    public boolean saveBatch(Collection<T> entityList) {
        return saveBatch(entityList, DEFAULT_BATCH_SIZE);
    }
    @Override
    public boolean saveOrUpdateBatch(Collection<T> entityList) {
        return saveOrUpdateBatch(entityList, DEFAULT_BATCH_SIZE);
    }
    @Override
    public boolean updateBatchById(Collection<T> entityList) {
        return updateBatchById(entityList, DEFAULT_BATCH_SIZE);
    }
    /**
     * å•sql批量插入( å…¨é‡å¡«å…… æ— è§†æ•°æ®åº“默认值 )
     * é€‚用于无脑插入
     */
    @Override
    public boolean saveAll(Collection<T> entityList) {
        return baseMapper.insertAll(entityList) == entityList.size();
    }
    /**
     * æ ¹æ® ID æŸ¥è¯¢
     *
     * @param id ä¸»é”®ID
     */
    @Override
    public K getVoById(Serializable id, CopyOptions copyOptions) {
        T t = getBaseMapper().selectById(id);
        return BeanCopyUtils.oneCopy(t, copyOptions, voClass);
    }
    /**
     * æŸ¥è¯¢ï¼ˆæ ¹æ®ID æ‰¹é‡æŸ¥è¯¢ï¼‰
     *
     * @param idList ä¸»é”®ID列表
     */
    @Override
    public List<K> listVoByIds(Collection<? extends Serializable> idList, CopyOptions copyOptions) {
        List<T> list = getBaseMapper().selectBatchIds(idList);
        if (list == null) {
            return null;
        }
        return BeanCopyUtils.listCopy(list, copyOptions, voClass);
    }
    /**
     * æŸ¥è¯¢ï¼ˆæ ¹æ® columnMap æ¡ä»¶ï¼‰
     *
     * @param columnMap è¡¨å­—段 map å¯¹è±¡
     */
    @Override
    public List<K> listVoByMap(Map<String, Object> columnMap, CopyOptions copyOptions) {
        List<T> list = getBaseMapper().selectByMap(columnMap);
        if (list == null) {
            return null;
        }
        return BeanCopyUtils.listCopy(list, copyOptions, voClass);
    }
    /**
     * æ ¹æ® Wrapper,查询一条记录 <br/>
     * <p>结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")</p>
     *
     * @param queryWrapper å®žä½“对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    @Override
    public K getVoOne(Wrapper<T> queryWrapper, CopyOptions copyOptions) {
        T t = getOne(queryWrapper, true);
        return BeanCopyUtils.oneCopy(t, copyOptions, voClass);
    }
    /**
     * æŸ¥è¯¢åˆ—表
     *
     * @param queryWrapper å®žä½“对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    @Override
    public List<K> listVo(Wrapper<T> queryWrapper, CopyOptions copyOptions) {
        List<T> list = getBaseMapper().selectList(queryWrapper);
        if (list == null) {
            return null;
        }
        return BeanCopyUtils.listCopy(list, copyOptions, voClass);
    }
    /**
     * ç¿»é¡µæŸ¥è¯¢
     *
     * @param page         ç¿»é¡µå¯¹è±¡
     * @param queryWrapper å®žä½“对象封装操作类
     */
    @Override
    public PagePlus<T, K> pageVo(PagePlus<T, K> page, Wrapper<T> queryWrapper, CopyOptions copyOptions) {
        PagePlus<T, K> result = getBaseMapper().selectPage(page, queryWrapper);
        List<K> volist = BeanCopyUtils.listCopy(result.getRecords(), copyOptions, voClass);
        result.setRecordsVo(volist);
        return result;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/validate/AddGroup.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
package com.ruoyi.common.core.validate;
/**
 * æ ¡éªŒåˆ†ç»„ add
 *
 * @author Lion Li
 */
public interface AddGroup {
}
ruoyi-common/src/main/java/com/ruoyi/common/core/validate/EditGroup.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
package com.ruoyi.common.core.validate;
/**
 * æ ¡éªŒåˆ†ç»„ edit
 *
 * @author Lion Li
 */
public interface EditGroup {
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/BeanCopyUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package com.ruoyi.common.utils;
import cn.hutool.core.bean.copier.BeanCopier;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.ReflectUtil;
import java.util.List;
import java.util.stream.Collectors;
/**
 * bean深拷贝工具
 *
 * @author Lion Li
 */
public class BeanCopyUtils {
    /**
     * å•对象基于class创建拷贝
     *
     * @param source      æ•°æ®æ¥æºå®žä½“
     * @param copyOptions copy条件
     * @param desc        æè¿°å¯¹è±¡ è½¬æ¢åŽçš„对象
     * @return desc
     */
    public static <T, V> V oneCopy(T source, CopyOptions copyOptions, Class<V> desc) {
        V v = ReflectUtil.newInstanceIfPossible(desc);
        return oneCopy(source, copyOptions, v);
    }
    /**
     * å•对象基于对象创建拷贝
     *
     * @param source      æ•°æ®æ¥æºå®žä½“
     * @param copyOptions copy条件
     * @param desc        è½¬æ¢åŽçš„对象
     * @return desc
     */
    public static <T, V> V oneCopy(T source, CopyOptions copyOptions, V desc) {
        return BeanCopier.create(source, desc, copyOptions).copy();
    }
    /**
     * åˆ—表对象基于class创建拷贝
     *
     * @param sourceList  æ•°æ®æ¥æºå®žä½“列表
     * @param copyOptions copy条件
     * @param desc        æè¿°å¯¹è±¡ è½¬æ¢åŽçš„对象
     * @return desc
     */
    public static <T, V> List<V> listCopy(List<T> sourceList, CopyOptions copyOptions, Class<V> desc) {
        return sourceList.stream()
            .map(source -> oneCopy(source, copyOptions, desc))
            .collect(Collectors.toList());
    }
}
ruoyi-demo/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.5.2</version>
        <version>2.6.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoAddBo.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoEditBo.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoQueryBo.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestTreeAddBo.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestTreeEditBo.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestTreeQueryBo.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
@@ -1,16 +1,17 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.demo.bo.TestDemoAddBo;
import com.ruoyi.demo.bo.TestDemoEditBo;
import com.ruoyi.demo.bo.TestDemoQueryBo;
import com.ruoyi.demo.domain.bo.TestDemoBo;
import com.ruoyi.demo.domain.vo.TestDemoVo;
import com.ruoyi.demo.service.ITestDemoService;
import com.ruoyi.demo.vo.TestDemoVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
@@ -28,8 +29,9 @@
 * æµ‹è¯•单表Controller
 *
 * @author Lion Li
 * @date 2021-05-30
 * @date 2021-07-26
 */
@Validated
@Api(value = "测试单表控制器", tags = {"测试单表管理"})
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@RestController
@@ -44,18 +46,28 @@
    @ApiOperation("查询测试单表列表")
    @PreAuthorize("@ss.hasPermi('demo:demo:list')")
    @GetMapping("/list")
    public TableDataInfo<TestDemoVo> list(@Validated TestDemoQueryBo bo) {
    public TableDataInfo<TestDemoVo> list(@Validated TestDemoBo bo) {
        return iTestDemoService.queryPageList(bo);
    }
    /**
    /**
     * è‡ªå®šä¹‰åˆ†é¡µæŸ¥è¯¢
     */
    @ApiOperation("自定义分页查询")
    @PreAuthorize("@ss.hasPermi('demo:demo:list')")
    @GetMapping("/page")
    public TableDataInfo<TestDemoVo> page(@Validated TestDemoBo bo) {
        return iTestDemoService.customPageList(bo);
    }
    /**
     * å¯¼å‡ºæµ‹è¯•单表列表
     */
    @ApiOperation("导出测试单表列表")
    @PreAuthorize("@ss.hasPermi('demo:demo:export')")
    @Log(title = "测试单表", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult<TestDemoVo> export(@Validated TestDemoQueryBo bo) {
    public AjaxResult<TestDemoVo> export(@Validated TestDemoBo bo) {
        List<TestDemoVo> list = iTestDemoService.queryList(bo);
        ExcelUtil<TestDemoVo> util = new ExcelUtil<TestDemoVo>(TestDemoVo.class);
        return util.exportExcel(list, "测试单表");
@@ -78,9 +90,10 @@
    @ApiOperation("新增测试单表")
    @PreAuthorize("@ss.hasPermi('demo:demo:add')")
    @Log(title = "测试单表", businessType = BusinessType.INSERT)
    @RepeatSubmit
    @PostMapping()
    public AjaxResult<Void> add(@Validated @RequestBody TestDemoAddBo bo) {
        return toAjax(iTestDemoService.insertByAddBo(bo) ? 1 : 0);
    public AjaxResult<Void> add(@Validated(AddGroup.class) @RequestBody TestDemoBo bo) {
        return toAjax(iTestDemoService.insertByBo(bo) ? 1 : 0);
    }
    /**
@@ -89,9 +102,10 @@
    @ApiOperation("修改测试单表")
    @PreAuthorize("@ss.hasPermi('demo:demo:edit')")
    @Log(title = "测试单表", businessType = BusinessType.UPDATE)
    @RepeatSubmit
    @PutMapping()
    public AjaxResult<Void> edit(@Validated @RequestBody TestDemoEditBo bo) {
        return toAjax(iTestDemoService.updateByEditBo(bo) ? 1 : 0);
    public AjaxResult<Void> edit(@Validated(EditGroup.class) @RequestBody TestDemoBo bo) {
        return toAjax(iTestDemoService.updateByBo(bo) ? 1 : 0);
    }
    /**
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java
@@ -1,15 +1,16 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.demo.bo.TestTreeAddBo;
import com.ruoyi.demo.bo.TestTreeEditBo;
import com.ruoyi.demo.bo.TestTreeQueryBo;
import com.ruoyi.demo.domain.bo.TestTreeBo;
import com.ruoyi.demo.domain.vo.TestTreeVo;
import com.ruoyi.demo.service.ITestTreeService;
import com.ruoyi.demo.vo.TestTreeVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
@@ -27,8 +28,9 @@
 * æµ‹è¯•树表Controller
 *
 * @author Lion Li
 * @date 2021-05-30
 * @date 2021-07-26
 */
@Validated
@Api(value = "测试树表控制器", tags = {"测试树表管理"})
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@RestController
@@ -43,8 +45,9 @@
    @ApiOperation("查询测试树表列表")
    @PreAuthorize("@ss.hasPermi('demo:tree:list')")
    @GetMapping("/list")
    public AjaxResult<List<TestTreeVo>> list(@Validated TestTreeQueryBo bo) {
        return AjaxResult.success(iTestTreeService.queryList(bo));
    public AjaxResult<List<TestTreeVo>> list(@Validated TestTreeBo bo) {
        List<TestTreeVo> list = iTestTreeService.queryList(bo);
        return AjaxResult.success(list);
    }
    /**
@@ -54,7 +57,7 @@
    @PreAuthorize("@ss.hasPermi('demo:tree:export')")
    @Log(title = "测试树表", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult<TestTreeVo> export(@Validated TestTreeQueryBo bo) {
    public AjaxResult<TestTreeVo> export(@Validated TestTreeBo bo) {
        List<TestTreeVo> list = iTestTreeService.queryList(bo);
        ExcelUtil<TestTreeVo> util = new ExcelUtil<TestTreeVo>(TestTreeVo.class);
        return util.exportExcel(list, "测试树表");
@@ -77,9 +80,10 @@
    @ApiOperation("新增测试树表")
    @PreAuthorize("@ss.hasPermi('demo:tree:add')")
    @Log(title = "测试树表", businessType = BusinessType.INSERT)
    @RepeatSubmit
    @PostMapping()
    public AjaxResult<Void> add(@Validated @RequestBody TestTreeAddBo bo) {
        return toAjax(iTestTreeService.insertByAddBo(bo) ? 1 : 0);
    public AjaxResult<Void> add(@Validated(AddGroup.class) @RequestBody TestTreeBo bo) {
        return toAjax(iTestTreeService.insertByBo(bo) ? 1 : 0);
    }
    /**
@@ -88,9 +92,10 @@
    @ApiOperation("修改测试树表")
    @PreAuthorize("@ss.hasPermi('demo:tree:edit')")
    @Log(title = "测试树表", businessType = BusinessType.UPDATE)
    @RepeatSubmit
    @PutMapping()
    public AjaxResult<Void> edit(@Validated @RequestBody TestTreeEditBo bo) {
        return toAjax(iTestTreeService.updateByEditBo(bo) ? 1 : 0);
    public AjaxResult<Void> edit(@Validated(EditGroup.class) @RequestBody TestTreeBo bo) {
        return toAjax(iTestTreeService.updateByBo(bo) ? 1 : 0);
    }
    /**
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java
@@ -14,7 +14,7 @@
 * æµ‹è¯•单表对象 test_demo
 *
 * @author Lion Li
 * @date 2021-05-30
 * @date 2021-07-26
 */
@Data
@NoArgsConstructor
@@ -32,43 +32,66 @@
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    /** éƒ¨é—¨id */
    /**
     * éƒ¨é—¨id
     */
    private Long deptId;
    /** ç”¨æˆ·id */
    /**
     * ç”¨æˆ·id
     */
    private Long userId;
    /** æŽ’序号 */
    @OrderBy(isDesc = false, sort = 1)
    /**
     * æŽ’序号
     */
    @OrderBy(isDesc = false, sort = 1)
    private Long orderNum;
    /** key键 */
    /**
     * key键
     */
    private String testKey;
    /** å€¼ */
    /**
     * å€¼
     */
    private String value;
    /** ç‰ˆæœ¬ */
    /**
     * ç‰ˆæœ¬
     */
    @Version
    private Long version;
    /** åˆ›å»ºæ—¶é—´ */
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    /** åˆ›å»ºäºº */
    /**
     * åˆ›å»ºäºº
     */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /** æ›´æ–°æ—¶é—´ */
    /**
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    /** æ›´æ–°äºº */
    /**
     * æ›´æ–°äºº
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /** åˆ é™¤æ ‡å¿— */
    /**
     * åˆ é™¤æ ‡å¿—
     */
    @TableLogic
    private Long delFlag;
}
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java
@@ -1,8 +1,6 @@
package com.ruoyi.demo.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@@ -14,7 +12,7 @@
 * æµ‹è¯•树表对象 test_tree
 *
 * @author Lion Li
 * @date 2021-05-30
 * @date 2021-07-26
 */
@Data
@NoArgsConstructor
@@ -25,44 +23,65 @@
    private static final long serialVersionUID=1L;
    /** ä¸»é”® */
    /**
     * ä¸»é”®
     */
    @TableId(value = "id")
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    /** çˆ¶id */
    /**
     * çˆ¶id
     */
    private Long parentId;
    /** éƒ¨é—¨id */
    /**
     * éƒ¨é—¨id
     */
    private Long deptId;
    /** ç”¨æˆ·id */
    /**
     * ç”¨æˆ·id
     */
    private Long userId;
    /** æ ‘节点名 */
    /**
     * æ ‘节点名
     */
    private String treeName;
    /** ç‰ˆæœ¬ */
    /**
     * ç‰ˆæœ¬
     */
    @Version
    private Long version;
    /** åˆ›å»ºæ—¶é—´ */
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    /** åˆ›å»ºäºº */
    /**
     * åˆ›å»ºäºº
     */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /** æ›´æ–°æ—¶é—´ */
    /**
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    /** æ›´æ–°äºº */
    /**
     * æ›´æ–°äºº
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /** åˆ é™¤æ ‡å¿— */
    /**
     * åˆ é™¤æ ‡å¿—
     */
    @TableLogic
    private Long delFlag;
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,94 @@
package com.ruoyi.demo.domain.bo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.*;
import java.util.Date;
import com.ruoyi.common.core.domain.BaseEntity;
/**
 * æµ‹è¯•单表业务对象 test_demo
 *
 * @author Lion Li
 * @date 2021-07-26
 */
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("测试单表业务对象")
public class TestDemoBo extends BaseEntity {
    /**
     * ä¸»é”®
     */
    @ApiModelProperty("主键")
    @NotNull(message = "主键不能为空", groups = { EditGroup.class })
    private Long id;
    /**
     * éƒ¨é—¨id
     */
    @ApiModelProperty("部门id")
    @NotNull(message = "部门id不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long deptId;
    /**
     * ç”¨æˆ·id
     */
    @ApiModelProperty("用户id")
    @NotNull(message = "用户id不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long userId;
    /**
     * æŽ’序号
     */
    @ApiModelProperty("排序号")
    @NotNull(message = "排序号不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long orderNum;
    /**
     * key键
     */
    @ApiModelProperty("key键")
    @NotBlank(message = "key键不能为空", groups = { AddGroup.class, EditGroup.class })
    private String testKey;
    /**
     * å€¼
     */
    @ApiModelProperty("值")
    @NotBlank(message = "值不能为空", groups = { AddGroup.class, EditGroup.class })
    private String value;
    /**
     * åˆ†é¡µå¤§å°
     */
    @ApiModelProperty("分页大小")
    private Integer pageSize;
    /**
     * å½“前页数
     */
    @ApiModelProperty("当前页数")
    private Integer pageNum;
    /**
     * æŽ’序列
     */
    @ApiModelProperty("排序列")
    private String orderByColumn;
    /**
     * æŽ’序的方向desc或者asc
     */
    @ApiModelProperty(value = "排序的方向", example = "asc,desc")
    private String isAsc;
}
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestTreeBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,79 @@
package com.ruoyi.demo.domain.bo;
import com.ruoyi.common.core.domain.TreeEntity;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
 * æµ‹è¯•树表业务对象 test_tree
 *
 * @author Lion Li
 * @date 2021-07-26
 */
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("测试树表业务对象")
public class TestTreeBo extends TreeEntity {
    /**
     * ä¸»é”®
     */
    @ApiModelProperty("主键")
    @NotNull(message = "主键不能为空", groups = { EditGroup.class })
    private Long id;
    /**
     * éƒ¨é—¨id
     */
    @ApiModelProperty("部门id")
    @NotNull(message = "部门id不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long deptId;
    /**
     * ç”¨æˆ·id
     */
    @ApiModelProperty("用户id")
    @NotNull(message = "用户id不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long userId;
    /**
     * æ ‘节点名
     */
    @ApiModelProperty("树节点名")
    @NotBlank(message = "树节点名不能为空", groups = { AddGroup.class, EditGroup.class })
    private String treeName;
    /**
     * åˆ†é¡µå¤§å°
     */
    @ApiModelProperty("分页大小")
    private Integer pageSize;
    /**
     * å½“前页数
     */
    @ApiModelProperty("当前页数")
    private Integer pageNum;
    /**
     * æŽ’序列
     */
    @ApiModelProperty("排序列")
    private String orderByColumn;
    /**
     * æŽ’序的方向desc或者asc
     */
    @ApiModelProperty(value = "排序的方向", example = "asc,desc")
    private String isAsc;
}
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java
ÎļþÃû´Ó ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestDemoVo.java ÐÞ¸Ä
@@ -1,12 +1,9 @@
package com.ruoyi.demo.vo;
package com.ruoyi.demo.domain.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ruoyi.common.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@@ -15,7 +12,7 @@
 * æµ‹è¯•单表视图对象 test_demo
 *
 * @author Lion Li
 * @date 2021-05-30
 * @date 2021-07-26
 */
@Data
@ApiModel("测试单表视图对象")
@@ -24,57 +21,70 @@
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®
     * å¦‚果是自定义id æˆ–者 é›ªèбid
     * éœ€è¦å¢žåŠ åºåˆ—åŒ–ä¸ºå­—ç¬¦ä¸²æ³¨è§£ å› ä¸ºLong到前端会失真
     */
     *  ä¸»é”®
     */
    @ApiModelProperty("主键")
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    /** éƒ¨é—¨id */
    /**
     * éƒ¨é—¨id
     */
    @Excel(name = "部门id")
    @ApiModelProperty("部门id")
    private Long deptId;
    /** ç”¨æˆ·id */
    /**
     * ç”¨æˆ·id
     */
    @Excel(name = "用户id")
    @ApiModelProperty("用户id")
    private Long userId;
    /** æŽ’序号 */
    /**
     * æŽ’序号
     */
    @Excel(name = "排序号")
    @ApiModelProperty("排序号")
    private Long orderNum;
    /** key键 */
    /**
     * key键
     */
    @Excel(name = "key键")
    @ApiModelProperty("key键")
    private String testKey;
    /** å€¼ */
    /**
     * å€¼
     */
    @Excel(name = "值")
    @ApiModelProperty("值")
    private String value;
    /** åˆ›å»ºæ—¶é—´ */
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @Excel(name = "创建时间" , width = 30, dateFormat = "yyyy-MM-dd")
//    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("创建时间")
    private Date createTime;
    /** åˆ›å»ºäºº */
    /**
     * åˆ›å»ºäºº
     */
    @Excel(name = "创建人")
    @ApiModelProperty("创建人")
    private String createBy;
    /** æ›´æ–°æ—¶é—´ */
    /**
     * æ›´æ–°æ—¶é—´
     */
    @Excel(name = "更新时间" , width = 30, dateFormat = "yyyy-MM-dd")
//    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("更新时间")
    private Date updateTime;
    /** æ›´æ–°äºº */
    /**
     * æ›´æ–°äºº
     */
    @Excel(name = "更新人")
    @ApiModelProperty("更新人")
    private String updateBy;
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestTreeVo.java
ÎļþÃû´Ó ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestTreeVo.java ÐÞ¸Ä
@@ -1,13 +1,9 @@
package com.ruoyi.demo.vo;
package com.ruoyi.demo.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ruoyi.common.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@@ -16,7 +12,7 @@
 * æµ‹è¯•树表视图对象 test_tree
 *
 * @author Lion Li
 * @date 2021-05-30
 * @date 2021-07-26
 */
@Data
@ApiModel("测试树表视图对象")
@@ -24,34 +20,44 @@
    private static final long serialVersionUID = 1L;
    /** ä¸»é”® */
    /**
     *  ä¸»é”®
     */
    @ApiModelProperty("主键")
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    /** çˆ¶id */
    /**
     * çˆ¶id
     */
    @Excel(name = "父id")
    @ApiModelProperty("父id")
    private Long parentId;
    /** éƒ¨é—¨id */
    /**
     * éƒ¨é—¨id
     */
    @Excel(name = "部门id")
    @ApiModelProperty("部门id")
    private Long deptId;
    /** ç”¨æˆ·id */
    /**
     * ç”¨æˆ·id
     */
    @Excel(name = "用户id")
    @ApiModelProperty("用户id")
    private Long userId;
    /** æ ‘节点名 */
    /**
     * æ ‘节点名
     */
    @Excel(name = "树节点名")
    @ApiModelProperty("树节点名")
    private String treeName;
    /** åˆ›å»ºæ—¶é—´ */
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @Excel(name = "创建时间" , width = 30, dateFormat = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("创建时间")
    private Date createTime;
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java
@@ -1,18 +1,24 @@
package com.ruoyi.demo.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.mybatisplus.cache.MybatisPlusRedisCache;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.demo.domain.vo.TestDemoVo;
import org.apache.ibatis.annotations.CacheNamespace;
import org.apache.ibatis.annotations.Param;
/**
 * æµ‹è¯•单表Mapper接口
 *
 * @author Lion Li
 * @date 2021-05-30
 * @date 2021-07-26
 */
// å¦‚使需切换数据源 è¯·å‹¿ä½¿ç”¨ç¼“å­˜ ä¼šé€ æˆæ•°æ®ä¸ä¸€è‡´çŽ°è±¡
@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
public interface TestDemoMapper extends BaseMapperPlus<TestDemo> {
    Page<TestDemoVo> customPageList(@Param("page") Page<TestDemo> page, @Param("ew") Wrapper<TestDemo> wrapper);
}
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java
@@ -7,9 +7,8 @@
 * æµ‹è¯•树表Mapper接口
 *
 * @author Lion Li
 * @date 2021-05-30
 * @date 2021-07-26
 */
//@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
public interface TestTreeMapper extends BaseMapperPlus<TestTree> {
}
ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java
@@ -1,10 +1,8 @@
package com.ruoyi.demo.service;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.demo.vo.TestDemoVo;
import com.ruoyi.demo.bo.TestDemoQueryBo;
import com.ruoyi.demo.bo.TestDemoAddBo;
import com.ruoyi.demo.bo.TestDemoEditBo;
import com.ruoyi.demo.domain.vo.TestDemoVo;
import com.ruoyi.demo.domain.bo.TestDemoBo;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
@@ -15,9 +13,9 @@
 * æµ‹è¯•单表Service接口
 *
 * @author Lion Li
 * @date 2021-05-30
 * @date 2021-07-26
 */
public interface ITestDemoService extends IServicePlus<TestDemo> {
public interface ITestDemoService extends IServicePlus<TestDemo, TestDemoVo> {
    /**
     * æŸ¥è¯¢å•个
@@ -28,25 +26,31 @@
    /**
     * æŸ¥è¯¢åˆ—表
     */
    TableDataInfo<TestDemoVo> queryPageList(TestDemoQueryBo bo);
    TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo);
    /**
     * è‡ªå®šä¹‰åˆ†é¡µæŸ¥è¯¢
     */
    TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo);
    /**
     * æŸ¥è¯¢åˆ—表
     */
    List<TestDemoVo> queryList(TestDemoQueryBo bo);
    List<TestDemoVo> queryList(TestDemoBo bo);
    /**
     * æ ¹æ®æ–°å¢žä¸šåŠ¡å¯¹è±¡æ’å…¥æµ‹è¯•å•è¡¨
     * @param bo æµ‹è¯•单表新增业务对象
     * @return
     */
    Boolean insertByAddBo(TestDemoAddBo bo);
    Boolean insertByBo(TestDemoBo bo);
    /**
     * æ ¹æ®ç¼–辑业务对象修改测试单表
     * @param bo æµ‹è¯•单表编辑业务对象
     * @return
     */
    Boolean updateByEditBo(TestDemoEditBo bo);
    Boolean updateByBo(TestDemoBo bo);
    /**
     * æ ¡éªŒå¹¶åˆ é™¤æ•°æ®
ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestTreeService.java
@@ -1,11 +1,9 @@
package com.ruoyi.demo.service;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.demo.bo.TestTreeAddBo;
import com.ruoyi.demo.bo.TestTreeEditBo;
import com.ruoyi.demo.bo.TestTreeQueryBo;
import com.ruoyi.demo.domain.TestTree;
import com.ruoyi.demo.vo.TestTreeVo;
import com.ruoyi.demo.domain.vo.TestTreeVo;
import com.ruoyi.demo.domain.bo.TestTreeBo;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import java.util.Collection;
import java.util.List;
@@ -14,9 +12,9 @@
 * æµ‹è¯•树表Service接口
 *
 * @author Lion Li
 * @date 2021-05-30
 * @date 2021-07-26
 */
public interface ITestTreeService extends IServicePlus<TestTree> {
public interface ITestTreeService extends IServicePlus<TestTree, TestTreeVo> {
    /**
     * æŸ¥è¯¢å•个
     * @return
@@ -26,21 +24,21 @@
    /**
     * æŸ¥è¯¢åˆ—表
     */
    List<TestTreeVo> queryList(TestTreeQueryBo bo);
    List<TestTreeVo> queryList(TestTreeBo bo);
    /**
     * æ ¹æ®æ–°å¢žä¸šåŠ¡å¯¹è±¡æ’å…¥æµ‹è¯•æ ‘è¡¨
     * @param bo æµ‹è¯•树表新增业务对象
     * @return
     */
    Boolean insertByAddBo(TestTreeAddBo bo);
    Boolean insertByBo(TestTreeBo bo);
    /**
     * æ ¹æ®ç¼–辑业务对象修改测试树表
     * @param bo æµ‹è¯•树表编辑业务对象
     * @return
     */
    Boolean updateByEditBo(TestTreeEditBo bo);
    Boolean updateByBo(TestTreeBo bo);
    /**
     * æ ¡éªŒå¹¶åˆ é™¤æ•°æ®
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java
@@ -4,18 +4,17 @@
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.PagePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.demo.bo.TestDemoAddBo;
import com.ruoyi.demo.bo.TestDemoEditBo;
import com.ruoyi.demo.bo.TestDemoQueryBo;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.demo.domain.bo.TestDemoBo;
import com.ruoyi.demo.domain.vo.TestDemoVo;
import com.ruoyi.demo.mapper.TestDemoMapper;
import com.ruoyi.demo.service.ITestDemoService;
import com.ruoyi.demo.vo.TestDemoVo;
import org.springframework.stereotype.Service;
import java.util.Collection;
@@ -26,30 +25,40 @@
 * æµ‹è¯•单表Service业务层处理
 *
 * @author Lion Li
 * @date 2021-05-30
 * @date 2021-07-26
 */
@Service
public class TestDemoServiceImpl extends ServicePlusImpl<TestDemoMapper, TestDemo> implements ITestDemoService {
public class TestDemoServiceImpl extends ServicePlusImpl<TestDemoMapper, TestDemo, TestDemoVo> implements ITestDemoService {
    @Override
    public TestDemoVo queryById(Long id) {
        return getVoById(id, TestDemoVo.class);
        return getVoById(id);
    }
    @DataScope(isUser = true)
    @Override
    public TableDataInfo<TestDemoVo> queryPageList(TestDemoQueryBo bo) {
        PagePlus<TestDemo, TestDemoVo> result = pageVo(PageUtils.buildPagePlus(), buildQueryWrapper(bo), TestDemoVo.class);
    public TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo) {
        PagePlus<TestDemo, TestDemoVo> result = pageVo(PageUtils.buildPagePlus(), buildQueryWrapper(bo));
        return PageUtils.buildDataInfo(result);
    }
    /**
     * è‡ªå®šä¹‰åˆ†é¡µæŸ¥è¯¢
     */
    @DataScope(isUser = true)
    @Override
    public TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo) {
        Page<TestDemoVo> result = baseMapper.customPageList(PageUtils.buildPage(), buildQueryWrapper(bo));
        return PageUtils.buildDataInfo(result);
    }
    @DataScope(isUser = true)
    @Override
    public List<TestDemoVo> queryList(TestDemoQueryBo bo) {
        return listVo(buildQueryWrapper(bo), TestDemoVo.class);
    public List<TestDemoVo> queryList(TestDemoBo bo) {
        return listVo(buildQueryWrapper(bo));
    }
    private LambdaQueryWrapper<TestDemo> buildQueryWrapper(TestDemoQueryBo bo) {
    private LambdaQueryWrapper<TestDemo> buildQueryWrapper(TestDemoBo bo) {
        Map<String, Object> params = bo.getParams();
        Object dataScope = params.get("dataScope");
        LambdaQueryWrapper<TestDemo> lqw = Wrappers.lambdaQuery();
@@ -63,14 +72,14 @@
    }
    @Override
    public Boolean insertByAddBo(TestDemoAddBo bo) {
    public Boolean insertByBo(TestDemoBo bo) {
        TestDemo add = BeanUtil.toBean(bo, TestDemo.class);
        validEntityBeforeSave(add);
        return save(add);
    }
    @Override
    public Boolean updateByEditBo(TestDemoEditBo bo) {
    public Boolean updateByBo(TestDemoBo bo) {
        TestDemo update = BeanUtil.toBean(bo, TestDemo.class);
        validEntityBeforeSave(update);
        return updateById(update);
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java
@@ -6,13 +6,11 @@
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.demo.bo.TestTreeAddBo;
import com.ruoyi.demo.bo.TestTreeEditBo;
import com.ruoyi.demo.bo.TestTreeQueryBo;
import com.ruoyi.demo.domain.TestTree;
import com.ruoyi.demo.domain.bo.TestTreeBo;
import com.ruoyi.demo.domain.vo.TestTreeVo;
import com.ruoyi.demo.mapper.TestTreeMapper;
import com.ruoyi.demo.service.ITestTreeService;
import com.ruoyi.demo.vo.TestTreeVo;
import org.springframework.stereotype.Service;
import java.util.Collection;
@@ -23,24 +21,24 @@
 * æµ‹è¯•树表Service业务层处理
 *
 * @author Lion Li
 * @date 2021-05-30
 * @date 2021-07-26
 */
@Service
public class TestTreeServiceImpl extends ServicePlusImpl<TestTreeMapper, TestTree> implements ITestTreeService {
public class TestTreeServiceImpl extends ServicePlusImpl<TestTreeMapper, TestTree, TestTreeVo> implements ITestTreeService {
    @Override
    public TestTreeVo queryById(Long id) {
        return getVoById(id, TestTreeVo.class);
        return getVoById(id);
    }
//    @DataSource(DataSourceType.SLAVE) // åˆ‡æ¢ä»Žåº“查询
    @DataScope(isUser = true)
    @Override
    public List<TestTreeVo> queryList(TestTreeQueryBo bo) {
        return listVo(buildQueryWrapper(bo), TestTreeVo.class);
    public List<TestTreeVo> queryList(TestTreeBo bo) {
        return listVo(buildQueryWrapper(bo));
    }
    private LambdaQueryWrapper<TestTree> buildQueryWrapper(TestTreeQueryBo bo) {
    private LambdaQueryWrapper<TestTree> buildQueryWrapper(TestTreeBo bo) {
        Map<String, Object> params = bo.getParams();
        Object dataScope = params.get("dataScope");
        LambdaQueryWrapper<TestTree> lqw = Wrappers.lambdaQuery();
@@ -53,14 +51,14 @@
    }
    @Override
    public Boolean insertByAddBo(TestTreeAddBo bo) {
    public Boolean insertByBo(TestTreeBo bo) {
        TestTree add = BeanUtil.toBean(bo, TestTree.class);
        validEntityBeforeSave(add);
        return save(add);
    }
    @Override
    public Boolean updateByEditBo(TestTreeEditBo bo) {
    public Boolean updateByBo(TestTreeBo bo) {
        TestTree update = BeanUtil.toBean(bo, TestTree.class);
        validEntityBeforeSave(update);
        return updateById(update);
ruoyi-demo/src/main/resources/mapper/demo/TestDemoMapper.xml
@@ -18,6 +18,9 @@
        <result property="updateBy" column="update_by"/>
        <result property="delFlag" column="del_flag"/>
    </resultMap>
    <select id="customPageList" resultType="com.ruoyi.demo.domain.vo.TestDemoVo">
        SELECT * FROM test_demo ${ew.customSqlSegment}
    </select>
</mapper>
</mapper>
ruoyi-extend/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.5.2</version>
        <version>2.6.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-extend</artifactId>
ruoyi-extend/ruoyi-monitor-admin/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-extend</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.5.2</version>
        <version>2.6.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
ruoyi-framework/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.5.2</version>
        <version>2.6.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java
@@ -65,7 +65,7 @@
        // éªŒè¯ç å¼€å…³
        if (captchaOnOff)
        {
            validateCapcha(username, code, uuid, request);
            validateCaptcha(username, code, uuid, request);
        }
        // ç”¨æˆ·éªŒè¯
        Authentication authentication = null;
@@ -103,7 +103,7 @@
     * @param uuid å”¯ä¸€æ ‡è¯†
     * @return ç»“æžœ
     */
    public void validateCapcha(String username, String code, String uuid, HttpServletRequest request) {
    public void validateCaptcha(String username, String code, String uuid, HttpServletRequest request) {
        String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
        String captcha = redisCache.getCacheObject(verifyKey);
        redisCache.deleteObject(verifyKey);
ruoyi-generator/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.5.2</version>
        <version>2.6.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java
@@ -1,7 +1,7 @@
package com.ruoyi.generator.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.generator.domain.GenTableColumn;
import com.ruoyi.generator.mapper.GenTableColumnMapper;
import org.springframework.stereotype.Service;
@@ -15,7 +15,7 @@
 * @author ruoyi
 */
@Service
public class GenTableColumnServiceImpl extends ServicePlusImpl<GenTableColumnMapper, GenTableColumn> implements IGenTableColumnService {
public class GenTableColumnServiceImpl extends ServiceImpl<GenTableColumnMapper, GenTableColumn> implements IGenTableColumnService {
    /**
     * æŸ¥è¯¢ä¸šåŠ¡å­—æ®µåˆ—è¡¨
ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java
@@ -6,9 +6,9 @@
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.JsonUtils;
@@ -35,7 +35,10 @@
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -47,7 +50,7 @@
 */
@Slf4j
@Service
public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTable> implements IGenTableService {
public class GenTableServiceImpl extends ServiceImpl<GenTableMapper, GenTable> implements IGenTableService {
    @Autowired
    private GenTableColumnMapper genTableColumnMapper;
ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java
@@ -1,6 +1,6 @@
package com.ruoyi.generator.service;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.generator.domain.GenTableColumn;
import java.util.List;
@@ -10,7 +10,7 @@
 *
 * @author ruoyi
 */
public interface IGenTableColumnService extends IServicePlus<GenTableColumn> {
public interface IGenTableColumnService extends IService<GenTableColumn> {
    /**
     * æŸ¥è¯¢ä¸šåŠ¡å­—æ®µåˆ—è¡¨
     *
ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java
@@ -1,6 +1,6 @@
package com.ruoyi.generator.service;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.generator.domain.GenTable;
@@ -12,7 +12,7 @@
 *
 * @author ruoyi
 */
public interface IGenTableService extends IServicePlus<GenTable> {
public interface IGenTableService extends IService<GenTable> {
    TableDataInfo<GenTable> selectPageGenTableList(GenTable genTable);
ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java
@@ -132,9 +132,7 @@
        List<String> templates = new ArrayList<String>();
        templates.add("vm/java/domain.java.vm");
        templates.add("vm/java/vo.java.vm");
        templates.add("vm/java/queryBo.java.vm");
        templates.add("vm/java/addBo.java.vm");
        templates.add("vm/java/editBo.java.vm");
        templates.add("vm/java/bo.java.vm");
        templates.add("vm/java/mapper.java.vm");
        templates.add("vm/java/service.java.vm");
        templates.add("vm/java/serviceImpl.java.vm");
@@ -184,20 +182,12 @@
        }
        if (template.contains("vo.java.vm"))
        {
            fileName = StrUtil.format("{}/vo/{}Vo.java", javaPath, className);
            fileName = StrUtil.format("{}/domain/vo/{}Vo.java", javaPath, className);
        }
        if (template.contains("queryBo.java.vm"))
        {
            fileName = StrUtil.format("{}/bo/{}QueryBo.java", javaPath, className);
        }
        if (template.contains("addBo.java.vm"))
        {
            fileName = StrUtil.format("{}/bo/{}AddBo.java", javaPath, className);
        }
        if (template.contains("editBo.java.vm"))
        {
            fileName = StrUtil.format("{}/bo/{}EditBo.java", javaPath, className);
        }
        if (template.contains("bo.java.vm"))
        {
            fileName = StrUtil.format("{}/domain/bo/{}Bo.java", javaPath, className);
        }
        if (template.contains("sub-domain.java.vm") && StrUtil.equals(GenConstants.TPL_SUB, genTable.getTplCategory()))
        {
            fileName = StrUtil.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName());
ruoyi-generator/src/main/resources/vm/java/addBo.java.vm
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/resources/vm/java/bo.java.vm
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,88 @@
package ${packageName}.domain.bo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.*;
import java.util.Date;
#foreach ($import in $importList)
import ${import};
#end
#if($table.crud || $table.sub)
import com.ruoyi.common.core.domain.BaseEntity;
#elseif($table.tree)
import com.ruoyi.common.core.domain.TreeEntity;
#end
/**
 * ${functionName}业务对象 ${tableName}
 *
 * @author ${author}
 * @date ${datetime}
 */
#if($table.crud || $table.sub)
#set($Entity="BaseEntity")
#elseif($table.tree)
#set($Entity="TreeEntity")
#end
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("${functionName}业务对象")
public class ${ClassName}Bo extends ${Entity} {
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField) && ($column.query || $column.isInsert || $column.isEdit))
    /**
     * $column.columnComment
     */
    @ApiModelProperty("$column.columnComment")
#if($column.isInsert && $column.isEdit)
#set($Group="AddGroup.class, EditGroup.class")
#elseif($column.isInsert)
#set($Group="AddGroup.class")
#elseif($column.isEdit)
#set($Group="EditGroup.class")
#end
#if($column.isRequired == 1)
#if($column.javaType == 'String')
    @NotBlank(message = "$column.columnComment不能为空", groups = { $Group })
#else
    @NotNull(message = "$column.columnComment不能为空", groups = { $Group })
#end
#end
    private $column.javaType $column.javaField;
#end
#end
    /**
     * åˆ†é¡µå¤§å°
     */
    @ApiModelProperty("分页大小")
    private Integer pageSize;
    /**
     * å½“前页数
     */
    @ApiModelProperty("当前页数")
    private Integer pageNum;
    /**
     * æŽ’序列
     */
    @ApiModelProperty("排序列")
    private String orderByColumn;
    /**
     * æŽ’序的方向desc或者asc
     */
    @ApiModelProperty(value = "排序的方向", example = "asc,desc")
    private String isAsc;
}
ruoyi-generator/src/main/resources/vm/java/controller.java.vm
@@ -13,11 +13,11 @@
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.enums.BusinessType;
import ${packageName}.vo.${ClassName}Vo;
import ${packageName}.bo.${ClassName}QueryBo;
import ${packageName}.bo.${ClassName}AddBo;
import ${packageName}.bo.${ClassName}EditBo;
import ${packageName}.domain.vo.${ClassName}Vo;
import ${packageName}.domain.bo.${ClassName}Bo;
import ${packageName}.service.I${ClassName}Service;
import com.ruoyi.common.utils.poi.ExcelUtil;
#if($table.crud || $table.sub)
@@ -49,11 +49,11 @@
    @PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')")
    @GetMapping("/list")
#if($table.crud || $table.sub)
    public TableDataInfo<${ClassName}Vo> list(@Validated ${ClassName}QueryBo bo) {
    public TableDataInfo<${ClassName}Vo> list(@Validated ${ClassName}Bo bo) {
        return i${ClassName}Service.queryPageList(bo);
    }
#elseif($table.tree)
    public AjaxResult<List<${ClassName}Vo>> list(@Validated ${ClassName}QueryBo bo) {
    public AjaxResult<List<${ClassName}Vo>> list(@Validated ${ClassName}Bo bo) {
        List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo);
        return AjaxResult.success(list);
    }
@@ -66,7 +66,7 @@
    @PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')")
    @Log(title = "${functionName}", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult<${ClassName}Vo> export(@Validated ${ClassName}QueryBo bo) {
    public AjaxResult<${ClassName}Vo> export(@Validated ${ClassName}Bo bo) {
        List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo);
        ExcelUtil<${ClassName}Vo> util = new ExcelUtil<${ClassName}Vo>(${ClassName}Vo.class);
        return util.exportExcel(list, "${functionName}");
@@ -91,8 +91,8 @@
    @Log(title = "${functionName}", businessType = BusinessType.INSERT)
    @RepeatSubmit
    @PostMapping()
    public AjaxResult<Void> add(@Validated @RequestBody ${ClassName}AddBo bo) {
        return toAjax(i${ClassName}Service.insertByAddBo(bo) ? 1 : 0);
    public AjaxResult<Void> add(@Validated(AddGroup.class) @RequestBody ${ClassName}Bo bo) {
        return toAjax(i${ClassName}Service.insertByBo(bo) ? 1 : 0);
    }
    /**
@@ -103,8 +103,8 @@
    @Log(title = "${functionName}", businessType = BusinessType.UPDATE)
    @RepeatSubmit
    @PutMapping()
    public AjaxResult<Void> edit(@Validated @RequestBody ${ClassName}EditBo bo) {
        return toAjax(i${ClassName}Service.updateByEditBo(bo) ? 1 : 0);
    public AjaxResult<Void> edit(@Validated(EditGroup.class) @RequestBody ${ClassName}Bo bo) {
        return toAjax(i${ClassName}Service.updateByBo(bo) ? 1 : 0);
    }
    /**
ruoyi-generator/src/main/resources/vm/java/editBo.java.vm
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/resources/vm/java/queryBo.java.vm
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/resources/vm/java/service.java.vm
@@ -1,10 +1,8 @@
package ${packageName}.service;
import ${packageName}.domain.${ClassName};
import ${packageName}.vo.${ClassName}Vo;
import ${packageName}.bo.${ClassName}QueryBo;
import ${packageName}.bo.${ClassName}AddBo;
import ${packageName}.bo.${ClassName}EditBo;
import ${packageName}.domain.vo.${ClassName}Vo;
import ${packageName}.domain.bo.${ClassName}Bo;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
#if($table.crud || $table.sub)
import com.ruoyi.common.core.page.TableDataInfo;
@@ -19,7 +17,7 @@
 * @author ${author}
 * @date ${datetime}
 */
public interface I${ClassName}Service extends IServicePlus<${ClassName}> {
public interface I${ClassName}Service extends IServicePlus<${ClassName}, ${ClassName}Vo> {
    /**
     * æŸ¥è¯¢å•个
     * @return
@@ -30,27 +28,27 @@
    /**
     * æŸ¥è¯¢åˆ—表
     */
    TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}QueryBo bo);
    TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo);
#end
    /**
     * æŸ¥è¯¢åˆ—表
     */
    List<${ClassName}Vo> queryList(${ClassName}QueryBo bo);
    List<${ClassName}Vo> queryList(${ClassName}Bo bo);
    /**
     * æ ¹æ®æ–°å¢žä¸šåŠ¡å¯¹è±¡æ’å…¥${functionName}
     * @param bo ${functionName}新增业务对象
     * @return
     */
    Boolean insertByAddBo(${ClassName}AddBo bo);
    Boolean insertByBo(${ClassName}Bo bo);
    /**
     * æ ¹æ®ç¼–辑业务对象修改${functionName}
     * @param bo ${functionName}编辑业务对象
     * @return
     */
    Boolean updateByEditBo(${ClassName}EditBo bo);
    Boolean updateByBo(${ClassName}Bo bo);
    /**
     * æ ¡éªŒå¹¶åˆ é™¤æ•°æ®
ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm
@@ -11,12 +11,10 @@
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import ${packageName}.bo.${ClassName}AddBo;
import ${packageName}.bo.${ClassName}QueryBo;
import ${packageName}.bo.${ClassName}EditBo;
import ${packageName}.domain.bo.${ClassName}Bo;
import ${packageName}.domain.vo.${ClassName}Vo;
import ${packageName}.domain.${ClassName};
import ${packageName}.mapper.${ClassName}Mapper;
import ${packageName}.vo.${ClassName}Vo;
import ${packageName}.service.I${ClassName}Service;
import java.util.List;
@@ -30,27 +28,27 @@
 * @date ${datetime}
 */
@Service
public class ${ClassName}ServiceImpl extends ServicePlusImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service {
public class ${ClassName}ServiceImpl extends ServicePlusImpl<${ClassName}Mapper, ${ClassName}, ${ClassName}Vo> implements I${ClassName}Service {
    @Override
    public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}){
        return getVoById(${pkColumn.javaField}, ${ClassName}Vo.class);
        return getVoById(${pkColumn.javaField});
    }
#if($table.crud || $table.sub)
    @Override
    public TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}QueryBo bo) {
        PagePlus<${ClassName}, ${ClassName}Vo> result = pageVo(PageUtils.buildPagePlus(), buildQueryWrapper(bo), ${ClassName}Vo.class);
    public TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo) {
        PagePlus<${ClassName}, ${ClassName}Vo> result = pageVo(PageUtils.buildPagePlus(), buildQueryWrapper(bo));
        return PageUtils.buildDataInfo(result);
    }
#end
    @Override
    public List<${ClassName}Vo> queryList(${ClassName}QueryBo bo) {
        return listVo(buildQueryWrapper(bo), ${ClassName}Vo.class);
    public List<${ClassName}Vo> queryList(${ClassName}Bo bo) {
        return listVo(buildQueryWrapper(bo));
    }
    private LambdaQueryWrapper<${ClassName}> buildQueryWrapper(${ClassName}QueryBo bo) {
    private LambdaQueryWrapper<${ClassName}> buildQueryWrapper(${ClassName}Bo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<${ClassName}> lqw = Wrappers.lambdaQuery();
#foreach($column in $columns)
@@ -78,14 +76,14 @@
    }
    @Override
    public Boolean insertByAddBo(${ClassName}AddBo bo) {
    public Boolean insertByBo(${ClassName}Bo bo) {
        ${ClassName} add = BeanUtil.toBean(bo, ${ClassName}.class);
        validEntityBeforeSave(add);
        return save(add);
    }
    @Override
    public Boolean updateByEditBo(${ClassName}EditBo bo) {
    public Boolean updateByBo(${ClassName}Bo bo) {
        ${ClassName} update = BeanUtil.toBean(bo, ${ClassName}.class);
        validEntityBeforeSave(update);
        return updateById(update);
ruoyi-generator/src/main/resources/vm/java/vo.java.vm
@@ -1,4 +1,4 @@
package ${packageName}.vo;
package ${packageName}.domain.vo;
import com.ruoyi.common.annotation.Excel;
#foreach ($import in $importList)
ruoyi-oss/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.6.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-oss</artifactId>
    <description>
        OSS对象存储模块
    </description>
    <dependencies>
        <!-- é€šç”¨å·¥å…·-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-system</artifactId>
        </dependency>
        <dependency>
            <groupId>com.qiniu</groupId>
            <artifactId>qiniu-java-sdk</artifactId>
            <version>${qiniu.version}</version>
        </dependency>
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>${aliyun.oss.version}</version>
        </dependency>
        <dependency>
            <groupId>com.qcloud</groupId>
            <artifactId>cos_api</artifactId>
            <version>${qcloud.cos.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>${minio.version}</version>
        </dependency>
    </dependencies>
</project>
ruoyi-oss/src/main/java/com/ruoyi/oss/constant/CloudConstant.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.oss.constant;
/**
 * äº‘存储常量
 *
 * @author Lion Li
 */
public class CloudConstant {
    /**
     * äº‘存储配置KEY
     */
    public final static String CLOUD_STORAGE_CONFIG_KEY = "sys.oss.cloudStorageService";
    /**
     * é¢„览列表资源开关Key
     */
    public final static String PEREVIEW_LIST_RESOURCE_KEY = "sys.oss.previewListResource";
}
ruoyi-oss/src/main/java/com/ruoyi/oss/entity/UploadResult.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package com.ruoyi.oss.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
 * ä¸Šä¼ è¿”回体
 *
 * @author Lion Li
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class UploadResult {
    /**
     * æ–‡ä»¶è·¯å¾„
     */
    private String url;
    /**
     * æ–‡ä»¶å
     */
    private String filename;
}
ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/CloudServiceEnumd.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
package com.ruoyi.oss.enumd;
import com.ruoyi.oss.service.impl.AliyunCloudStorageServiceImpl;
import com.ruoyi.oss.service.impl.MinioCloudStorageServiceImpl;
import com.ruoyi.oss.service.impl.QcloudCloudStorageServiceImpl;
import com.ruoyi.oss.service.impl.QiniuCloudStorageServiceImpl;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * äº‘存储服务商枚举
 *
 * @author Lion Li
 */
@Getter
@AllArgsConstructor
public enum CloudServiceEnumd {
    /**
     * ä¸ƒç‰›äº‘
     */
    QINIU("qiniu", QiniuCloudStorageServiceImpl.class),
    /**
     * é˜¿é‡Œäº‘
     */
    ALIYUN("aliyun", AliyunCloudStorageServiceImpl.class),
    /**
     * è…¾è®¯äº‘
     */
    QCLOUD("qcloud", QcloudCloudStorageServiceImpl.class),
    /**
     * minio
     */
    MINIO("minio", MinioCloudStorageServiceImpl.class);
    private final String value;
    private final Class<?> serviceClass;
    public static Class<?> getServiceClass(String value) {
        for (CloudServiceEnumd clazz : values()) {
            if (clazz.getValue().equals(value)) {
                return clazz.getServiceClass();
            }
        }
        return null;
    }
}
ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/PolicyType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill åº„骞 (smallchill@163.com)
 */
package com.ruoyi.oss.enumd;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * minio策略配置
 *
 * @author Lion Li
 */
@Getter
@AllArgsConstructor
public enum PolicyType {
    /**
     * åªè¯»
     */
    READ("read-only"),
    /**
     * åªå†™
     */
    WRITE("write-only"),
    /**
     * è¯»å†™
     */
    READ_WRITE("read-write");
    /**
     * ç±»åž‹
     */
    private final String type;
}
ruoyi-oss/src/main/java/com/ruoyi/oss/exception/OssException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.oss.exception;
/**
 * OSS异常类
 *
 * @author Lion Li
 */
public class OssException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    public OssException(String msg) {
        super(msg);
    }
}
ruoyi-oss/src/main/java/com/ruoyi/oss/factory/OssFactory.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package com.ruoyi.oss.factory;
import cn.hutool.core.lang.Assert;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.oss.constant.CloudConstant;
import com.ruoyi.oss.enumd.CloudServiceEnumd;
import com.ruoyi.oss.service.ICloudStorageService;
import com.ruoyi.system.service.ISysConfigService;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * æ–‡ä»¶ä¸Šä¼ Factory
 *
 * @author Lion Li
 */
public class OssFactory {
    private static ISysConfigService sysConfigService;
    static {
        OssFactory.sysConfigService = SpringUtils.getBean(ISysConfigService.class);
    }
    private static final Map<String, ICloudStorageService> SERVICES = new ConcurrentHashMap<>();
    public static ICloudStorageService instance() {
        String type = sysConfigService.selectConfigByKey(CloudConstant.CLOUD_STORAGE_CONFIG_KEY);
        return instance(type);
    }
    public static ICloudStorageService instance(String type) {
        ICloudStorageService service = SERVICES.get(type);
        if (service == null) {
            service = (ICloudStorageService) SpringUtils.getBean(CloudServiceEnumd.getServiceClass(type));
        }
        return service;
    }
    public static void register(String type, ICloudStorageService iCloudStorageService) {
        Assert.notNull(type, "type can't be null");
        SERVICES.put(type, iCloudStorageService);
    }
}
ruoyi-oss/src/main/java/com/ruoyi/oss/properties/CloudStorageProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,187 @@
package com.ruoyi.oss.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * OSS云存储 é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
@Component
@ConfigurationProperties(prefix = "cloud-storage")
public class CloudStorageProperties {
    private Boolean previewListImage;
    private QiniuProperties qiniu;
    private AliyunProperties aliyun;
    private QcloudProperties qcloud;
    private MinioProperties minio;
    /**
     * é˜¿é‡Œäº‘ é…ç½®å±žæ€§
     *
     * @author Lion Li
     */
    @Data
    @NoArgsConstructor
    public static class AliyunProperties {
        /**
         * é˜¿é‡Œäº‘绑定的域名
         */
        private String endpoint;
        /**
         * é˜¿é‡Œäº‘路径前缀
         */
        private String prefix;
        /**
         * é˜¿é‡Œäº‘AccessKeyId
         */
        private String accessKeyId;
        /**
         * é˜¿é‡Œäº‘AccessKeySecret
         */
        private String accessKeySecret;
        /**
         * é˜¿é‡Œäº‘BucketName
         */
        private String bucketName;
    }
    /**
     * Minio é…ç½®å±žæ€§
     *
     * @author Lion Li
     */
    @Data
    @NoArgsConstructor
    public static class MinioProperties {
        /**
         * minio域名
         */
        private String endpoint;
        /**
         * minio ACCESS_KEY
         */
        private String accessKey;
        /**
         * minio SECRET_KEY
         */
        private String secretKey;
        /**
         * minio å­˜å‚¨ç©ºé—´å
         */
        private String bucketName;
    }
    /**
     * è…¾è®¯äº‘COS é…ç½®å±žæ€§
     *
     * @author Lion Li
     */
    @Data
    @NoArgsConstructor
    public static class QcloudProperties {
        /**
         * è…¾è®¯äº‘绑定的域名
         */
        private String endpoint;
        /**
         * è…¾è®¯äº‘路径前缀
         */
        private String prefix;
        /**
         * è…¾è®¯äº‘SecretId
         */
        private String secretId;
        /**
         * è…¾è®¯äº‘SecretKey
         */
        private String secretKey;
        /**
         * è…¾è®¯äº‘BucketName
         */
        private String bucketName;
        /**
         * ä¸ƒç‰›æ˜¯å¦ä½¿ç”¨https
         */
        private Boolean isHttps;
        /**
         * è…¾è®¯äº‘COS所属地区
         */
        private String region;
    }
    /**
     * ä¸ƒç‰›äº‘ é…ç½®å±žæ€§
     *
     * @author Lion Li
     */
    @Data
    @NoArgsConstructor
    public static class QiniuProperties {
        /**
         * ä¸ƒç‰›ç»‘定的域名
         */
        private String domain;
        /**
         * ä¸ƒç‰›è·¯å¾„前缀
         */
        private String prefix;
        /**
         * ä¸ƒç‰›ACCESS_KEY
         */
        private String accessKey;
        /**
         * ä¸ƒç‰›SECRET_KEY
         */
        private String secretKey;
        /**
         * ä¸ƒç‰›å­˜å‚¨ç©ºé—´å
         */
        private String bucketName;
        /**
         * ä¸ƒç‰›å­˜å‚¨åŒºåŸŸ
         */
        private String region;
        /**
         * ä¸ƒç‰›æ˜¯å¦ä½¿ç”¨https
         */
        private Boolean isHttps;
    }
}
ruoyi-oss/src/main/java/com/ruoyi/oss/service/ICloudStorageService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,74 @@
package com.ruoyi.oss.service;
import com.ruoyi.oss.entity.UploadResult;
import java.io.InputStream;
/**
 * äº‘存储服务接口
 *
 * @author Lion Li
 */
public interface ICloudStorageService {
    void createBucket();
    /**
     * èŽ·å–æœåŠ¡å•†ç±»åž‹
     */
    String getServiceType();
    /**
     * æ–‡ä»¶è·¯å¾„
     *
     * @param prefix å‰ç¼€
     * @param suffix åŽç¼€
     * @return è¿”回上传路径
     */
    String getPath(String prefix, String suffix);
    /**
     * æ–‡ä»¶ä¸Šä¼ 
     *
     * @param data æ–‡ä»¶å­—节数组
     * @param path æ–‡ä»¶è·¯å¾„,包含文件名
     * @return è¿”回http地址
     */
    UploadResult upload(byte[] data, String path, String contentType);
    /**
     * æ–‡ä»¶åˆ é™¤
     *
     * @param path æ–‡ä»¶è·¯å¾„,包含文件名
     */
    void delete(String path);
    /**
     * æ–‡ä»¶ä¸Šä¼ 
     *
     * @param data   æ–‡ä»¶å­—节数组
     * @param suffix åŽç¼€
     * @return è¿”回http地址
     */
    UploadResult uploadSuffix(byte[] data, String suffix, String contentType);
    /**
     * æ–‡ä»¶ä¸Šä¼ 
     *
     * @param inputStream å­—节流
     * @param path        æ–‡ä»¶è·¯å¾„,包含文件名
     * @return è¿”回http地址
     */
    UploadResult upload(InputStream inputStream, String path, String contentType);
    /**
     * æ–‡ä»¶ä¸Šä¼ 
     *
     * @param inputStream å­—节流
     * @param suffix      åŽç¼€
     * @return è¿”回http地址
     */
    UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType);
    String getEndpointLink();
}
ruoyi-oss/src/main/java/com/ruoyi/oss/service/abstractd/AbstractCloudStorageService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
package com.ruoyi.oss.service.abstractd;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.oss.entity.UploadResult;
import com.ruoyi.oss.service.ICloudStorageService;
import org.springframework.beans.factory.InitializingBean;
import java.io.InputStream;
import java.util.Date;
/**
 * äº‘存储(支持七牛、阿里云、腾讯云、minio)
 *
 * @author Lion Li
 */
public abstract class AbstractCloudStorageService implements ICloudStorageService, InitializingBean {
    @Override
    public abstract void createBucket();
    @Override
    public abstract String getServiceType();
    @Override
    public String getPath(String prefix, String suffix) {
        // ç”Ÿæˆuuid
        String uuid = IdUtil.fastSimpleUUID();
        // æ–‡ä»¶è·¯å¾„
        String path = DateUtil.format(new Date(), "yyyyMMdd") + "/" + uuid;
        if (StrUtil.isNotBlank(prefix)) {
            path = prefix + "/" + path;
        }
        return path + suffix;
    }
    @Override
    public abstract UploadResult upload(byte[] data, String path, String contentType);
    @Override
    public abstract void delete(String path);
    @Override
    public UploadResult upload(InputStream inputStream, String path, String contentType) {
        byte[] data = IoUtil.readBytes(inputStream);
        return this.upload(data, path, contentType);
    }
    @Override
    public abstract UploadResult uploadSuffix(byte[] data, String suffix, String contentType);
    @Override
    public abstract UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType);
    @Override
    public abstract void afterPropertiesSet() throws Exception;
    @Override
    public abstract String getEndpointLink();
}
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/AliyunCloudStorageServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,128 @@
package com.ruoyi.oss.service.impl;
import cn.hutool.core.util.StrUtil;
import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.CreateBucketRequest;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.ruoyi.oss.entity.UploadResult;
import com.ruoyi.oss.enumd.CloudServiceEnumd;
import com.ruoyi.oss.exception.OssException;
import com.ruoyi.oss.factory.OssFactory;
import com.ruoyi.oss.properties.CloudStorageProperties;
import com.ruoyi.oss.properties.CloudStorageProperties.AliyunProperties;
import com.ruoyi.oss.service.abstractd.AbstractCloudStorageService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
 * é˜¿é‡Œäº‘存储
 *
 * @author Lion Li
 */
@Lazy
@Service
public class AliyunCloudStorageServiceImpl extends AbstractCloudStorageService implements InitializingBean {
    private final OSSClient client;
    private final AliyunProperties properties;
    @Autowired
    public AliyunCloudStorageServiceImpl(CloudStorageProperties properties) {
        this.properties = properties.getAliyun();
        try {
            ClientConfiguration configuration = new ClientConfiguration();
            DefaultCredentialProvider credentialProvider = new DefaultCredentialProvider(
                this.properties.getAccessKeyId(),
                this.properties.getAccessKeySecret());
            client = new OSSClient(this.properties.getEndpoint(), credentialProvider, configuration);
            createBucket();
        } catch (Exception e) {
            throw new IllegalArgumentException("阿里云存储配置错误! è¯·æ£€æŸ¥ç³»ç»Ÿé…ç½®!");
        }
    }
    @Override
    public void createBucket() {
        try {
            String bucketName = properties.getBucketName();
            if (client.doesBucketExist(bucketName)) {
                return;
            }
            CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
            createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
            client.createBucket(createBucketRequest);
        } catch (Exception e) {
            throw new OssException("创建Bucket失败, è¯·æ ¸å¯¹é˜¿é‡Œäº‘配置信息");
        }
    }
    @Override
    public String getServiceType() {
        return CloudServiceEnumd.ALIYUN.getValue();
    }
    @Override
    public UploadResult upload(byte[] data, String path, String contentType) {
        return upload(new ByteArrayInputStream(data), path, contentType);
    }
    @Override
    public UploadResult upload(InputStream inputStream, String path, String contentType) {
        try {
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentType(contentType);
            client.putObject(new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata));
        } catch (Exception e) {
            throw new OssException("上传文件失败,请检查阿里云配置信息");
        }
        return new UploadResult().setUrl(getEndpointLink() + "/" + path).setFilename(path);
    }
    @Override
    public void delete(String path) {
        path = path.replace(getEndpointLink() + "/", "");
        try {
            client.deleteObject(properties.getBucketName(), path);
        } catch (Exception e) {
            throw new OssException("上传文件失败,请检查阿里云配置信息");
        }
    }
    @Override
    public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
        return upload(data, getPath(properties.getPrefix(), suffix), contentType);
    }
    @Override
    public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
        return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        OssFactory.register(getServiceType(), this);
    }
    @Override
    public String getEndpointLink() {
        String endpoint = properties.getEndpoint();
        StringBuilder sb = new StringBuilder(endpoint);
        if (StrUtil.containsAnyIgnoreCase(endpoint, "http://")) {
            sb.insert(7, properties.getBucketName() + ".");
        } else if (StrUtil.containsAnyIgnoreCase(endpoint, "https://")) {
            sb.insert(8, properties.getBucketName() + ".");
        } else {
            throw new OssException("Endpoint配置错误");
        }
        return sb.toString();
    }
}
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/MinioCloudStorageServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,195 @@
package com.ruoyi.oss.service.impl;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.oss.entity.UploadResult;
import com.ruoyi.oss.enumd.CloudServiceEnumd;
import com.ruoyi.oss.enumd.PolicyType;
import com.ruoyi.oss.exception.OssException;
import com.ruoyi.oss.factory.OssFactory;
import com.ruoyi.oss.properties.CloudStorageProperties;
import com.ruoyi.oss.properties.CloudStorageProperties.MinioProperties;
import com.ruoyi.oss.service.abstractd.AbstractCloudStorageService;
import io.minio.*;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
 * minio存储
 *
 * @author Lion Li
 */
@Lazy
@Service
public class MinioCloudStorageServiceImpl extends AbstractCloudStorageService implements InitializingBean {
    private final MinioClient minioClient;
    private final MinioProperties properties;
    @Autowired
    public MinioCloudStorageServiceImpl(CloudStorageProperties properties) {
        this.properties = properties.getMinio();
        try {
            minioClient = MinioClient.builder()
                .endpoint(this.properties.getEndpoint())
                .credentials(this.properties.getAccessKey(), this.properties.getSecretKey())
                .build();
            createBucket();
        } catch (Exception e) {
            throw new IllegalArgumentException("Minio存储配置错误! è¯·æ£€æŸ¥ç³»ç»Ÿé…ç½®!");
        }
    }
    @Override
    public void createBucket() {
        try {
            String bucketName = properties.getBucketName();
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (exists) {
                return;
            }
            // ä¸å­˜åœ¨å°±åˆ›å»ºæ¡¶
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
                .bucket(bucketName)
                .config(getPolicy(bucketName, PolicyType.READ))
                .build());
        } catch (Exception e) {
            throw new OssException("创建Bucket失败, è¯·æ ¸å¯¹Minio配置信息");
        }
    }
    @Override
    public String getServiceType() {
        return CloudServiceEnumd.MINIO.getValue();
    }
    @Override
    public UploadResult upload(byte[] data, String path, String contentType) {
        return upload(new ByteArrayInputStream(data), path, contentType);
    }
    @Override
    public UploadResult upload(InputStream inputStream, String path, String contentType) {
        try {
            minioClient.putObject(PutObjectArgs.builder()
                .bucket(properties.getBucketName())
                .object(path)
                .contentType(StrUtil.blankToDefault(contentType, MediaType.APPLICATION_OCTET_STREAM_VALUE))
                .stream(inputStream, inputStream.available(), -1)
                .build());
        } catch (Exception e) {
            throw new OssException("上传文件失败,请核对Minio配置信息");
        }
        return new UploadResult().setUrl(getEndpointLink() + "/" + path).setFilename(path);
    }
    @Override
    public void delete(String path) {
        path = path.replace(getEndpointLink() + "/", "");
        try {
            minioClient.removeObject(RemoveObjectArgs.builder()
                .bucket(properties.getBucketName())
                .object(path)
                .build());
        } catch (Exception e) {
            throw new OssException(e.getMessage());
        }
    }
    @Override
    public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
        return upload(data, getPath("", suffix), contentType);
    }
    @Override
    public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
        return upload(inputStream, getPath("", suffix), contentType);
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        OssFactory.register(getServiceType(), this);
    }
    @Override
    public String getEndpointLink() {
        return properties.getEndpoint() + "/" + properties.getBucketName();
    }
    private String getPolicy(String bucketName, PolicyType policyType) {
        StringBuilder builder = new StringBuilder();
        builder.append("{\n");
        builder.append("    \"Statement\": [\n");
        builder.append("        {\n");
        builder.append("            \"Action\": [\n");
        if (policyType == PolicyType.WRITE) {
            builder.append("                \"s3:GetBucketLocation\",\n");
            builder.append("                \"s3:ListBucketMultipartUploads\"\n");
        } else if (policyType == PolicyType.READ_WRITE) {
            builder.append("                \"s3:GetBucketLocation\",\n");
            builder.append("                \"s3:ListBucket\",\n");
            builder.append("                \"s3:ListBucketMultipartUploads\"\n");
        } else {
            builder.append("                \"s3:GetBucketLocation\"\n");
        }
        builder.append("            ],\n");
        builder.append("            \"Effect\": \"Allow\",\n");
        builder.append("            \"Principal\": \"*\",\n");
        builder.append("            \"Resource\": \"arn:aws:s3:::");
        builder.append(bucketName);
        builder.append("\"\n");
        builder.append("        },\n");
        if (PolicyType.READ.equals(policyType)) {
            builder.append("        {\n");
            builder.append("            \"Action\": [\n");
            builder.append("                \"s3:ListBucket\"\n");
            builder.append("            ],\n");
            builder.append("            \"Effect\": \"Deny\",\n");
            builder.append("            \"Principal\": \"*\",\n");
            builder.append("            \"Resource\": \"arn:aws:s3:::");
            builder.append(bucketName);
            builder.append("\"\n");
            builder.append("        },\n");
        }
        builder.append("        {\n");
        builder.append("            \"Action\": ");
        switch (policyType) {
            case WRITE:
                builder.append("[\n");
                builder.append("                \"s3:AbortMultipartUpload\",\n");
                builder.append("                \"s3:DeleteObject\",\n");
                builder.append("                \"s3:ListMultipartUploadParts\",\n");
                builder.append("                \"s3:PutObject\"\n");
                builder.append("            ],\n");
                break;
            case READ_WRITE:
                builder.append("[\n");
                builder.append("                \"s3:AbortMultipartUpload\",\n");
                builder.append("                \"s3:DeleteObject\",\n");
                builder.append("                \"s3:GetObject\",\n");
                builder.append("                \"s3:ListMultipartUploadParts\",\n");
                builder.append("                \"s3:PutObject\"\n");
                builder.append("            ],\n");
                break;
            default:
                builder.append("\"s3:GetObject\",\n");
                break;
        }
        builder.append("            \"Effect\": \"Allow\",\n");
        builder.append("            \"Principal\": \"*\",\n");
        builder.append("            \"Resource\": \"arn:aws:s3:::");
        builder.append(bucketName);
        builder.append("/*\"\n");
        builder.append("        }\n");
        builder.append("    ],\n");
        builder.append("    \"Version\": \"2012-10-17\"\n");
        builder.append("}\n");
        return builder.toString();
    }
}
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/QcloudCloudStorageServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,136 @@
package com.ruoyi.oss.service.impl;
import cn.hutool.core.util.StrUtil;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.*;
import com.qcloud.cos.region.Region;
import com.ruoyi.oss.entity.UploadResult;
import com.ruoyi.oss.enumd.CloudServiceEnumd;
import com.ruoyi.oss.exception.OssException;
import com.ruoyi.oss.factory.OssFactory;
import com.ruoyi.oss.properties.CloudStorageProperties;
import com.ruoyi.oss.properties.CloudStorageProperties.QcloudProperties;
import com.ruoyi.oss.service.abstractd.AbstractCloudStorageService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
 * è…¾è®¯äº‘存储
 *
 * @author Lion Li
 */
@Lazy
@Service
public class QcloudCloudStorageServiceImpl extends AbstractCloudStorageService implements InitializingBean {
    private final COSClient client;
    private final QcloudProperties properties;
    @Autowired
    public QcloudCloudStorageServiceImpl(CloudStorageProperties properties) {
        this.properties = properties.getQcloud();
        try {
            COSCredentials credentials = new BasicCOSCredentials(
                this.properties.getSecretId(),
                this.properties.getSecretKey());
            // åˆå§‹åŒ–客户端配置
            ClientConfig clientConfig = new ClientConfig();
            // è®¾ç½®bucket所在的区域,华南:gz åŽåŒ—:tj åŽä¸œï¼šsh
            clientConfig.setRegion(new Region(this.properties.getRegion()));
            if (this.properties.getIsHttps()) {
                clientConfig.setHttpProtocol(HttpProtocol.https);
            } else {
                clientConfig.setHttpProtocol(HttpProtocol.http);
            }
            client = new COSClient(credentials, clientConfig);
            createBucket();
        } catch (Exception e) {
            throw new IllegalArgumentException("腾讯云存储配置错误! è¯·æ£€æŸ¥ç³»ç»Ÿé…ç½®!");
        }
    }
    @Override
    public void createBucket() {
        try {
            String bucketName = properties.getBucketName();
            if (client.doesBucketExist(bucketName)) {
                return;
            }
            CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
            createBucketRequest.setCannedAcl(CannedAccessControlList.PublicRead);
            client.createBucket(createBucketRequest);
        } catch (Exception e) {
            throw new OssException("创建Bucket失败, è¯·æ ¸å¯¹è…¾è®¯äº‘配置信息");
        }
    }
    @Override
    public String getServiceType() {
        return CloudServiceEnumd.QCLOUD.getValue();
    }
    @Override
    public UploadResult upload(byte[] data, String path, String contentType) {
        return upload(new ByteArrayInputStream(data), path, contentType);
    }
    @Override
    public UploadResult upload(InputStream inputStream, String path, String contentType) {
        try {
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentType(contentType);
            client.putObject(new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata));
        } catch (Exception e) {
            throw new OssException("上传文件失败,请检查腾讯云配置信息");
        }
        return new UploadResult().setUrl(getEndpointLink() + "/" + path).setFilename(path);
    }
    @Override
    public void delete(String path) {
        path = path.replace(getEndpointLink() + "/", "");
        try {
            client.deleteObject(new DeleteObjectRequest(properties.getBucketName(), path));
        } catch (Exception e) {
            throw new OssException("上传文件失败,请检腾讯云查配置信息");
        }
    }
    @Override
    public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
        return upload(data, getPath(properties.getPrefix(), suffix), contentType);
    }
    @Override
    public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
        return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        OssFactory.register(getServiceType(),this);
    }
    @Override
    public String getEndpointLink() {
        String endpoint = properties.getEndpoint();
        StringBuilder sb = new StringBuilder(endpoint);
        if (StrUtil.containsAnyIgnoreCase(endpoint, "http://")) {
            sb.insert(7, properties.getBucketName() + ".");
        } else if (StrUtil.containsAnyIgnoreCase(endpoint, "https://")) {
            sb.insert(8, properties.getBucketName() + ".");
        } else {
            throw new OssException("Endpoint配置错误");
        }
        return sb.toString();
    }
}
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/QiniuCloudStorageServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,145 @@
package com.ruoyi.oss.service.impl;
import cn.hutool.core.util.ArrayUtil;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.util.Auth;
import com.ruoyi.oss.entity.UploadResult;
import com.ruoyi.oss.enumd.CloudServiceEnumd;
import com.ruoyi.oss.exception.OssException;
import com.ruoyi.oss.factory.OssFactory;
import com.ruoyi.oss.properties.CloudStorageProperties;
import com.ruoyi.oss.properties.CloudStorageProperties.QiniuProperties;
import com.ruoyi.oss.service.abstractd.AbstractCloudStorageService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import java.io.InputStream;
/**
 * ä¸ƒç‰›äº‘存储
 *
 * @author Lion Li
 */
@Lazy
@Service
public class QiniuCloudStorageServiceImpl extends AbstractCloudStorageService implements InitializingBean {
    private final UploadManager uploadManager;
    private final BucketManager bucketManager;
    private final String token;
    private final QiniuProperties properties;
    @Autowired
    public QiniuCloudStorageServiceImpl(CloudStorageProperties properties) {
        this.properties = properties.getQiniu();
        try {
            Configuration config = new Configuration(getRegion(this.properties.getRegion()));
            // https设置
            config.useHttpsDomains = false;
            if (this.properties.getIsHttps() != null) {
                config.useHttpsDomains = this.properties.getIsHttps();
            }
            uploadManager = new UploadManager(config);
            Auth auth = Auth.create(
                this.properties.getAccessKey(),
                this.properties.getSecretKey());
            String bucketName = this.properties.getBucketName();
            token = auth.uploadToken(bucketName);
            bucketManager = new BucketManager(auth, config);
            if (!ArrayUtil.contains(bucketManager.buckets(), bucketName)) {
                bucketManager.createBucket(bucketName, this.properties.getRegion());
            }
        } catch (Exception e) {
            throw new IllegalArgumentException("七牛云存储配置错误! è¯·æ£€æŸ¥ç³»ç»Ÿé…ç½®!");
        }
    }
    @Override
    public void createBucket() {
        try {
            String bucketName = properties.getBucketName();
            if (ArrayUtil.contains(bucketManager.buckets(), bucketName)) {
                return;
            }
            bucketManager.createBucket(bucketName, properties.getRegion());
        } catch (Exception e) {
            throw new OssException("创建Bucket失败, è¯·æ ¸å¯¹ä¸ƒç‰›äº‘配置信息");
        }
    }
    @Override
    public String getServiceType() {
        return CloudServiceEnumd.QINIU.getValue();
    }
    @Override
    public UploadResult upload(byte[] data, String path, String contentType) {
        try {
            Response res = uploadManager.put(data, path, token, null, contentType, false);
            if (!res.isOK()) {
                throw new RuntimeException("上传七牛出错:" + res.toString());
            }
        } catch (Exception e) {
            throw new OssException("上传文件失败,请核对七牛配置信息");
        }
        return new UploadResult().setUrl(getEndpointLink() + "/" + path).setFilename(path);
    }
    @Override
    public void delete(String path) {
        try {
            path = path.replace(getEndpointLink() + "/", "");
            Response res = bucketManager.delete(properties.getBucketName(), path);
            if (!res.isOK()) {
                throw new RuntimeException("删除七牛文件出错:" + res.toString());
            }
        } catch (Exception e) {
            throw new OssException(e.getMessage());
        }
    }
    @Override
    public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
        return upload(data, getPath(properties.getPrefix(), suffix), contentType);
    }
    @Override
    public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
        return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        OssFactory.register(getServiceType(),this);
    }
    @Override
    public String getEndpointLink() {
        return properties.getDomain();
    }
    private Region getRegion(String region) {
        switch (region) {
            case "z0":
                return Region.region0();
            case "z1":
                return Region.region1();
            case "z2":
                return Region.region2();
            case "na0":
                return Region.regionNa0();
            case "as0":
                return Region.regionAs0();
            default:
                return Region.autoRegion();
        }
    }
}
ruoyi-oss/src/main/java/com/ruoyi/system/controller/SysOssController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,114 @@
package com.ruoyi.system.controller;
import cn.hutool.core.convert.Convert;
import cn.hutool.http.HttpUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.system.domain.bo.SysOssBo;
import com.ruoyi.system.domain.SysOss;
import com.ruoyi.system.service.ISysOssService;
import com.ruoyi.system.domain.vo.SysOssVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
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.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
 * æ–‡ä»¶ä¸Šä¼  æŽ§åˆ¶å±‚
 *
 * @author Lion Li
 */
@Validated
@Api(value = "OSS云存储控制器", tags = {"OSS云存储管理"})
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@RestController
@RequestMapping("/system/oss")
public class SysOssController extends BaseController {
    private final ISysOssService iSysOssService;
    /**
     * æŸ¥è¯¢OSS云存储列表
     */
    @ApiOperation("查询OSS云存储列表")
    @PreAuthorize("@ss.hasPermi('system:oss:list')")
    @GetMapping("/list")
    public TableDataInfo<SysOssVo> list(@Validated SysOssBo bo) {
        return iSysOssService.queryPageList(bo);
    }
    /**
     * ä¸Šä¼ OSS云存储
     */
    @ApiOperation("上传OSS云存储")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "file", value = "文件", dataType = "java.io.File", required = true),
    })
    @PreAuthorize("@ss.hasPermi('system:oss:upload')")
    @Log(title = "OSS云存储", businessType = BusinessType.INSERT)
    @RepeatSubmit
    @PostMapping("/upload")
    public AjaxResult<Map<String, String>> upload(@RequestPart("file") MultipartFile file) {
        if (file.isEmpty()) {
            throw new CustomException("上传文件不能为空");
        }
        SysOss oss = iSysOssService.upload(file);
        Map<String, String> map = new HashMap<>(2);
        map.put("url", oss.getUrl());
        map.put("fileName", oss.getFileName());
        return AjaxResult.success(map);
    }
    @ApiOperation("下载OSS云存储")
    @PreAuthorize("@ss.hasPermi('system:oss:download')")
    @GetMapping("/download/{ossId}")
    public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
        SysOss sysOss = iSysOssService.getById(ossId);
        if (sysOss == null) {
            throw new CustomException("文件数据不存在!");
        }
        response.reset();
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
        FileUtils.setAttachmentResponseHeader(response, URLEncoder.encode(sysOss.getOriginalName(), StandardCharsets.UTF_8));
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
        long data = HttpUtil.download(sysOss.getUrl(), response.getOutputStream(), false);
        response.setContentLength(Convert.toInt(data));
    }
    /**
     * åˆ é™¤OSS云存储
     */
    @ApiOperation("删除OSS云存储")
    @PreAuthorize("@ss.hasPermi('system:oss:remove')")
    @Log(title = "OSS云存储" , businessType = BusinessType.DELETE)
    @DeleteMapping("/{ossIds}")
    public AjaxResult<Void> remove(@NotEmpty(message = "主键不能为空")
                                   @PathVariable Long[] ossIds) {
        return toAjax(iSysOssService.deleteWithValidByIds(Arrays.asList(ossIds), true) ? 1 : 0);
    }
}
ruoyi-oss/src/main/java/com/ruoyi/system/domain/SysOss.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,80 @@
package com.ruoyi.system.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
 * OSS云存储对象
 *
 * @author Lion Li
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_oss")
public class SysOss implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * äº‘存储主键
     */
    @TableId(value = "oss_id", type = IdType.AUTO)
    private Long ossId;
    /**
     * æ–‡ä»¶å
     */
    private String fileName;
    /**
     * åŽŸå
     */
    private String originalName;
    /**
     * æ–‡ä»¶åŽç¼€å
     */
    private String fileSuffix;
    /**
     * URL地址
     */
    private String url;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    /**
     * ä¸Šä¼ äºº
     */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    /**
     * æ›´æ–°äºº
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /**
     * æœåС商
     */
    private String service;
}
ruoyi-oss/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
package com.ruoyi.system.domain.bo;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * OSS云存储分页查询对象 sys_oss
 *
 * @author Lion Li
 */
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("OSS云存储分页查询对象")
public class SysOssBo extends BaseEntity {
    /**
     * åˆ†é¡µå¤§å°
     */
    @ApiModelProperty("分页大小")
    private Integer pageSize;
    /**
     * å½“前页数
     */
    @ApiModelProperty("当前页数")
    private Integer pageNum;
    /**
     * æŽ’序列
     */
    @ApiModelProperty("排序列")
    private String orderByColumn;
    /**
     * æŽ’序的方向desc或者asc
     */
    @ApiModelProperty(value = "排序的方向", example = "asc,desc")
    private String isAsc;
    /**
     * æ–‡ä»¶å
     */
    @ApiModelProperty("文件名")
    private String fileName;
    /**
     * åŽŸå
     */
    @ApiModelProperty("原名")
    private String originalName;
    /**
     * æ–‡ä»¶åŽç¼€å
     */
    @ApiModelProperty("文件后缀名")
    private String fileSuffix;
    /**
     * URL地址
     */
    @ApiModelProperty("URL地址")
    private String url;
    /**
     * æœåС商
     */
    @ApiModelProperty("服务商")
    private String service;
}
ruoyi-oss/src/main/java/com/ruoyi/system/domain/vo/SysOssVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package com.ruoyi.system.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
 * OSS云存储视图对象 sys_oss
 *
 * @author Lion Li
 */
@Data
@ApiModel("OSS云存储视图对象")
public class SysOssVo {
    private static final long serialVersionUID = 1L;
    /**
     *  äº‘存储主键
     */
    @ApiModelProperty("云存储主键")
    private Long ossId;
    /**
     * æ–‡ä»¶å
     */
    @ApiModelProperty("文件名")
    private String fileName;
    /**
     * åŽŸå
     */
    @ApiModelProperty("原名")
    private String originalName;
    /**
     * æ–‡ä»¶åŽç¼€å
     */
    @ApiModelProperty("文件后缀名")
    private String fileSuffix;
    /**
     * URL地址
     */
    @ApiModelProperty("URL地址")
    private String url;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @ApiModelProperty("创建时间")
    private Date createTime;
    /**
     * ä¸Šä¼ äºº
     */
    @ApiModelProperty("上传人")
    private String createBy;
    /**
     * æœåС商
     */
    @ApiModelProperty("服务商")
    private String service;
}
ruoyi-oss/src/main/java/com/ruoyi/system/mapper/SysOssMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.system.domain.SysOss;
/**
 * æ–‡ä»¶ä¸Šä¼  æ•°æ®å±‚
 *
 * @author Lion Li
 */
public interface SysOssMapper extends BaseMapperPlus<SysOss> {
}
ruoyi-oss/src/main/java/com/ruoyi/system/service/ISysOssService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.ruoyi.system.service;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.domain.bo.SysOssBo;
import com.ruoyi.system.domain.SysOss;
import com.ruoyi.system.domain.vo.SysOssVo;
import org.springframework.web.multipart.MultipartFile;
import java.util.Collection;
/**
 * æ–‡ä»¶ä¸Šä¼  æœåС层
 *
 * @author Lion Li
 */
public interface ISysOssService extends IServicePlus<SysOss, SysOssVo> {
    TableDataInfo<SysOssVo> queryPageList(SysOssBo sysOss);
    SysOss upload(MultipartFile file);
    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}
ruoyi-oss/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.PagePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.oss.entity.UploadResult;
import com.ruoyi.oss.factory.OssFactory;
import com.ruoyi.oss.service.ICloudStorageService;
import com.ruoyi.system.domain.bo.SysOssBo;
import com.ruoyi.system.domain.SysOss;
import com.ruoyi.system.mapper.SysOssMapper;
import com.ruoyi.system.service.ISysOssService;
import com.ruoyi.system.domain.vo.SysOssVo;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
 * æ–‡ä»¶ä¸Šä¼  æœåŠ¡å±‚å®žçŽ°
 *
 * @author Lion Li
 */
@Service
public class SysOssServiceImpl extends ServicePlusImpl<SysOssMapper, SysOss, SysOssVo> implements ISysOssService {
    @Override
    public TableDataInfo<SysOssVo> queryPageList(SysOssBo bo) {
        PagePlus<SysOss, SysOssVo> result = pageVo(PageUtils.buildPagePlus(), buildQueryWrapper(bo));
        return PageUtils.buildDataInfo(result);
    }
    private LambdaQueryWrapper<SysOss> buildQueryWrapper(SysOssBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<SysOss> lqw = Wrappers.lambdaQuery();
        lqw.like(StrUtil.isNotBlank(bo.getFileName()), SysOss::getFileName, bo.getFileName());
        lqw.like(StrUtil.isNotBlank(bo.getOriginalName()), SysOss::getOriginalName, bo.getOriginalName());
        lqw.eq(StrUtil.isNotBlank(bo.getFileSuffix()), SysOss::getFileSuffix, bo.getFileSuffix());
        lqw.eq(StrUtil.isNotBlank(bo.getUrl()), SysOss::getUrl, bo.getUrl());
        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
            SysOss::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
        lqw.eq(StrUtil.isNotBlank(bo.getCreateBy()), SysOss::getCreateBy, bo.getCreateBy());
        lqw.eq(StrUtil.isNotBlank(bo.getService()), SysOss::getService, bo.getService());
        return lqw;
    }
    @Override
    public SysOss upload(MultipartFile file) {
        String originalfileName = file.getOriginalFilename();
        String suffix = StrUtil.sub(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
        ICloudStorageService storage = OssFactory.instance();
        UploadResult uploadResult;
        try {
            uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
        } catch (IOException e) {
            throw new CustomException("文件读取异常!!!", e);
        }
        // ä¿å­˜æ–‡ä»¶ä¿¡æ¯
        SysOss oss = new SysOss()
            .setUrl(uploadResult.getUrl())
            .setFileSuffix(suffix)
            .setFileName(uploadResult.getFilename())
            .setOriginalName(originalfileName)
            .setService(storage.getServiceType());
        save(oss);
        return oss;
    }
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            // åšä¸€äº›ä¸šåŠ¡ä¸Šçš„æ ¡éªŒ,判断是否需要校验
        }
        List<SysOss> list = listByIds(ids);
        for (SysOss sysOss : list) {
            ICloudStorageService storage = OssFactory.instance(sysOss.getService());
            storage.delete(sysOss.getUrl());
        }
        return removeByIds(ids);
    }
}
ruoyi-oss/src/main/resources/mapper/system/SysOssMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.system.mapper.SysOssMapper">
    <resultMap type="com.ruoyi.system.domain.SysOss" id="SysOssResult">
        <result property="ossId" column="oss_id"/>
        <result property="fileName" column="file_name"/>
        <result property="fileSuffix" column="file_suffix"/>
        <result property="url" column="url"/>
        <result property="createTime" column="create_time"/>
        <result property="createBy" column="create_by"/>
        <result property="updateTime" column="update_time"/>
        <result property="updateBy" column="update_by"/>
        <result property="service" column="service"/>
    </resultMap>
</mapper>
ruoyi-quartz/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.5.2</version>
        <version>2.6.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java
@@ -1,6 +1,6 @@
package com.ruoyi.quartz.service;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.quartz.domain.SysJobLog;
@@ -11,7 +11,7 @@
 *
 * @author ruoyi
 */
public interface ISysJobLogService extends IServicePlus<SysJobLog> {
public interface ISysJobLogService extends IService<SysJobLog> {
    TableDataInfo<SysJobLog> selectPageJobLogList(SysJobLog jobLog);
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java
@@ -1,6 +1,6 @@
package com.ruoyi.quartz.service;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.quartz.domain.SysJob;
@@ -13,7 +13,7 @@
 *
 * @author ruoyi
 */
public interface ISysJobService extends IServicePlus<SysJob> {
public interface ISysJobService extends IService<SysJob> {
    /**
     * èŽ·å–quartz调度器的计划任务
     *
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java
@@ -3,7 +3,7 @@
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.quartz.domain.SysJobLog;
@@ -21,7 +21,7 @@
 * @author ruoyi
 */
@Service
public class SysJobLogServiceImpl extends ServicePlusImpl<SysJobLogMapper, SysJobLog> implements ISysJobLogService {
public class SysJobLogServiceImpl extends ServiceImpl<SysJobLogMapper, SysJobLog> implements ISysJobLogService {
    @Override
    public TableDataInfo<SysJobLog> selectPageJobLogList(SysJobLog jobLog) {
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java
@@ -2,8 +2,8 @@
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.common.utils.PageUtils;
@@ -29,7 +29,7 @@
 * @author ruoyi
 */
@Service
public class SysJobServiceImpl extends ServicePlusImpl<SysJobMapper, SysJob> implements ISysJobService {
public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implements ISysJobService {
    @Autowired
    private Scheduler scheduler;
ruoyi-system/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.5.2</version>
        <version>2.6.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java
@@ -1,6 +1,6 @@
package com.ruoyi.system.service;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.domain.SysConfig;
@@ -11,7 +11,7 @@
 *
 * @author ruoyi
 */
public interface ISysConfigService extends IServicePlus<SysConfig> {
public interface ISysConfigService extends IService<SysConfig> {
    TableDataInfo<SysConfig> selectPageConfigList(SysConfig config);
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java
@@ -1,8 +1,8 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import java.util.List;
@@ -11,7 +11,7 @@
 *
 * @author ruoyi
 */
public interface ISysDeptService extends IServicePlus<SysDept> {
public interface ISysDeptService extends IService<SysDept> {
    /**
     * æŸ¥è¯¢éƒ¨é—¨ç®¡ç†æ•°æ®
     *
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java
@@ -1,7 +1,7 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import java.util.List;
@@ -11,7 +11,7 @@
 *
 * @author ruoyi
 */
public interface ISysDictDataService extends IServicePlus<SysDictData> {
public interface ISysDictDataService extends IService<SysDictData> {
    TableDataInfo<SysDictData> selectPageDictDataList(SysDictData dictData);
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java
@@ -1,8 +1,8 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.domain.entity.SysDictType;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import java.util.List;
@@ -12,7 +12,7 @@
 *
 * @author ruoyi
 */
public interface ISysDictTypeService extends IServicePlus<SysDictType> {
public interface ISysDictTypeService extends IService<SysDictType> {
    TableDataInfo<SysDictType> selectPageDictTypeList(SysDictType dictType);
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java
@@ -1,6 +1,6 @@
package com.ruoyi.system.service;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.domain.SysLogininfor;
@@ -11,7 +11,7 @@
 *
 * @author ruoyi
 */
public interface ISysLogininforService extends IServicePlus<SysLogininfor> {
public interface ISysLogininforService extends IService<SysLogininfor> {
    TableDataInfo<SysLogininfor> selectPageLogininforList(SysLogininfor logininfor);
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java
@@ -1,8 +1,8 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.system.domain.vo.RouterVo;
import java.util.List;
@@ -13,7 +13,7 @@
 *
 * @author ruoyi
 */
public interface ISysMenuService extends IServicePlus<SysMenu> {
public interface ISysMenuService extends IService<SysMenu> {
    /**
     * æ ¹æ®ç”¨æˆ·æŸ¥è¯¢ç³»ç»Ÿèœå•列表
     *
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java
@@ -1,6 +1,6 @@
package com.ruoyi.system.service;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.domain.SysNotice;
@@ -11,7 +11,7 @@
 *
 * @author ruoyi
 */
public interface ISysNoticeService extends IServicePlus<SysNotice> {
public interface ISysNoticeService extends IService<SysNotice> {
    TableDataInfo<SysNotice> selectPageNoticeList(SysNotice notice);
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java
@@ -1,6 +1,6 @@
package com.ruoyi.system.service;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.domain.SysOperLog;
@@ -11,7 +11,7 @@
 *
 * @author ruoyi
 */
public interface ISysOperLogService extends IServicePlus<SysOperLog> {
public interface ISysOperLogService extends IService<SysOperLog> {
    TableDataInfo<SysOperLog> selectPageOperLogList(SysOperLog operLog);
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java
@@ -1,6 +1,6 @@
package com.ruoyi.system.service;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.domain.SysPost;
@@ -11,7 +11,7 @@
 *
 * @author ruoyi
 */
public interface ISysPostService extends IServicePlus<SysPost> {
public interface ISysPostService extends IService<SysPost> {
    TableDataInfo<SysPost> selectPagePostList(SysPost post);
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java
@@ -1,9 +1,8 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.system.domain.SysUserRole;
import java.util.List;
@@ -14,7 +13,7 @@
 *
 * @author ruoyi
 */
public interface ISysRoleService extends IServicePlus<SysRole> {
public interface ISysRoleService extends IService<SysRole> {
    TableDataInfo<SysRole> selectPageRoleList(SysRole role);
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
@@ -1,7 +1,7 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import java.util.List;
@@ -11,7 +11,7 @@
 *
 * @author ruoyi
 */
public interface ISysUserService extends IServicePlus<SysUser> {
public interface ISysUserService extends IService<SysUser> {
    TableDataInfo<SysUser> selectPageUserList(SysUser user);
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java
@@ -4,10 +4,10 @@
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.annotation.DataSource;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.DataSourceType;
@@ -31,7 +31,7 @@
 * @author ruoyi
 */
@Service
public class SysConfigServiceImpl extends ServicePlusImpl<SysConfigMapper, SysConfig> implements ISysConfigService {
public class SysConfigServiceImpl extends ServiceImpl<SysConfigMapper, SysConfig> implements ISysConfigService {
    @Autowired
    private RedisCache redisCache;
@@ -98,6 +98,7 @@
     *
     * @return true开启,false关闭
     */
    @Override
    public boolean selectCaptchaOnOff() {
        String captchaOnOff = selectConfigByKey("sys.account.captchaOnOff");
        if (StrUtil.isEmpty(captchaOnOff)) {
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
@@ -4,13 +4,13 @@
import cn.hutool.core.lang.Validator;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.mapper.SysRoleMapper;
@@ -30,7 +30,7 @@
 * @author ruoyi
 */
@Service
public class SysDeptServiceImpl extends ServicePlusImpl<SysDeptMapper, SysDept> implements ISysDeptService {
public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> implements ISysDeptService {
    @Autowired
    private SysRoleMapper roleMapper;
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java
@@ -2,8 +2,8 @@
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.PageUtils;
@@ -11,7 +11,6 @@
import com.ruoyi.system.service.ISysDictDataService;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
/**
@@ -20,7 +19,7 @@
 * @author ruoyi
 */
@Service
public class SysDictDataServiceImpl extends ServicePlusImpl<SysDictDataMapper, SysDictData> implements ISysDictDataService {
public class SysDictDataServiceImpl extends ServiceImpl<SysDictDataMapper, SysDictData> implements ISysDictDataService {
    @Override
    public TableDataInfo<SysDictData> selectPageDictDataList(SysDictData dictData) {
@@ -84,10 +83,10 @@
    public void deleteDictDataByIds(Long[] dictCodes) {
        for (Long dictCode : dictCodes) {
            SysDictData data = selectDictDataById(dictCode);
            removeById(dictCode);
            List<SysDictData> dictDatas = baseMapper.selectDictDataByType(data.getDictType());
            DictUtils.setDictCache(data.getDictType(), dictDatas);
        }
        baseMapper.deleteBatchIds(Arrays.asList(dictCodes));
    }
    /**
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java
@@ -5,10 +5,10 @@
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.domain.entity.SysDictType;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.DictUtils;
@@ -31,7 +31,7 @@
 * @author ruoyi
 */
@Service
public class SysDictTypeServiceImpl extends ServicePlusImpl<SysDictTypeMapper, SysDictType> implements ISysDictTypeService {
public class SysDictTypeServiceImpl extends ServiceImpl<SysDictTypeMapper, SysDictType> implements ISysDictTypeService {
    @Autowired
    private SysDictDataMapper dictDataMapper;
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java
@@ -3,7 +3,7 @@
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.system.domain.SysLogininfor;
@@ -22,7 +22,7 @@
 * @author ruoyi
 */
@Service
public class SysLogininforServiceImpl extends ServicePlusImpl<SysLogininforMapper, SysLogininfor> implements ISysLogininforService {
public class SysLogininforServiceImpl extends ServiceImpl<SysLogininforMapper, SysLogininfor> implements ISysLogininforService {
    @Override
    public TableDataInfo<SysLogininfor> selectPageLogininforList(SysLogininfor logininfor) {
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
@@ -3,13 +3,13 @@
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.domain.SysRoleMenu;
import com.ruoyi.system.domain.vo.MetaVo;
@@ -31,7 +31,7 @@
 * @author ruoyi
 */
@Service
public class SysMenuServiceImpl extends ServicePlusImpl<SysMenuMapper, SysMenu> implements ISysMenuService {
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {
    public static final String PREMISSION_STRING = "perms[\"{0}\"]";
    @Autowired
@@ -154,14 +154,14 @@
                childrenList.add(children);
                router.setChildren(childrenList);
            } else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) {
                router.setMeta(null);
                router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon()));
                router.setPath("/inner");
                List<RouterVo> childrenList = new ArrayList<RouterVo>();
                RouterVo children = new RouterVo();
                String routerPath = StringUtils.replaceEach(menu.getPath(), new String[] { Constants.HTTP, Constants.HTTPS }, new String[] { "", "" });
                children.setPath(routerPath);
                children.setComponent(UserConstants.INNER_LINK);
                children.setName(StringUtils.capitalize(routerPath));
                children.setName(StrUtil.upperFirst(routerPath));
                children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath()));
                childrenList.add(children);
                router.setChildren(childrenList);
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java
@@ -2,7 +2,7 @@
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.system.domain.SysNotice;
@@ -19,7 +19,7 @@
 * @author ruoyi
 */
@Service
public class SysNoticeServiceImpl extends ServicePlusImpl<SysNoticeMapper, SysNotice> implements ISysNoticeService {
public class SysNoticeServiceImpl extends ServiceImpl<SysNoticeMapper, SysNotice> implements ISysNoticeService {
    @Override
    public TableDataInfo<SysNotice> selectPageNoticeList(SysNotice notice) {
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java
@@ -4,7 +4,7 @@
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.system.domain.SysOperLog;
@@ -23,7 +23,7 @@
 * @author ruoyi
 */
@Service
public class SysOperLogServiceImpl extends ServicePlusImpl<SysOperLogMapper, SysOperLog> implements ISysOperLogService {
public class SysOperLogServiceImpl extends ServiceImpl<SysOperLogMapper, SysOperLog> implements ISysOperLogService {
    @Override
    public TableDataInfo<SysOperLog> selectPageOperLogList(SysOperLog operLog) {
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java
@@ -3,8 +3,8 @@
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.PageUtils;
@@ -25,7 +25,7 @@
 * @author ruoyi
 */
@Service
public class SysPostServiceImpl extends ServicePlusImpl<SysPostMapper, SysPost> implements ISysPostService {
public class SysPostServiceImpl extends ServiceImpl<SysPostMapper, SysPost> implements ISysPostService {
    @Autowired
    private SysUserPostMapper userPostMapper;
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
@@ -2,10 +2,10 @@
import cn.hutool.core.lang.Validator;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.PageUtils;
@@ -30,7 +30,7 @@
 * @author ruoyi
 */
@Service
public class SysRoleServiceImpl extends ServicePlusImpl<SysRoleMapper, SysRole> implements ISysRoleService {
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements ISysRoleService {
    @Autowired
    private SysRoleMenuMapper roleMenuMapper;
@@ -364,13 +364,17 @@
    @Override
    public int insertAuthUsers(Long roleId, Long[] userIds) {
        // æ–°å¢žç”¨æˆ·ä¸Žè§’色管理
        List<SysUserRole> list = new ArrayList<SysUserRole>();
        int rows = 1;
        List<SysUserRole> list = new ArrayList<SysUserRole>();
        for (Long userId : userIds) {
            SysUserRole ur = new SysUserRole();
            ur.setUserId(userId);
            ur.setRoleId(roleId);
            list.add(ur);
        }
        return userRoleMapper.insertAll(list);
        if (list.size() > 0) {
            rows = userRoleMapper.insertAll(list);
        }
        return rows;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -3,11 +3,11 @@
import cn.hutool.core.lang.Validator;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.PageUtils;
@@ -34,7 +34,7 @@
 */
@Slf4j
@Service
public class SysUserServiceImpl extends ServicePlusImpl<SysUserMapper, SysUser> implements ISysUserService {
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {
    @Autowired
    private SysRoleMapper roleMapper;
ruoyi-ui/package.json
@@ -1,6 +1,6 @@
{
  "name": "ruoyi-vue-plus",
  "version": "2.5.2",
  "version": "2.6.0",
  "description": "RuoYi-Vue-Plus后台管理系统",
  "author": "LionLi",
  "license": "MIT",
@@ -41,7 +41,7 @@
    "clipboard": "2.0.6",
    "core-js": "3.8.1",
    "echarts": "4.9.0",
    "element-ui": "2.15.2",
    "element-ui": "2.15.3",
    "file-saver": "2.0.4",
    "fuse.js": "6.4.3",
    "highlight.js": "9.18.5",
ruoyi-ui/src/api/demo/demo.js
@@ -9,6 +9,15 @@
  })
}
// è‡ªå®šä¹‰åˆ†é¡µæŽ¥å£
export function pageDemo(query) {
  return request({
    url: '/demo/demo/page',
    method: 'get',
    params: query
  })
}
// æŸ¥è¯¢æµ‹è¯•单表详细
export function getDemo(id) {
  return request({
@@ -50,4 +59,4 @@
    method: 'get',
    params: query
  })
}
}
ruoyi-ui/src/api/system/oss.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
import request from '@/utils/request'
// æŸ¥è¯¢OSS云存储列表
export function listOss(query) {
  return request({
    url: '/system/oss/list',
    method: 'get',
    params: query
  })
}
// åˆ é™¤OSS云存储
export function delOss(ossId) {
  return request({
    url: '/system/oss/' + ossId,
    method: 'delete'
  })
}
ruoyi-ui/src/components/Editor/index.vue
@@ -2,6 +2,7 @@
  <div>
    <el-upload
      :action="uploadUrl"
      :before-upload="handleBeforeUpload"
      :on-success="handleUploadSuccess"
      :on-error="handleUploadError"
      name="file"
@@ -46,6 +47,11 @@
      type: Boolean,
      default: false,
    },
    // ä¸Šä¼ æ–‡ä»¶å¤§å°é™åˆ¶(MB)
    fileSize: {
      type: Number,
      default: 5,
    },
    /* ç±»åž‹ï¼ˆbase64格式、url格式) */
    type: {
      type: String,
@@ -54,7 +60,7 @@
  },
  data() {
    return {
      uploadUrl: process.env.VUE_APP_BASE_API + "/common/upload", // ä¸Šä¼ çš„图片服务器地址
      uploadUrl: process.env.VUE_APP_BASE_API + "/system/oss/upload", // ä¸Šä¼ çš„图片服务器地址
      headers: {
        Authorization: "Bearer " + getToken()
      },
@@ -130,14 +136,6 @@
            this.quill.format("image", false);
          }
        });
        // toolbar.addHandler("video", (value) => {
        //   this.uploadType = "video";
        //   if (value) {
        //     this.$refs.upload.$children[0].$refs.input.click();
        //   } else {
        //     this.quill.format("video", false);
        //   }
        // });
      }
      this.Quill.pasteHTML(this.currentValue);
      this.Quill.on("text-change", (delta, oldDelta, source) => {
@@ -158,6 +156,18 @@
        this.$emit("on-editor-change", eventName, ...args);
      });
    },
    // ä¸Šä¼ å‰æ ¡æ£€æ ¼å¼å’Œå¤§å°
    handleBeforeUpload(file) {
      // æ ¡æ£€æ–‡ä»¶å¤§å°
      if (this.fileSize) {
        const isLt = file.size / 1024 / 1024 < this.fileSize;
        if (!isLt) {
          this.$message.error(`上传文件大小不能超过 ${this.fileSize} MB!`);
          return false;
        }
      }
      return true;
    },
    handleUploadSuccess(res, file) {
      // èŽ·å–å¯Œæ–‡æœ¬ç»„ä»¶å®žä¾‹
      let quill = this.Quill;
ruoyi-ui/src/components/FileUpload/index.vue
@@ -27,7 +27,7 @@
    <!-- æ–‡ä»¶åˆ—表 -->
    <transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
      <li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
        <el-link :href="`${baseUrl}${file.url}`" :underline="false" target="_blank">
        <el-link :href="`${file.url}`" :underline="false" target="_blank">
          <span class="el-icon-document"> {{ getFileName(file.name) }} </span>
        </el-link>
        <div class="ele-upload-list__item-content-action">
@@ -70,7 +70,7 @@
  data() {
    return {
      baseUrl: process.env.VUE_APP_BASE_API,
      uploadFileUrl: process.env.VUE_APP_BASE_API + "/common/upload", // ä¸Šä¼ çš„图片服务器地址
      uploadFileUrl: process.env.VUE_APP_BASE_API + "/system/oss/upload", // ä¸Šä¼ çš„图片服务器地址
      headers: {
        Authorization: "Bearer " + getToken(),
      },
ruoyi-ui/src/components/ImageUpload/index.vue
@@ -74,7 +74,7 @@
      dialogVisible: false,
      hideUpload: false,
      baseUrl: process.env.VUE_APP_BASE_API,
      uploadImgUrl: process.env.VUE_APP_BASE_API + "/common/upload", // ä¸Šä¼ çš„图片服务器地址
      uploadImgUrl: process.env.VUE_APP_BASE_API + "/system/oss/upload", // ä¸Šä¼ çš„图片服务器地址
      headers: {
        Authorization: "Bearer " + getToken(),
      },
@@ -90,11 +90,7 @@
          // ç„¶åŽå°†æ•°ç»„转为对象数组
          this.fileList = list.map(item => {
            if (typeof item === "string") {
              if (item.indexOf(this.baseUrl) === -1) {
                  item = { name: this.baseUrl + item, url: this.baseUrl + item };
              } else {
                  item = { name: item, url: item };
              }
              item = { name: item, url: item };
            }
            return item;
          });
@@ -122,7 +118,7 @@
    },
    // ä¸Šä¼ æˆåŠŸå›žè°ƒ
    handleUploadSuccess(res) {
      this.fileList.push({ name: res.data.fileName, url: res.data.fileName });
      this.fileList.push({ name: res.data.fileName, url: res.data.url });
      this.$emit("input", this.listToString(this.fileList));
      this.loading.close();
    },
ruoyi-ui/src/components/RuoYi/Doc/index.vue
@@ -1,21 +1,21 @@
<template>
  <div>
    <svg-icon icon-class="question" @click="goto"/>
  </div>
</template>
<script>
export default {
  name: 'RuoYiDoc',
  data() {
    return {
      url: 'http://doc.ruoyi.vip/ruoyi-vue'
    }
  },
  methods: {
    goto() {
      window.open(this.url)
    }
  }
}
</script>
<template>
  <div>
    <svg-icon icon-class="question" @click="goto"/>
  </div>
</template>
<script>
export default {
  name: 'RuoYiDoc',
  data() {
    return {
      url: 'https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages'
    }
  },
  methods: {
    goto() {
      window.open(this.url)
    }
  }
}
</script>
ruoyi-ui/src/components/RuoYi/Git/index.vue
@@ -9,7 +9,7 @@
  name: 'RuoYiGit',
  data() {
    return {
      url: 'https://gitee.com/y_project/RuoYi-Vue'
      url: 'https://gitee.com/JavaLionLi/RuoYi-Vue-Plus'
    }
  },
  methods: {
@@ -18,4 +18,4 @@
    }
  }
}
</script>
</script>
ruoyi-ui/src/components/TopNav/index.vue
@@ -12,7 +12,7 @@
    </template>
    <!-- é¡¶éƒ¨èœå•超出数量折叠 -->
    <el-submenu index="more" v-if="topMenus.length > visibleNumber">
    <el-submenu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
      <template slot="title">更多菜单</template>
      <template v-for="(item, index) in topMenus">
        <el-menu-item
@@ -87,7 +87,7 @@
    // é»˜è®¤æ¿€æ´»çš„菜单
    activeMenu() {
      const path = this.$route.path;
      let activePath = this.routers[0].path;
      let activePath = this.defaultRouter();
      if (path.lastIndexOf("/") > 0) {
        const tmpPath = path.substring(1, path.length);
        activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/"));
@@ -100,7 +100,7 @@
      }
      var routes = this.activeRoutes(activePath);
      if (routes.length === 0) {
        activePath = this.currentIndex || this.routers[0].path
        activePath = this.currentIndex || this.defaultRouter()
        this.activeRoutes(activePath);
      }
      return activePath;
@@ -120,6 +120,17 @@
    setVisibleNumber() {
      const width = document.body.getBoundingClientRect().width / 3;
      this.visibleNumber = parseInt(width / 85);
    },
    // é»˜è®¤æ¿€æ´»çš„路由
    defaultRouter() {
      let router;
      Object.keys(this.routers).some((key) => {
        if (!this.routers[key].hidden) {
          router = this.routers[key].path;
          return true;
        }
      });
      return router;
    },
    // èœå•选择事件
    handleSelect(key, keyPath) {
@@ -158,25 +169,27 @@
</script>
<style lang="scss">
.el-menu--horizontal > .el-menu-item {
.topmenu-container.el-menu--horizontal > .el-menu-item {
  float: left;
  height: 50px;
  line-height: 50px;
  margin: 0;
  border-bottom: 3px solid transparent;
  color: #999093;
  padding: 0 5px;
  margin: 0 10px;
  height: 50px !important;
  line-height: 50px !important;
  color: #999093 !important;
  padding: 0 5px !important;
  margin: 0 10px !important;
}
.el-menu--horizontal > .el-menu-item.is-active {
  border-bottom: 3px solid #{'var(--theme)'};
.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-submenu.is-active .el-submenu__title {
  border-bottom: 2px solid #{'var(--theme)'} !important;
  color: #303133;
}
/* submenu item */
.el-menu--horizontal > .el-submenu .el-submenu__title {
    height: 50px !important;
    line-height: 50px !important;
.topmenu-container.el-menu--horizontal > .el-submenu .el-submenu__title {
  float: left;
  height: 50px !important;
  line-height: 50px !important;
  color: #999093 !important;
  padding: 0 5px !important;
  margin: 0 10px !important;
}
</style>
ruoyi-ui/src/router/index.js
@@ -5,8 +5,6 @@
/* Layout */
import Layout from '@/layout'
import ParentView from '@/components/ParentView';
import InnerLink from '@/layout/components/InnerLink'
/**
 * Note: è·¯ç”±é…ç½®é¡¹
@@ -23,6 +21,7 @@
    title: 'title'               // è®¾ç½®è¯¥è·¯ç”±åœ¨ä¾§è¾¹æ å’Œé¢åŒ…屑中展示的名字
    icon: 'svg-name'             // è®¾ç½®è¯¥è·¯ç”±çš„图标,对应路径src/assets/icons/svg
    breadcrumb: false            // å¦‚果设置为false,则不会在breadcrumb面包屑中显示
    activeMenu: '/system/user'   // å½“路由设置了该属性,则会高亮相对应的侧边栏。
  }
 */
@@ -82,7 +81,7 @@
    ]
  },
  {
    path: '/auth',
    path: '/system/user-auth',
    component: Layout,
    hidden: true,
    children: [
@@ -90,12 +89,12 @@
        path: 'role/:userId(\\d+)',
        component: (resolve) => require(['@/views/system/user/authRole'], resolve),
        name: 'AuthRole',
        meta: { title: '分配角色'}
        meta: { title: '分配角色', activeMenu: '/system/user'}
      }
    ]
  },
  {
    path: '/auth',
    path: '/system/role-auth',
    component: Layout,
    hidden: true,
    children: [
@@ -103,46 +102,46 @@
        path: 'user/:roleId(\\d+)',
        component: (resolve) => require(['@/views/system/role/authUser'], resolve),
        name: 'AuthUser',
        meta: { title: '分配用户'}
        meta: { title: '分配用户', activeMenu: '/system/role'}
      }
    ]
  },
  {
    path: '/dict',
    path: '/system/dict-data',
    component: Layout,
    hidden: true,
    children: [
      {
        path: 'type/data/:dictId(\\d+)',
        path: 'index/:dictId(\\d+)',
        component: (resolve) => require(['@/views/system/dict/data'], resolve),
        name: 'Data',
        meta: { title: '字典数据', icon: '' }
        meta: { title: '字典数据', activeMenu: '/system/dict'}
      }
    ]
  },
  {
    path: '/job',
    path: '/monitor/job-log',
    component: Layout,
    hidden: true,
    children: [
      {
        path: 'log',
        path: 'index',
        component: (resolve) => require(['@/views/monitor/job/log'], resolve),
        name: 'JobLog',
        meta: { title: '调度日志' }
        meta: { title: '调度日志', activeMenu: '/monitor/job'}
      }
    ]
  },
  {
    path: '/gen',
    path: '/tool/gen-edit',
    component: Layout,
    hidden: true,
    children: [
      {
        path: 'edit/:tableId(\\d+)',
        path: 'index/:tableId(\\d+)',
        component: (resolve) => require(['@/views/tool/gen/editTable'], resolve),
        name: 'GenEdit',
        meta: { title: '修改生成配置' }
        meta: { title: '修改生成配置', activeMenu: '/tool/gen'}
      }
    ]
  }
ruoyi-ui/src/utils/ossdownload.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
import axios from 'axios'
import { getToken } from '@/utils/auth'
const mimeMap = {
  oss: 'application/octet-stream'
}
const baseUrl = process.env.VUE_APP_BASE_API
export function downLoadOss(ossId) {
  var url = baseUrl + '/system/oss/download/' + ossId
    axios({
    method: 'get',
    url: url,
    responseType: 'blob',
    headers: { 'Authorization': 'Bearer ' + getToken() }
  }).then(res => {
    resolveBlob(res, mimeMap.oss)
  })
}
/**
 * è§£æžblob响应内容并下载
 * @param {*} res blob响应内容
 * @param {String} mimeType MIME类型
 */
export function resolveBlob(res, mimeType) {
  const aLink = document.createElement('a')
  var blob = new Blob([res.data], { type: mimeType })
  // ä»Žresponse的headers中获取filename, åŽç«¯response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") è®¾ç½®çš„æ–‡ä»¶å;
  var patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
  var contentDisposition = decodeURI(res.headers['content-disposition'])
  var result = patt.exec(contentDisposition)
  var fileName = result[1]
  fileName = fileName.replace(/\"/g, '')
  aLink.href = URL.createObjectURL(blob)
  aLink.setAttribute('download', decodeURI(fileName)) // è®¾ç½®ä¸‹è½½æ–‡ä»¶åç§°
  document.body.appendChild(aLink)
  aLink.click()
  document.body.removeChild(aLink);
}
ruoyi-ui/src/utils/request.js
@@ -64,6 +64,7 @@
          location.href = '/index';
        })
      }).catch(() => {});
      return Promise.reject('error')
    } else if (code === 500) {
      Message({
        message: msg,
ruoyi-ui/src/views/demo/demo/index.vue
@@ -33,6 +33,7 @@
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
        <el-button type="primary" icon="el-icon-search" size="mini" @click="handlePage">搜索(自定义分页接口)</el-button>
        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>
@@ -168,7 +169,7 @@
</template>
<script>
import { listDemo, getDemo, delDemo, addDemo, updateDemo, exportDemo } from "@/api/demo/demo";
import { listDemo, pageDemo, getDemo, delDemo, addDemo, updateDemo, exportDemo } from "@/api/demo/demo";
export default {
  name: "Demo",
@@ -239,6 +240,20 @@
        this.loading = false;
      });
    },
    /** è‡ªå®šä¹‰åˆ†é¡µæŸ¥è¯¢ */
    getPage() {
      this.loading = true;
      this.queryParams.params = {};
      if (null != this.daterangeCreateTime && '' != this.daterangeCreateTime) {
        this.queryParams.params["beginCreateTime"] = this.daterangeCreateTime[0];
        this.queryParams.params["endCreateTime"] = this.daterangeCreateTime[1];
      }
      pageDemo(this.queryParams).then(response => {
        this.demoList = response.rows;
        this.total = response.total;
        this.loading = false;
      });
    },
    // å–消按钮
    cancel() {
      this.open = false;
@@ -267,6 +282,11 @@
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** æœç´¢æŒ‰é’®æ“ä½œ */
    handlePage() {
      this.queryParams.pageNum = 1;
      this.getPage();
    },
    /** é‡ç½®æŒ‰é’®æ“ä½œ */
    resetQuery() {
      this.daterangeCreateTime = [];
ruoyi-ui/src/views/index.vue
@@ -22,6 +22,8 @@
          * å¤šæ•°æ®æºæ¡†æž¶ dynamic-datasource æ”¯æŒä¸»ä»Žä¸Žå¤šç§ç±»æ•°æ®åº“异构<br/>
          * Redis客户端 é‡‡ç”¨ Redisson æ€§èƒ½æ›´å¼º<br/>
          * åˆ†å¸ƒå¼é” Lock4j æ³¨è§£é”ã€å·¥å…·é” å¤šç§å¤šæ ·<br/>
          * éƒ¨ç½²æ–¹å¼ Docker å®¹å™¨ç¼–排 ä¸€é”®éƒ¨ç½²ä¸šåŠ¡é›†ç¾¤<br/>
          * æ–‡ä»¶å­˜å‚¨ OSS å¯¹è±¡å­˜å‚¨æ¨¡å— æ”¯æŒ(Minio、七牛、阿里、腾讯)<br/>
        </p>
        <p>
          <b>当前版本:</b> <span>v{{ version }}</span>
@@ -91,6 +93,37 @@
            <span>更新日志</span>
          </div>
          <el-collapse accordion>
            <el-collapse-item title="v2.6.0 - 2021-7-28">
              <ol>
                <li>add [重大新增] å¢žåŠ  OSS å¯¹è±¡å­˜å‚¨æ¨¡å—</li>
                <li>remove [重大改动] åˆ é™¤ è‡ªå¸¦é€šç”¨ä¸Šä¼  æŽ¥å£ ä½¿ç”¨OSS模块替换<li>
                <li>update [重大改动] é‡å†™VO转换 æ”¯æŒæ·±æ‹·è´ å°†VO类抽象到 ServicePlus æ³›åž‹å¤„理<li>
                <li>update [重大改动] å¤šBO合并 ä½¿ç”¨åˆ†ç»„校验 ç”ŸæˆBO代码<li>
                <li>update [重大改动] é‡æž„ IServicePlus åŠŸèƒ½ å¢žåŠ  BeanCopyUtils æ·±æ‹·è´å·¥å…·<li>
                <li>update springboot 2.4.9 => 2.5.3<li>
                <li>update hutool 5.7.4 => 5.7.6<li>
                <li>update minio 8.2.2 => 8.3.0<li>
                <li>update docker plugin 1.2.0 => 1.2.2<li>
                <li>update redisson 3.16.0 => 3.16.1<li>
                <li>update datasource 3.4.0 => 3.4.1<li>
                <li>update element-ui 2.15.2 => 2.15.3<li>
                <li>add æ¼”示Demo增加自定义分页接口案例</li>
                <li>add è§’色&菜单新增字段属性提示信息</li>
                <li>update æ›´æ–°druid配置 ç‹¬ç«‹é…ç½®æ›´æ˜Žæ˜¾</li>
                <li>update é¡¶éƒ¨èœå•排除隐藏的默认路由</li>
                <li>update å¯Œæ–‡æœ¬æ–°å¢žä¸Šä¼ æ–‡ä»¶å¤§å°é™åˆ¶</li>
                <li>update å¯¼å…¥ç”¨æˆ·æ ·å¼è°ƒæ•´</li>
                <li>update é¡¶éƒ¨èœå•样式调整</li>
                <li>update å¯†ç æ¡†æ–°å¢žæ˜¾ç¤ºåˆ‡æ¢å¯†ç å›¾æ ‡</li>
                <li>update å†…链设置meta信息</li>
                <li>update è·³è½¬è·¯ç”±é«˜äº®ç›¸å¯¹åº”的菜单栏</li>
                <li>fix ä¿®å¤å¤šæ•°æ®æºdruid全局配置缩进错误 å¼•起无效配置问题</li>
                <li>fix ä¿®å¤å®šæ—¶ä»»åŠ¡æ—¥å¿—æ‰§è¡ŒçŠ¶æ€æ˜¾ç¤º</li>
                <li>fix ä¿®å¤ æŽˆæƒè§’色空数据问题</li>
                <li>fix ä¿®å¤ DictData åˆ é™¤é€»è¾‘问题</li>
                <li>fix ä¿®å¤ä»»æ„è´¦æˆ·è¶Šæƒæ¼æ´ž</li>
              </ol>
            </el-collapse-item>
            <el-collapse-item title="v2.5.2 - 2021-7-19">
              <ol>
                <li>update ä¼˜åŒ–代码生成器注释格式</li>
ruoyi-ui/src/views/monitor/job/index.vue
@@ -115,17 +115,30 @@
          <el-button
            size="mini"
            type="text"
            icon="el-icon-caret-right"
            @click="handleRun(scope.row)"
            v-hasPermi="['monitor:job:changeStatus']"
          >执行一次</el-button>
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['monitor:job:edit']"
          >修改</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-view"
            @click="handleView(scope.row)"
            v-hasPermi="['monitor:job:query']"
          >详细</el-button>
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['monitor:job:remove']"
          >删除</el-button>
          <el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['monitor:job:changeStatus', 'monitor:job:query']">
            <span class="el-dropdown-link">
              <i class="el-icon-d-arrow-right el-icon--right"></i>更多
            </span>
            <el-dropdown-menu slot="dropdown">
              <el-dropdown-item command="handleRun" icon="el-icon-caret-right"
                v-hasPermi="['monitor:job:changeStatus']">执行一次</el-dropdown-item>
              <el-dropdown-item command="handleView" icon="el-icon-view"
                v-hasPermi="['monitor:job:query']">任务详细</el-dropdown-item>
              <el-dropdown-item command="handleJobLog" icon="el-icon-s-operation"
                v-hasPermi="['monitor:job:query']">调度日志</el-dropdown-item>
            </el-dropdown-menu>
          </el-dropdown>
        </template>
      </el-table-column>
    </el-table>
@@ -385,6 +398,22 @@
      this.single = selection.length != 1;
      this.multiple = !selection.length;
    },
    // æ›´å¤šæ“ä½œè§¦å‘
    handleCommand(command, row) {
      switch (command) {
        case "handleRun":
          this.handleRun(row);
          break;
        case "handleView":
          this.handleView(row);
          break;
        case "handleJobLog":
          this.handleJobLog(row);
          break;
        default:
          break;
      }
    },
    // ä»»åŠ¡çŠ¶æ€ä¿®æ”¹
    handleStatusChange(row) {
      let text = row.status === "0" ? "启用" : "停用";
@@ -420,8 +449,9 @@
      });
    },
    /** ä»»åŠ¡æ—¥å¿—åˆ—è¡¨æŸ¥è¯¢ */
    handleJobLog() {
      this.$router.push("/job/log");
    handleJobLog(row) {
      const jobId = row.jobId || 0;
      this.$router.push({ path: '/monitor/job-log/index', query: { jobId: jobId } })
    },
    /** æ–°å¢žæŒ‰é’®æ“ä½œ */
    handleAdd() {
ruoyi-ui/src/views/monitor/job/log.vue
@@ -94,6 +94,15 @@
          v-hasPermi="['monitor:job:export']"
        >导出</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="warning"
          plain
          icon="el-icon-close"
          size="mini"
          @click="handleClose"
        >关闭</el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>
@@ -168,6 +177,7 @@
</template>
<script>
import { getJob} from "@/api/monitor/job";
import { listJobLog, delJobLog, exportJobLog, cleanJobLog } from "@/api/monitor/jobLog";
export default {
@@ -209,8 +219,17 @@
    };
  },
  created() {
    this.getList();
    this.getDicts("sys_job_status").then(response => {
    const jobId = this.$route.query.jobId;
    if (jobId !== undefined && jobId != 0) {
      getJob(jobId).then(response => {
        this.queryParams.jobName = response.data.jobName;
        this.queryParams.jobGroup = response.data.jobGroup;
        this.getList();
      });
    } else {
      this.getList();
    }
    this.getDicts("sys_common_status").then(response => {
      this.statusOptions = response.data;
    });
    this.getDicts("sys_job_group").then(response => {
@@ -236,6 +255,11 @@
    jobGroupFormat(row, column) {
      return this.selectDictLabel(this.jobGroupOptions, row.jobGroup);
    },
    // è¿”回按钮
    handleClose() {
      this.$store.dispatch("tagsView/delView", this.$route);
      this.$router.push({ path: "/monitor/job" });
    },
    /** æœç´¢æŒ‰é’®æ“ä½œ */
    handleQuery() {
      this.queryParams.pageNum = 1;
ruoyi-ui/src/views/system/dict/index.vue
@@ -118,7 +118,7 @@
      <el-table-column label="字典名称" align="center" prop="dictName" :show-overflow-tooltip="true" />
      <el-table-column label="字典类型" align="center" :show-overflow-tooltip="true">
        <template slot-scope="scope">
          <router-link :to="'/dict/type/data/' + scope.row.dictId" class="link-type">
          <router-link :to="'/system/dict-data/index/' + scope.row.dictId" class="link-type">
            <span>{{ scope.row.dictType }}</span>
          </router-link>
        </template>
ruoyi-ui/src/views/system/menu/index.vue
@@ -89,7 +89,7 @@
    <!-- æ·»åŠ æˆ–ä¿®æ”¹èœå•å¯¹è¯æ¡† -->
    <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
        <el-row>
          <el-col :span="24">
            <el-form-item label="上级菜单">
@@ -144,7 +144,13 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item v-if="form.menuType != 'F'" label="是否外链">
            <el-form-item v-if="form.menuType != 'F'">
              <span slot="label">
                <el-tooltip content="选择是外链则路由地址需要以`http(s)://`开头" placement="top">
                <i class="el-icon-question"></i>
                </el-tooltip>
                æ˜¯å¦å¤–链
              </span>
              <el-radio-group v-model="form.isFrame">
                <el-radio label="0">是</el-radio>
                <el-radio label="1">否</el-radio>
@@ -152,22 +158,46 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item v-if="form.menuType != 'F'" label="路由地址" prop="path">
            <el-form-item v-if="form.menuType != 'F'" prop="path">
              <span slot="label">
                <el-tooltip content="访问的路由地址,如:`user`,如外网地址需内链访问则以`http(s)://`开头" placement="top">
                <i class="el-icon-question"></i>
                </el-tooltip>
                è·¯ç”±åœ°å€
              </span>
              <el-input v-model="form.path" placeholder="请输入路由地址" />
            </el-form-item>
          </el-col>
          <el-col :span="12" v-if="form.menuType == 'C'">
            <el-form-item label="组件路径" prop="component">
            <el-form-item prop="component">
              <span slot="label">
                <el-tooltip content="访问的组件路径,如:`system/user/index`,默认在`views`目录下" placement="top">
                <i class="el-icon-question"></i>
                </el-tooltip>
                ç»„件路径
              </span>
              <el-input v-model="form.component" placeholder="请输入组件路径" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item v-if="form.menuType != 'M'" label="权限标识">
            <el-form-item v-if="form.menuType != 'M'">
              <el-input v-model="form.perms" placeholder="请输入权限标识" maxlength="100" />
              <span slot="label">
                <el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasPermi('system:user:list')`)" placement="top">
                <i class="el-icon-question"></i>
                </el-tooltip>
                æƒé™å­—符
              </span>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item v-if="form.menuType != 'F'" label="显示状态">
            <el-form-item v-if="form.menuType != 'F'">
              <span slot="label">
                <el-tooltip content="选择隐藏则路由将不会出现在侧边栏,但仍然可以访问" placement="top">
                <i class="el-icon-question"></i>
                </el-tooltip>
                æ˜¾ç¤ºçŠ¶æ€
              </span>
              <el-radio-group v-model="form.visible">
                <el-radio
                  v-for="dict in visibleOptions"
@@ -178,7 +208,13 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item v-if="form.menuType != 'F'" label="菜单状态">
            <el-form-item v-if="form.menuType != 'F'">
              <span slot="label">
                <el-tooltip content="选择停用则路由将不会出现在侧边栏,也不能被访问" placement="top">
                <i class="el-icon-question"></i>
                </el-tooltip>
                èœå•状态
              </span>
              <el-radio-group v-model="form.status">
                <el-radio
                  v-for="dict in statusOptions"
@@ -189,7 +225,13 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item v-if="form.menuType == 'C'" label="是否缓存">
            <el-form-item v-if="form.menuType == 'C'">
              <span slot="label">
                <el-tooltip content="选择是则会被`keep-alive`缓存,需要匹配组件的`name`和地址保持一致" placement="top">
                <i class="el-icon-question"></i>
                </el-tooltip>
                æ˜¯å¦ç¼“å­˜
              </span>
              <el-radio-group v-model="form.isCache">
                <el-radio label="0">缓存</el-radio>
                <el-radio label="1">不缓存</el-radio>
ruoyi-ui/src/views/system/oss/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,322 @@
<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
      <el-form-item label="文件名" prop="fileName">
        <el-input
          v-model="queryParams.fileName"
          placeholder="请输入文件名"
          clearable
          size="small"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="原名" prop="originalName">
        <el-input
          v-model="queryParams.originalName"
          placeholder="请输入原名"
          clearable
          size="small"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="文件后缀" prop="fileSuffix">
        <el-input
          v-model="queryParams.fileSuffix"
          placeholder="请输入文件后缀"
          clearable
          size="small"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="创建时间">
        <el-date-picker
          v-model="daterangeCreateTime"
          size="small"
          style="width: 240px"
          value-format="yyyy-MM-dd"
          type="daterange"
          range-separator="-"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
        ></el-date-picker>
      </el-form-item>
      <el-form-item label="上传人" prop="createBy">
        <el-input
          v-model="queryParams.createBy"
          placeholder="请输入上传人"
          clearable
          size="small"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="服务商" prop="service">
        <el-input
          v-model="queryParams.service"
          placeholder="请输入服务商"
          clearable
          size="small"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>
    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button
          type="primary"
          plain
          icon="el-icon-plus"
          size="mini"
          @click="handleFile"
          v-hasPermi="['system:oss:upload']"
        >上传文件</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="primary"
          plain
          icon="el-icon-plus"
          size="mini"
          @click="handleImage"
          v-hasPermi="['system:oss:upload']"
        >上传图片</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="danger"
          plain
          icon="el-icon-delete"
          size="mini"
          :disabled="multiple"
          @click="handleDelete"
          v-hasPermi="['system:oss:remove']"
        >删除</el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>
    <el-table v-loading="loading" :data="ossList" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column label="云存储主键" align="center" prop="ossId" v-if="false"/>
      <el-table-column label="文件名" align="center" prop="fileName" />
      <el-table-column label="原名" align="center" prop="originalName" />
      <el-table-column label="文件后缀" align="center" prop="fileSuffix" />
      <el-table-column label="文件展示" align="center" prop="url">
        <template slot-scope="scope">
          <el-image
            v-if="previewListResource && scope.row.fileSuffix.indexOf('png','jpg','jpeg') > 0"
            style="width: 100px; height: 100px;"
            :src="scope.row.url"
            :preview-src-list="[scope.row.url]"/>
          <span v-text="scope.row.url"
                v-if="scope.row.fileSuffix.indexOf('png','jpg','jpeg') < 0 || !previewListResource"/>
        </template>
      </el-table-column>
      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
        </template>
      </el-table-column>
      <el-table-column label="上传人" align="center" prop="createBy" />
      <el-table-column label="服务商" align="center" prop="service" />
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleDownload(scope.row)"
            v-hasPermi="['system:oss:download']"
          >下载</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['system:oss:remove']"
          >删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <pagination
      v-show="total>0"
      :total="total"
      :page.sync="queryParams.pageNum"
      :limit.sync="queryParams.pageSize"
      @pagination="getList"
    />
    <!-- æ·»åŠ æˆ–ä¿®æ”¹OSS云存储对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
        <el-form-item label="文件名">
          <fileUpload v-model="form.file" v-if="type === 0"/>
          <imageUpload v-model="form.file" v-if="type === 1"/>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button :loading="buttonLoading" type="primary" @click="submitForm">ç¡® å®š</el-button>
        <el-button @click="cancel">取 æ¶ˆ</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import { listOss, delOss } from "@/api/system/oss";
import { downLoadOss } from "@/utils/ossdownload";
import { updateConfig } from "@/api/system/config";
export default {
  name: "Oss",
  data() {
    return {
      // æŒ‰é’®loading
      buttonLoading: false,
      // é®ç½©å±‚
      loading: true,
      // å¯¼å‡ºé®ç½©å±‚
      exportLoading: false,
      // é€‰ä¸­æ•°ç»„
      ids: [],
      // éžå•个禁用
      single: true,
      // éžå¤šä¸ªç¦ç”¨
      multiple: true,
      // æ˜¾ç¤ºæœç´¢æ¡ä»¶
      showSearch: true,
      // æ€»æ¡æ•°
      total: 0,
      // OSS云存储表格数据
      ossList: [],
      // å¼¹å‡ºå±‚标题
      title: "",
      // å¼¹å‡ºå±‚标题
      type: 0,
      // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚
      open: false,
      // é¢„览列表图片
      previewListResource: true,
      // åˆ›å»ºæ—¶é—´æ—¶é—´èŒƒå›´
      daterangeCreateTime: [],
      // æŸ¥è¯¢å‚æ•°
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        fileName: undefined,
        originalName: undefined,
        fileSuffix: undefined,
        url: undefined,
        createTime: undefined,
        createBy: undefined,
        service: undefined
      },
      // è¡¨å•参数
      form: {},
      // è¡¨å•校验
      rules: {
        file: [
          { required: true, message: "文件不能为空", trigger: "blur" }
        ]
      }
    };
  },
  created() {
    this.getList();
  },
  methods: {
    /** æŸ¥è¯¢OSS云存储列表 */
    getList() {
      this.loading = true;
      this.queryParams.params = {};
      if (null != this.daterangeCreateTime && '' != this.daterangeCreateTime) {
        this.queryParams.params["beginCreateTime"] = this.daterangeCreateTime[0];
        this.queryParams.params["endCreateTime"] = this.daterangeCreateTime[1];
      }
      this.getConfigKey("sys.oss.previewListResource").then(response => {
        this.previewListResource = response.msg === undefined ? true : response.msg === 'true';
      });
      listOss(this.queryParams).then(response => {
        this.ossList = response.rows;
        this.total = response.total;
        this.loading = false;
      });
    },
    // å–消按钮
    cancel() {
      this.open = false;
      this.reset();
    },
    // è¡¨å•重置
    reset() {
      this.form = {
        file: undefined,
      };
      this.resetForm("form");
    },
    /** æœç´¢æŒ‰é’®æ“ä½œ */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** é‡ç½®æŒ‰é’®æ“ä½œ */
    resetQuery() {
      this.daterangeCreateTime = [];
      this.resetForm("queryForm");
      this.handleQuery();
    },
    // å¤šé€‰æ¡†é€‰ä¸­æ•°æ®
    handleSelectionChange(selection) {
      this.ids = selection.map(item => item.ossId)
      this.single = selection.length!==1
      this.multiple = !selection.length
    },
    /** æ–‡ä»¶æŒ‰é’®æ“ä½œ */
    handleFile() {
      this.reset();
      this.open = true;
      this.title = "上传文件";
      this.type = 0;
    },
    /** å›¾ç‰‡æŒ‰é’®æ“ä½œ */
    handleImage() {
      this.reset();
      this.open = true;
      this.title = "上传图片";
      this.type = 1;
    },
    /** æäº¤æŒ‰é’® */
    submitForm() {
      this.open = false;
      this.getList();
    },
    /** ä¸‹è½½æŒ‰é’®æ“ä½œ */
    handleDownload(row) {
      downLoadOss(row.ossId)
    },
    /** åˆ é™¤æŒ‰é’®æ“ä½œ */
    handleDelete(row) {
      const ossIds = row.ossId || this.ids;
      this.$confirm('是否确认删除OSS云存储编号为"' + ossIds + '"的数据项?', "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }).then(() => {
          this.loading = true;
          return delOss(ossIds);
        }).then(() => {
          this.loading = false;
          this.getList();
          this.msgSuccess("删除成功");
        }).catch(() => {});
    }
  }
};
</script>
ruoyi-ui/src/views/system/role/index.vue
@@ -139,7 +139,7 @@
            @click="handleDelete(scope.row)"
            v-hasPermi="['system:role:remove']"
          >删除</el-button>
          <el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)">
          <el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['system:role:edit']">
            <span class="el-dropdown-link">
              <i class="el-icon-d-arrow-right el-icon--right"></i>更多
            </span>
@@ -164,11 +164,17 @@
    <!-- æ·»åŠ æˆ–ä¿®æ”¹è§’è‰²é…ç½®å¯¹è¯æ¡† -->
    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
        <el-form-item label="角色名称" prop="roleName">
          <el-input v-model="form.roleName" placeholder="请输入角色名称" />
        </el-form-item>
        <el-form-item label="权限字符" prop="roleKey">
        <el-form-item prop="roleKey">
          <span slot="label">
            <el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasRole('admin')`)" placement="top">
              <i class="el-icon-question"></i>
            </el-tooltip>
            æƒé™å­—符
          </span>
          <el-input v-model="form.roleKey" placeholder="请输入权限字符" />
        </el-form-item>
        <el-form-item label="角色顺序" prop="roleSort">
@@ -568,7 +574,7 @@
    /** åˆ†é…ç”¨æˆ·æ“ä½œ */
    handleAuthUser: function(row) {
      const roleId = row.roleId;
      this.$router.push("/auth/user/" + roleId);
      this.$router.push("/system/role-auth/user/" + roleId);
    },
    /** æäº¤æŒ‰é’® */
    submitForm: function() {
ruoyi-ui/src/views/system/user/index.vue
@@ -182,7 +182,7 @@
                @click="handleDelete(scope.row)"
                v-hasPermi="['system:user:remove']"
              >删除</el-button>
              <el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)">
              <el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['system:user:resetPwd', 'system:user:edit']">
                <span class="el-dropdown-link">
                  <i class="el-icon-d-arrow-right el-icon--right"></i>更多
                </span>
@@ -242,7 +242,7 @@
          </el-col>
          <el-col :span="12">
            <el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
              <el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" />
              <el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -328,15 +328,14 @@
        drag
      >
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">
          å°†æ–‡ä»¶æ‹–到此处,或
          <em>点击上传</em>
        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
        <div class="el-upload__tip text-center" slot="tip">
          <div class="el-upload__tip" slot="tip">
            <el-checkbox v-model="upload.updateSupport" /> æ˜¯å¦æ›´æ–°å·²ç»å­˜åœ¨çš„用户数据
          </div>
          <span>仅允许导入xls、xlsx格式文件。</span>
          <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
        </div>
        <div class="el-upload__tip" slot="tip">
          <el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
          <el-link type="info" style="font-size:12px" @click="importTemplate">下载模板</el-link>
        </div>
        <div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入“xls”或“xlsx”格式文件!</div>
      </el-upload>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitFileForm">ç¡® å®š</el-button>
@@ -622,7 +621,7 @@
    /** åˆ†é…è§’色操作 */
    handleAuthRole: function(row) {
      const userId = row.userId;
      this.$router.push("/auth/role/" + userId);
      this.$router.push("/system/user-auth/role/" + userId);
    },
    /** æäº¤æŒ‰é’® */
    submitForm: function() {
ruoyi-ui/src/views/system/user/profile/resetPwd.vue
@@ -1,13 +1,13 @@
<template>
  <el-form ref="form" :model="user" :rules="rules" label-width="80px">
    <el-form-item label="旧密码" prop="oldPassword">
      <el-input v-model="user.oldPassword" placeholder="请输入旧密码" type="password" />
      <el-input v-model="user.oldPassword" placeholder="请输入旧密码" type="password" show-password/>
    </el-form-item>
    <el-form-item label="新密码" prop="newPassword">
      <el-input v-model="user.newPassword" placeholder="请输入新密码" type="password" />
      <el-input v-model="user.newPassword" placeholder="请输入新密码" type="password" show-password/>
    </el-form-item>
    <el-form-item label="确认密码" prop="confirmPassword">
      <el-input v-model="user.confirmPassword" placeholder="请确认密码" type="password" />
      <el-input v-model="user.confirmPassword" placeholder="请确认密码" type="password" show-password/>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" size="mini" @click="submit">保存</el-button>
ruoyi-ui/src/views/tool/gen/index.vue
@@ -319,7 +319,7 @@
    /** ä¿®æ”¹æŒ‰é’®æ“ä½œ */
    handleEditTable(row) {
      const tableId = row.tableId || this.ids[0];
      this.$router.push("/gen/edit/" + tableId);
      this.$router.push("/tool/gen-edit/index/" + tableId);
    },
    /** åˆ é™¤æŒ‰é’®æ“ä½œ */
    handleDelete(row) {
sql/oss.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
-- ----------------------------
-- OSS云存储表
-- ----------------------------
drop table if exists sys_oss;
create table sys_oss (
    oss_id          bigint(20)   not null auto_increment    comment '云存储主键',
    file_name       varchar(64)  not null default ''        comment '文件名',
    original_name   varchar(64)  not null default ''        comment '原名',
    file_suffix     varchar(10)  not null default ''        comment '文件后缀名',
    url              varchar(200) not null                   comment 'URL地址',
    create_time     datetime              default null      comment '创建时间',
    create_by       varchar(64)           default ''        comment '上传人',
    update_time     datetime              default null      comment '更新时间',
    update_by       varchar(64)           default ''        comment '更新人',
    service         varchar(10)  not null default 'minio'   comment '服务商',
    primary key (oss_id)
) engine=innodb comment ='OSS云存储表';
insert into sys_config values(10, 'OSS云存储服务商',       'sys.oss.cloudStorageService',      'minio',          'Y', 'admin', sysdate(), '', null, 'OSS云存储服务商(qiniu:七牛云, aliyun:阿里云, qcloud:腾讯云, minio: Minio)');
insert into sys_config values(11, 'OSS预览列表资源开关',   'sys.oss.previewListResource',      'true',           'Y', 'admin', sysdate(), '', null, 'true:开启, false:关闭');
insert into sys_menu values('118',  '文件管理', '1',   '10', 'oss',     'system/oss/index',      1, 0, 'C', '0', '0', 'system:oss:list',      'upload',       'admin', sysdate(), '', null, '文件管理菜单');
insert into sys_menu values('1600', '文件查询', '118', '1', '#', '', 1, 0, 'F', '0', '0', 'system:oss:query',        '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1601', '文件上传', '118', '2', '#', '', 1, 0, 'F', '0', '0', 'system:oss:upload',       '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1602', '文件下载', '118', '3', '#', '', 1, 0, 'F', '0', '0', 'system:oss:download',     '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1603', '文件删除', '118', '4', '#', '', 1, 0, 'F', '0', '0', 'system:oss:remove',       '#', 'admin', sysdate(), '', null, '');